RFC9101: The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)¶
This section contains the generic implementation of RFC9101.
This specification describe how to pass the authorization request payload in a JWT (called request object) instead of directly instead of GET or POST params.
The request object can either be passed directly in a request parameter,
or be hosted by the client and be passed by reference with a request_uri
parameter. By reference, the Authorization Server fetches the request object
itself:
sequenceDiagram
autonumber
participant User as User's browser<br/>(User Agent)
participant Client
box rgba(80, 120, 220, 0.12) JWTAuthenticationRequest
participant AS as Authorization Server
end
Note over Client: Signs a request object
Client-->>User: 302 Found → /authorize
User->>AS: GET /authorize (request or request_uri)
opt By reference
AS->>Client: GET request_uri
Client-->>AS: 200 OK + request object
end
Note over AS: Verifies the request object
Note over User,AS: The user logs in and approves
AS-->>User: 302 Found → redirect_uri?code=…
The client signs a JWT that contains the authorization request parameters. It redirects the browser to the authorization endpoint.
The request contains
client_id, and either the JWT asrequest, or its URL asrequest_uri.JWTAuthenticationRequestrejects the request if both are present.With
request_urionly.validate_request_uridecides whether the server may fetch this URL for this client. It rejects every URL by default.get_request_objectthen fetches the JWT. See Retrieving request objects.The client returns the JWT.
In both cases,
JWTAuthenticationRequestchecks the JWT signature with the client key, the signing algorithm, and that theclient_idclaim matches theclient_idparameter. The claims of the JWT then replace the request parameters, and the authorization flow goes on as usual.
The /authorize path is an example.
This usage is more secure than passing the request payload directly in the request, read the RFC to know all the details.
Request objects are optional, unless it is enforced by clients with the
require_signed_request_object client metadata, or server-wide with the
require_signed_request_object server metadata.
Signature policy¶
When either the server or client requires signed request objects,
alg=none is rejected even if it appears in the server’s supported algorithm
list or is returned by get_request_object_signing_algorithms(). Unsigned
request objects remain supported when signatures are optional and the algorithm
configuration permits none.
Retrieving request objects¶
Implement validate_request_uri(request_uri, client) with an explicit policy
for the request objects your server can safely retrieve. The default rejects all
URIs, including URIs present in client metadata. Validation runs before
get_request_object() is called; rejected URIs return invalid_request_uri:
class JWTAuthenticationRequest(rfc9101.JWTAuthenticationRequest):
def validate_request_uri(self, request_uri, client):
return is_approved_request_uri(request_uri, client)
is_approved_request_uri is an application function: it must authorize the
reference for this client and enforce the destination restrictions described
below. Merely checking whether a URI was supplied by a client is insufficient.
Applications using references to locally stored objects, such as URNs, must
validate that those objects belong to the requesting client.
The generic JAR extension does not implement OIDC URI registration. OIDC providers
should use authlib.oidc.core.OpenIDJWTAuthenticationRequest; see
OpenID request objects. It checks require_request_uri_registration and
registered request_uris independently of the application’s retrieval policy.
For network retrieval, validate destinations during URI registration. Untrusted client metadata is not an approval policy. Require HTTPS, reject credentials in URLs, and restrict both hostnames and the IP addresses actually used for outbound connections. Block loopback, private, link-local, and other non-public destinations. Checking DNS once before an unrestricted HTTP request does not prevent DNS rebinding: the transport or network must enforce the destination restrictions.
Disable redirects, set connection and read timeouts, enforce an overall retrieval
deadline and response size limit, and require the response media type
application/oauth-authz-req+jwt. The get_request_object() API documentation
below includes an example of HTTP response limits. These safeguards apply even
to registered URIs, whose DNS records can change. JWT signature validation occurs
after retrieval and cannot prevent SSRF from the retrieval itself.
API Reference¶
- class authlib.oauth2.rfc9101.JWTAuthenticationRequest(support_request: bool = True, support_request_uri: bool = True)¶
Authorization server extension implementing the support for JWT secured authentication request, as defined in RFC9101.
- Parameters:
support_request – Whether to enable support for the
requestparameter.support_request_uri – Whether to enable support for the
request_uriparameter.
This extension is intended to be inherited and registered into the authorization server:
class JWTAuthenticationRequest(rfc9101.JWTAuthenticationRequest): def resolve_client_public_key(self, client: ClientMixin): return get_jwks_for_client(client) def validate_request_uri(self, request_uri: str, client: ClientMixin): return is_approved_request_uri(request_uri, client) def get_request_object(self, request_uri: str): # Apply the retrieval safeguards documented in get_request_object. return fetch_approved_request_object(request_uri) def get_server_metadata(self): return { "issuer": ..., "authorization_endpoint": ..., "require_signed_request_object": ..., } def get_client_require_signed_request_object(self, client: ClientMixin): return client.require_signed_request_object authorization_server.register_extension(JWTAuthenticationRequest())
- get_request_object_signing_algorithms(client)¶
Return the supported algorithms for verifying the
request_objectJWT signature. By default, this method will only return the recommended algorithms. If signed request object is not required, “none” algorithm will be included. The signature requirement is enforced independently, even if this method returns “none”.Developers can override this method to customize the supported algorithms:
def get_request_object_signing_algorithms(self, client): return ["RS256"]
- validate_request_uri(request_uri: str, client: ClientMixin) → bool¶
Return whether
request_urimay be retrieved for this client.Called before
get_request_object(). The default rejects every URI; applications supportingrequest_urimust implement a secure policy. For network retrieval, prevent SSRF, including private destinations and DNS rebinding. For references to locally stored request objects, validate that the object belongs to the requesting client.OIDC URI registration is handled by
authlib.oidc.core.JWTAuthenticationRequestindependently of this application policy.
- get_request_object(request_uri: str)¶
Retrieve the request object at an approved
request_uri.This method must be implemented if
request_uriis supported. For network retrieval, enforce HTTPS and restrict outbound connections to approved public destinations, including the resolved IP addresses. Do not follow redirects. Limit response size and retrieval time, and check the response media type, as described in RFC 9101 Section 10.4.1.The following example adds HTTP response safeguards. The application’s HTTP transport or network policy must also block private destinations and prevent DNS rebinding:
def get_request_object(self, request_uri: str): try: with requests.get( request_uri, timeout=(3, 5), allow_redirects=False, stream=True, headers={"Accept": "application/oauth-authz-req+jwt"}, ) as response: media_type = response.headers.get("Content-Type", "").split( ";", 1 )[0] if ( response.status_code != 200 or media_type != "application/oauth-authz-req+jwt" ): return None content = bytearray() for chunk in response.iter_content(chunk_size=4096): content.extend(chunk) if len(content) > 262144: return None return content.decode("utf-8") except (requests.RequestException, UnicodeError): return None
A timeout between reads is not an overall deadline. Also enforce an overall retrieval deadline in the transport or application.
- resolve_client_public_key(client: ClientMixin)¶
Resolve the client public key for verifying the JWT signature. A client may have many public keys, in this case, we can retrieve it via
kidvalue in headers. Developers MUST implement this method:from joserfc import KeySet class JWTAuthenticationRequest(rfc9101.JWTAuthenticationRequest): def resolve_client_public_key(self, client): if client.jwks_uri: data = requests.get(client.jwks_uri).json() return KeySet.import_key_set(data) return KeySet.import_key_set(client.jwks)
- get_server_metadata() → dict¶
Return server metadata which includes supported grant types, response types and etc.
When the
require_signed_request_objectclaim isTrue, all clients require that authorization requests use request objects, and an error will be returned when the authorization request payload is passed in the request body or query string:class JWTAuthenticationRequest(rfc9101.JWTAuthenticationRequest): def get_server_metadata(self): return { "issuer": ..., "authorization_endpoint": ..., "require_signed_request_object": ..., "request_object_signing_alg_values_supported": ["RS256", ...], }
- get_client_require_signed_request_object(client: ClientMixin) → bool¶
Return the ‘require_signed_request_object’ client metadata.
When
True, the client requires that authorization requests use request objects, and an error will be returned when the authorization request payload is passed in the request body or query string:class JWTAuthenticationRequest(rfc9101.JWTAuthenticationRequest): def get_client_require_signed_request_object(self, client): return client.require_signed_request_object
If not implemented, the value is considered as
False.
- class authlib.oauth2.rfc9101.ClientMetadataClaims(claims: dict[str, Any], header: dict[str, Any], options: dict[str, ClaimsOption] | None = None, params: dict[str, Any] = None)¶
Additional client metadata can be used with RFC7591: OAuth 2.0 Dynamic Client Registration Protocol and RFC7592: OAuth 2.0 Dynamic Client Registration Management Protocol endpoints.
This can be used with:
server.register_endpoint( ClientRegistrationEndpoint( claims_classes=[ rfc7591.ClientMetadataClaims, rfc9101.ClientMetadataClaims, ] ) ) server.register_endpoint( ClientRegistrationEndpoint( claims_classes=[ rfc7591.ClientMetadataClaims, rfc9101.ClientMetadataClaims, ] ) )
- class authlib.oauth2.rfc9101.AuthorizationServerMetadata¶
Authorization Server Metadata extension for RFC9101 (JAR).
This class can be used with
validate()to validate JAR-specific metadata:from authlib.oauth2 import rfc8414, rfc9101 metadata = rfc8414.AuthorizationServerMetadata(data) metadata.validate(metadata_classes=[rfc9101.AuthorizationServerMetadata])
- validate_require_signed_request_object()¶
Indicates where authorization request needs to be protected as Request Object and provided through either request or request_uri parameter.