Authentication & Security
Protolink provides a pluggable, robust Authentication & Security framework designed to secure agent-to-agent and client-to-agent communication. This framework spans both the client side (lazily injecting credentials into outgoing requests) and the server side (extracting and validating credentials before route handlers are invoked).
Whether you are communicating over stateless HTTP or establishing persistent WebSocket connections, ProtoLink can authenticate peers before endpoint handlers run. Built-in authenticators verify credentials; application policy remains responsible for interpreting scopes and deciding authorization.
Overview
The authentication workflow decouples credentials management from both your core cognitive agent code and the low-level network libraries.
The same HTTP Authenticator protects ProtoLink's native task routes and the A2A JSON-RPC endpoint. The A2A Agent Card remains publicly discoverable and translates the configured card schemes into canonical A2A 1.0 securitySchemes and securityRequirements. Within the A2A adapter, task lookup and mutation are scoped to the authenticated principal and request tenant.
Below is a sequence diagram representing a typical authenticated request cycle:
Core Concepts
The authentication module revolves around three primary data models and abstractions.
SecurityContext
The SecurityContext represents an active, authenticated session. It encapsulates details about the authenticated principal, tokens, and expiration times.
from dataclasses import dataclass, field
from typing import Any
from protolink.utils import utc_now
@dataclass
class SecurityContext:
principal_id: str
token: str
expires_at: str | None = None
issued_at: str = field(default_factory=utc_now)
metadata: dict[str, Any] = field(default_factory=dict)
def is_expired(self) -> bool:
"""Check if the context token is past its expiration time."""
...
SecurityScheme
The SecurityScheme outlines the metadata of the authentication mechanism used. It directly corresponds to OpenAPI security schemes, allowing agents to advertise their security protocols in their dynamic agent card metadata.
from dataclasses import dataclass, field
from typing import Any
@dataclass
class SecurityScheme:
auth_type: str # e.g., "apiKey", "http", "oauth2"
auth_scheme: str | None # e.g., "bearer", "basic" (required if type is "http")
description: str
metadata: dict[str, Any] = field(default_factory=dict)
Authenticator (Base Class)
All security providers inherit from the Authenticator abstract base class. It specifies the API that security handlers must implement to validate credentials.
from abc import ABC, abstractmethod
class Authenticator(ABC):
@property
@abstractmethod
def security_scheme(self) -> SecurityScheme:
"""Expose metadata describing the security scheme."""
pass
@abstractmethod
async def authenticate(self, credentials: str) -> SecurityContext:
"""Authenticate raw credentials and return a SecurityContext."""
pass
@abstractmethod
async def refresh_token(self, context: SecurityContext) -> SecurityContext:
"""Refresh a token, or explicitly return the unchanged context."""
...
Built-in Providers
Protolink includes several security providers out of the box. The local API-key, Basic, and HMAC JWT providers validate credentials in-process. The OAuth delegation provider calls an external token-exchange endpoint and is intentionally a small integration primitive rather than a complete OAuth client.
- API Key Authentication
- Bearer Token Authentication
- Basic Authentication
- OAuth2 Delegation Authentication
APIKeyAuth validates simple API keys against a dictionary of known keys. Each
value is currently accepted as scope metadata for configuration compatibility,
but the authenticator only checks membership; it does not add those scopes to
the returned SecurityContext.
from protolink.security.auth import APIKeyAuth
auth = APIKeyAuth(
valid_keys={
"sk-12345": ["read", "write"],
"sk-abcde": ["read"]
}
)
BearerTokenAuth validates compact JSON Web Tokens (JWTs) signed with a shared HMAC secret. It checks the declared algorithm, signature, exp, nbf, iat, and optional issuer/audience claims before returning a SecurityContext.
from protolink.security.auth import BearerTokenAuth
auth = BearerTokenAuth(
secret="your-jwt-shared-signing-secret",
algorithm="HS256",
issuer="https://auth.example.com",
audience="protolink-agent",
)
Supported algorithms are HS256, HS384, and HS512. Use APIKeyAuth for static opaque service tokens.
BasicAuth implements standard HTTP Basic access authentication. It validates username:password values, automatically decoding Base64 strings sent via standard Authorization: Basic <base64> headers.
from protolink.security.auth import BasicAuth
auth = BasicAuth(
valid_credentials={
"admin": "super-secret-password-123",
"developer": "dev-pass"
}
)
OAuth2DelegationAuth performs token exchanges with an external identity
provider endpoint to obtain delegated access tokens. The returned provider
metadata is retained on the SecurityContext; ProtoLink does not interpret or
enforce response scopes.
from protolink.security.auth import OAuth2DelegationAuth
auth = OAuth2DelegationAuth(
exchange_endpoint="https://auth.myorganization.com/oauth/token",
client_id="my-agent-client-id",
client_secret="my-agent-client-secret"
)
Server-Side Authentication
When hosting an agent server, endpoints can be protected by configuring an authenticator on the transport. The transport backend (FastAPI or Starlette) will intercept incoming HTTP requests, extract headers, and invoke the validator.
An Authenticator decides whether an application request may access the agent. TLSConfig encrypts the network connection and verifies certificate identities before that request arrives. Configure tls= on an HTTP, SSE JSON-RPC, WebSocket, or gRPC transport with its secure URL scheme, then pass that transport to the Agent. Combine TLS with an authenticator when you need both protected traffic and application authorization. See TLS and mutual TLS.
Request Interception Flow
- Public-route check: The A2A Agent Card at
/.well-known/agent-card.jsonplus/healthzand/readyzbypass application authentication. The native/.well-known/agent.jsoncard does not bypass it. - Extraction: For every other route, the server calls the
extract_credentials()utility, searching the request in order:Authorizationheader withBearer,Basic, orApiKeyprefix.X-API-Keyheader.- Query parameters:
api_key,apikey, ortoken.
- Verification: If credentials are found, they are sent to the transport's
Authenticator.authenticate(credentials)method. - Rejection: If credentials are missing, or verification raises an exception, the request is terminated immediately, returning an HTTP
401 Unauthorizedstatus code with a JSON error message payload.
Setup Route Protection
To secure your server-side endpoints, provide the authenticator argument to your HTTPTransport:
from protolink.transport import HTTPTransport
from protolink.security.auth import APIKeyAuth
# Secure HTTP transport using FastAPI backend
transport = HTTPTransport(
url="http://127.0.0.1:8000",
backend="fastapi",
authenticator=APIKeyAuth(valid_keys={"my-secret": ["write"]})
)
WebSocket Handshake Authentication
WebSocket connections are authenticated during the HTTP connection upgrade handshake phase. The WebSocketTransport intercepts the request headers using a process_request hook before the socket upgrades, ensuring unauthenticated clients are denied connection with a 401 Unauthorized HTTP response immediately.
from protolink.transport import WebSocketTransport
from protolink.security.auth import APIKeyAuth
ws_transport = WebSocketTransport(
url="ws://127.0.0.1:8080",
authenticator=APIKeyAuth(valid_keys={"ws-key": ["connect"]})
)
SSE JSON-RPC Authentication
SSEJSONRPCTransport inherits the HTTP transport's authentication behavior.
Unary calls and the long-lived POST /tasks/stream request use the same lazy
outbound context and generated headers. On the server, the Starlette or FastAPI
backend validates credentials before it opens the event stream, so a rejected
request never reaches the streaming handler.
from protolink.security import BearerTokenAuth
from protolink.transport import SSEJSONRPCTransport
sse_transport = SSEJSONRPCTransport(
url="https://agent.example.com",
authenticator=BearerTokenAuth(secret="shared-signing-secret"),
credentials="signed.jwt",
)
gRPC Metadata Authentication
GRPCTransport applies the same lazy outbound authentication before unary or
stream calls, then translates the generated headers into lowercase gRPC
metadata. The receiving transport extracts credentials from invocation metadata
and aborts the ProtoLink Invoke or Stream RPC with
UNAUTHENTICATED when credentials are absent or invalid.
from protolink.security import APIKeyAuth
from protolink.transport import GRPCTransport
grpc_transport = GRPCTransport(
url="grpcs://agent.example.com:50051",
authenticator=APIKeyAuth(valid_keys={"service-key": []}),
credentials="service-key",
)
The optional standard gRPC health and reflection services are registered by gRPC itself and are not passed through ProtoLink's generic invocation authenticator.
Client-Side Authentication
On the client side, ProtoLink supports Lazy Authentication. When instantiating an HTTP, SSE JSON-RPC, WebSocket, or gRPC transport, provide both an authenticator and its raw credentials string.
from protolink.transport import HTTPTransport
from protolink.security.auth import APIKeyAuth
client_transport = HTTPTransport(
url="http://127.0.0.1:8000",
authenticator=APIKeyAuth(valid_keys={"key123": ["read"]}),
credentials="key123"
)
Automatic Header Injection
- Lazy Evaluation: On the first outbound
.send()or.subscribe()call supported by the configured HTTP, SSE JSON-RPC, WebSocket, or gRPC transport, it automatically invokesawait authenticator.authenticate(credentials). - Caching: The resulting
SecurityContextis stored in the transport instance for subsequent calls. - Signing: Based on the
SecuritySchemedefined by the authenticator, the transport overrides headers in_build_headers()for outgoing requests:- Bearer: Adds
Authorization: Bearer <token> - Basic: Adds
Authorization: Basic <token>without transforming the token. Supply a Base64 payload for standards-compliant HTTP Basic wire format; rawusername:passwordremains useful only when both ProtoLink peers intentionally accept it. - ApiKey: Adds
X-API-Key: <key>andAuthorization: ApiKey <key>
- Bearer: Adds
Agent Integration
The Agent class encapsulates transport management and automatically maps security metadata to the AgentCard.
from protolink.agents import Agent
from protolink.security.auth import BasicAuth
agent = Agent(
card={"name": "secure-agent", "description": "Needs login", "url": "http://127.0.0.1:8000"},
transport="http",
authenticator=BasicAuth(valid_credentials={"admin": "secret"}),
credentials="admin:secret"
)
Card Security Schemes
When an agent is initialized with an authenticator, its native metadata card
includes the advertised security schemes. With authentication enabled, callers
need credentials to read GET /.well-known/agent.json. When the HTTP Agent also
uses a2a=True, the public A2A card at
GET /.well-known/agent-card.json advertises the translated standard security
requirements without requiring credentials.
Example payload for an agent card:
{
"name": "secure-agent",
"description": "Needs login",
"url": "http://127.0.0.1:8000",
"securitySchemes": {
"http": {
"type": "http",
"scheme": "basic",
"description": "HTTP Basic authentication (username:password)",
"metadata": {}
}
}
}
Credential Extraction Helper
You can use the built-in credential extraction logic for custom route middleware or custom transport backends:
from protolink.security.auth import extract_credentials
headers = {"Authorization": "Bearer my-jwt-token"}
query_params = {"api_key": "some-api-key"}
# Returns "my-jwt-token" (headers have precedence)
credentials = extract_credentials(headers, query_params)
Custom Authenticators
To integrate custom enterprise identity management (e.g. LDAP, active directory, Auth0), subclass Authenticator:
from protolink.security.auth import Authenticator, SecurityScheme, SecurityContext
class LDAPAuthenticator(Authenticator):
@property
def security_scheme(self) -> SecurityScheme:
return SecurityScheme(
auth_type="http",
auth_scheme="basic",
description="Active Directory / LDAP validation"
)
async def authenticate(self, credentials: str) -> SecurityContext:
username, password = credentials.split(":")
# Implement custom LDAP check logic here
success = my_ldap_library.verify(username, password)
if not success:
raise ValueError("Invalid LDAP credentials")
return SecurityContext(
principal_id=username,
token=credentials
)
async def refresh_token(self, context: SecurityContext) -> SecurityContext:
# LDAP credentials have no token refresh protocol in this example.
return context
API Reference
The credential verification layer for incoming agent requests, advertised security schemes, outgoing credentials, bearer tokens, and custom authenticators.
protolink.securitySecurityContextSecuritySchemeAuthenticatorBearerTokenAuthCore authentication types
The data objects in this section are intentionally small. They can be attached
to transport requests, request state, policy context, or an AgentCard without
bringing a provider SDK into the rest of the application.
SecurityContext
class SecurityContext(
principal_id: str,
token: str,
expires_at: str | None = None,
issued_at: str = field(default_factory=utc_now),
metadata: dict[str, Any] = field(default_factory=dict),
)Represent the authenticated identity produced by an Authenticator. The
context carries the credential that was accepted, provider timestamps, and
application-specific metadata so later policy and transport layers do not need
to authenticate the request again.
Parameters
principal_idstrrequiredStable identifier for the authenticated user, service, client, or agent. ProtoLink does not interpret its format; use a value that your policy and persistence layers can consistently recognize.
tokenstrrequiredThe accepted credential or delegated access token. This value is retained verbatim and is included by
to_dict(), so do not log or serialize the context into an untrusted destination.expires_atstr | Nonedefault: NoneOptional absolute expiration timestamp in ISO 8601 format. Use a timezone-aware value such as
2026-07-20T12:30:00+00:00so it can be compared with ProtoLink's UTC clock.issued_atstrdefault: utc_now()ISO timestamp describing when the token was issued. When omitted, each new instance receives the current UTC time from
utc_now().metadatadict[str, Any]default: {}Provider- or application-specific details, such as verified custom JWT claims. A new dictionary is created for every context.
A SecurityContext records the result of authentication; creating
one directly does not verify a token. Only trust contexts returned by an
authenticator or another trusted boundary.
SecurityContext.is_expired
is_expired() -> boolCompare the context's absolute expiration timestamp with the current UTC time. This is a local timestamp check; it does not contact the issuing provider, inspect revocation state, or refresh the credential.
Returns
expiredboolTruewhen the current UTC time is later thanexpires_at. ReturnsFalsewhen no expiration was supplied.
Raises
ValueErrorRaised by
datetime.fromisoformat()whenexpires_atis not a valid ISO timestamp.TypeErrorRaised when a timezone-naive timestamp is compared with ProtoLink's timezone-aware UTC clock.
SecurityContext.to_dict
to_dict() -> dictCreate the dictionary representation used by serializers and integration code. All five context fields are included, including the raw token.
Returns
contextdict[str, Any]Mapping with
principal_id,token,expires_at,issued_at, andmetadatakeys. The outer mapping is new, but the metadata dictionary is not deep-copied.
SecurityScheme
class SecurityScheme(
auth_type: SecuritySchemeType,
auth_scheme: HttpAuthScheme | None,
description: str,
metadata: dict[str, Any] = field(default_factory=dict),
)Describe the authentication mechanism advertised by an authenticator. Agent metadata and transport code use this provider-neutral value to decide how a credential should be represented on the wire.
Parameters
auth_type"apiKey" | "http" | "oauth2" | "mutualTLS" | "openIdConnect"requiredBroad OpenAPI-style category for the authentication mechanism.
auth_schemeHttpAuthScheme | NonerequiredHTTP authentication scheme such as
"bearer"or"basic". PassNonefor non-HTTP scheme types. The annotation also acceptsdigest,hmac,negotiate,ntlm,aws4auth,hawk, andedgegrid.descriptionstrrequiredHuman-readable explanation suitable for discovery metadata.
metadatadict[str, Any]default: {}Scheme-specific extensions. Built-in bearer schemes report supported algorithms plus configured issuer and audience; OAuth delegation reports its exchange endpoint.
SecurityScheme is defined in
protolink.security.auth but is not currently re-exported from
protolink.security. Custom authenticators should import it from
the defining module.
SecurityScheme.to_dict
to_dict() -> dictConvert the scheme into the wire-oriented field names used in discovery metadata.
Returns
schemedict[str, Any]Mapping with
type,scheme,description, andmetadatakeys. Themetadatavalue is shared with the dataclass rather than deep-copied.
Authenticator contract
All built-in and application-defined authenticators implement the same asynchronous validation boundary. The transport owns when the methods are called; the authenticator owns how a raw credential becomes a trusted context.
Authenticator
class AuthenticatorAbstract base class for credential providers. Implementations must advertise a
SecurityScheme, validate raw credentials asynchronously, and provide an
explicit refresh behavior even when refresh is a no-op.
Abstract members
security_schemeSecuritySchemeRead-only property describing how the provider is advertised and how a client transport should present its credential.
authenticateasync (str) -> SecurityContextRequired credential-validation method.
refresh_tokenasync (SecurityContext) -> SecurityContextRequired refresh hook. Providers that cannot refresh return the original context unchanged.
All three members are abstract. A custom subclass remains non-instantiable
until it implements security_scheme,
authenticate(), and refresh_token().
Authenticator.security_scheme
security_scheme -> SecuritySchemeReturn a declarative description of the provider. Transports inspect this value to construct outbound headers, while agents expose it through discovery metadata.
Returns
schemeSecuritySchemeProvider category, optional HTTP scheme, human-readable description, and any provider metadata.
Authenticator.authenticate
await authenticate(
credentials: str,
) -> SecurityContextValidate raw credentials and translate them into the common authenticated principal context. Implementations may perform local cryptographic or dictionary checks, or await an external identity provider.
Parameters
credentialsstrrequiredCredential payload after transport-level prefix removal. Its expected syntax depends on the concrete provider.
Returns
contextSecurityContextVerified principal, accepted token, timestamps, and optional metadata.
Raises
authentication errorConcrete providers raise when credentials are missing, malformed, unverifiable, expired, or rejected by an external identity provider. The base interface does not define a specialized exception type.
Authenticator.refresh_token
await refresh_token(
context: SecurityContext,
) -> SecurityContextRefresh an authenticated context when the provider supports renewable credentials. This hook is part of the provider contract, but ProtoLink does not schedule refresh automatically.
Parameters
contextSecurityContextrequiredExisting authenticated context whose token should be renewed or retained.
Returns
contextSecurityContextRefreshed context, or the original object for providers whose refresh implementation is a no-op.
Built-in provider reference
BearerTokenAuth
class BearerTokenAuth(
secret: str,
algorithm: str = "HS256",
*,
issuer: str | None = None,
audience: str | None = None,
leeway_seconds: int = 0,
)Validate compact JWTs signed with a shared HMAC secret. The implementation is
dependency-free and deliberately restricted to symmetric HS256, HS384, and
HS512 signatures; it does not fetch JWK sets or accept asymmetric algorithms.
Parameters
secretstrrequiredNon-empty shared signing secret used to recompute and constant-time compare the JWT signature.
algorithm"HS256" | "HS384" | "HS512"default: "HS256"Exact algorithm required in the JWT header and used for HMAC verification. Algorithm substitution is rejected.
issuerstr | Nonedefault: NoneWhen set, require the payload's
issclaim to equal this value.audiencestr | Nonedefault: NoneWhen set, require this value in the payload's string or string-list
audclaim.leeway_secondsintdefault: 0Non-negative clock-skew allowance applied to
exp,nbf, andiatvalidation.
Raises
ValueErrorRaised immediately for an empty secret, unsupported algorithm, or negative leeway.
Advertised scheme
security_schemeSecuritySchemeHTTP bearer scheme whose metadata lists all three supported HMAC algorithms and the configured issuer and audience.
BearerTokenAuth.authenticate
await authenticate(
credentials: str,
) -> SecurityContextDecode and verify one compact JWT, validate its registered claims, and build the corresponding principal context.
Parameters
credentialsstrrequiredThree-segment compact JWT without the
Bearerprefix. Header and payload segments must be base64url-encoded JSON objects.
Returns
contextSecurityContextContext whose principal is
sub, thenclient_id, then"unknown". VerifiedexpandiatNumericDate claims become UTC ISO timestamps. A dictionary-valuedmetadataclaim is retained; other non-registered claims are nested undermetadata["claims"].
Raises
ExceptionAny malformed segment, invalid JSON, algorithm mismatch, signature mismatch, invalid time claim, expiry, premature validity, future issue time, issuer mismatch, or audience mismatch is wrapped as
Exception("Token authentication failed: …").
The provider validates exp, nbf,
iat, and optionally iss and aud. It
does not require a subject, consult a revocation list, or enforce
application-specific authorization claims.
BearerTokenAuth.refresh_token
await refresh_token(
context: SecurityContext,
) -> SecurityContextReturn the existing bearer context unchanged. A signed access token cannot be renewed without a separate issuer or refresh-token protocol, which this local validator does not implement.
Parameters
contextSecurityContextrequiredExisting bearer context.
Returns
contextSecurityContextThe exact same object passed by the caller.
OAuth2DelegationAuth
class OAuth2DelegationAuth(
exchange_endpoint: str,
client_id: str,
client_secret: str,
)Exchange an incoming subject token for a narrower access token at an external
HTTP endpoint. The provider uses httpx.AsyncClient and sends a compact JSON
token-exchange request.
Parameters
exchange_endpointstrrequiredURL receiving the token-exchange POST request.
client_idstrrequiredOAuth client identifier included in the JSON request body.
client_secretstrrequiredOAuth client secret included in the JSON request body. Constructor values are stored without validation.
Advertised scheme
security_schemeSecuritySchemeOAuth 2 scheme whose metadata exposes the configured exchange endpoint.
OAuth2DelegationAuth.authenticate
await authenticate(
credentials: str,
) -> SecurityContextPOST a subject-token exchange request and translate a successful JSON response
into a SecurityContext.
Parameters
credentialsstrrequiredBroad-scoped subject token sent as
subject_token. The provider also sends the standard token-exchange grant type plus its client ID and secret.
Returns
contextSecurityContextContext populated from response fields:
subdefaults to"unknown",access_tokendefaults to an empty string, andmetadatadefaults to an empty dictionary.
Raises
ExceptionNon-200 responses, network failures, JSON decoding errors, and response conversion failures are wrapped as
Exception("OAuth delegation failed: …").dependency errorA missing
httpxinstallation is also caught and wrapped in the same generic OAuth-delegationException.
The current implementation copies the response's expires_in
value directly into SecurityContext.expires_at. OAuth servers
usually return a relative number of seconds there, while
SecurityContext.is_expired() expects an absolute ISO timestamp.
Normalize that field in an application-specific provider before calling
is_expired().
OAuth2DelegationAuth.refresh_token
await refresh_token(
context: SecurityContext,
) -> SecurityContextReturn the delegated context unchanged. Despite the method name, the current provider does not call a refresh endpoint or retain an OAuth refresh token.
Parameters
contextSecurityContextrequiredExisting delegated context.
Returns
contextSecurityContextThe exact same object passed by the caller.
APIKeyAuth
class APIKeyAuth(
valid_keys: dict[str, list[str]],
)Perform an in-memory membership check for static service credentials. This provider is useful for small service-to-service deployments where keys are loaded and rotated by the application.
Parameters
valid_keysdict[str, list[str]]requiredMapping from accepted credential strings to scope lists. The mapping is stored by reference. The current implementation checks only whether the key exists; it does not inspect or propagate the associated list.
Advertised scheme
security_schemeSecuritySchemeAPI-key scheme with no HTTP sub-scheme and no provider metadata.
Keys and dictionary lookups are plain Python strings. Hash keys at the application boundary or use a constant-time external verifier when your threat model requires protection against memory disclosure or timing analysis.
APIKeyAuth.authenticate
await authenticate(
credentials: str,
) -> SecurityContextCheck whether the credential is a key in the configured mapping.
Parameters
credentialsstrrequiredRaw API key after transport-level prefix extraction.
Returns
contextSecurityContextNon-expiring context whose principal ID is
"api-key-"followed by the first eight key characters and whose token is the complete key. The configured scope list is not copied into metadata.
Raises
ExceptionRaised with
"Invalid API key"when the credential is absent fromvalid_keys.
APIKeyAuth.refresh_token
await refresh_token(
context: SecurityContext,
) -> SecurityContextReturn the static-key context unchanged. API keys do not have a refresh protocol; rotate the configured key mapping instead.
Parameters
contextSecurityContextrequiredExisting API-key context.
Returns
contextSecurityContextThe exact same object passed by the caller.
BasicAuth
class BasicAuth(
valid_credentials: dict[str, str],
)Validate HTTP Basic-style username and password pairs against an in-memory
mapping. The authenticator accepts either the decoded username:password text
or the Base64 payload normally carried after an Authorization: Basic prefix.
Parameters
valid_credentialsdict[str, str]requiredMapping from usernames to expected plaintext passwords. The mapping is stored by reference and constructor values are not validated.
Advertised scheme
security_schemeSecuritySchemeHTTP Basic scheme used by transport header construction and discovery.
Basic authentication only encodes credentials; it does not encrypt them. Always use HTTPS or another confidential transport outside local development.
BasicAuth.authenticate
await authenticate(
credentials: str,
) -> SecurityContextDecode the credential when it is valid Base64, split the resulting text at the first colon, and compare the username/password pair with the configured mapping.
Parameters
credentialsstrrequiredBase64-encoded or raw
username:passwordstring. Passwords may contain additional colons because splitting occurs only once.
Returns
contextSecurityContextNon-expiring context whose principal ID is the decoded username and whose token remains the original encoded or raw credential string.
Raises
ExceptionRaised for a decoded value without a colon or for a username/password pair that does not exactly match the mapping.
The method attempts strict Base64 decoding first and treats the original value as raw text only when decoding fails. A raw value that also happens to be valid Base64 is therefore interpreted as encoded credentials.
BasicAuth.refresh_token
await refresh_token(
context: SecurityContext,
) -> SecurityContextReturn the Basic-authentication context unchanged. Rotate passwords in the application's credential store rather than through this hook.
Parameters
contextSecurityContextrequiredExisting Basic-authentication context.
Returns
contextSecurityContextThe exact same object passed by the caller.
Credential helper reference
extract_credentials
extract_credentials(
headers: Any,
query_params: dict[str, str] | None = None,
) -> str | NoneExtract one raw credential from HTTP-style headers or query parameters using a fixed precedence order. The helper removes known authorization prefixes but does not authenticate or decode the returned value.
Parameters
headersAnyrequiredHeader collection. Mapping-like objects are read with
.get(); other values are expected to iterate as(name, value)pairs. Header name handling covers canonical and lowercase names for mappings and is case-insensitive for pair iterables.query_paramsdict[str, str] | Nonedefault: NoneOptional query mapping checked only when no usable authorization or API key header was found.
Returns
credentialsstr | NoneFirst credential selected by precedence after surrounding whitespace and a recognized authorization prefix are removed, or
Nonewhen no supported location is present. A prefix-only or whitespace-only value can produce the empty string.
Precedence
1Authorization headerRemoves a case-insensitive
Bearer,Basic,ApiKey, orapikeyprefix. Unknown schemes are returned as raw stripped header text.2X-API-Key headerUses the stripped header value when no authorization credential won.
3query parameterChecks
api_key, thenapikey, thentoken.
Raises
collection errorThe helper does not normalize arbitrary header objects. Invalid iterable shapes, non-string names or values, and custom
.get()failures may propagate their native exceptions.
Examples
from protolink.security import extract_credentials
extract_credentials({"Authorization": "Bearer signed.jwt"})
# "signed.jwt"
extract_credentials({}, {"token": "query-token"})
# "query-token"