Jwks
Validate a JWT against a JWKS (JSON Web Key Set).
- Overview
- jwks_url
- audience
- issuer
- algorithms
- jwks_cache_time
- authorization_url
- claims
- documentation_security_name
Overview
This authentication class fetches a set of public keys from a remote JWKS endpoint and uses them to verify the signature of a Bearer token presented in the Authorization header. It is the standard way to validate JWTs issued by OAuth 2.0 / OIDC providers such as Auth0, Azure Active Directory, or any provider that publishes a JWKS document.
The minimum configuration is a jwks_url. You will typically also want to set audience and issuer to ensure the token was issued for your application:
import clearskies
wsgi = clearskies.contexts.WsgiRef(
clearskies.endpoints.Callable(
lambda authorization_data: authorization_data,
authentication=clearskies.authentication.Jwks(
jwks_url="https://auth.example.com/.well-known/jwks.json",
audience="https://api.example.com/",
issuer="https://auth.example.com/",
),
)
)
wsgi()
Calling the endpoint without a valid token returns a 401:
$ curl http://localhost:8080 | jq
{
"status": "client_error",
"error": "Not Authenticated",
"data": [],
"pagination": {},
"input_errors": {}
}
With a valid Bearer token the decoded JWT payload is available via the authorization_data dependency:
$ curl http://localhost:8080 -H "Authorization: Bearer <token>" | jq
{
"status": "success",
"error": "",
"data": {
"sub": "example-user",
"iss": "https://auth.example.com/",
"aud": "https://api.example.com/"
},
"pagination": {},
"input_errors": {}
}
jwks_url
Required
The URL from which to fetch the JSON Web Key Set.
clearskies fetches this URL to retrieve the public keys used to verify incoming JWTs. The response must be a standard JWKS document — a JSON object with a keys array. Key selection is performed automatically by matching the kid header of the incoming token against the keys in the set.
The key set is cached for jwks_cache_time seconds (default one day) to avoid a remote fetch on every request.
audience
Optional
The audience to accept JWTs for.
If provided, JWTs will be rejected unless their aud claim contains this value. You should always set this to the identifier of your own API to prevent tokens issued for other services from being accepted. If you do not provide an audience then all audiences will be accepted.
issuer
Optional
The expected issuer of the JWTs.
If provided, JWTs will be rejected unless their iss claim exactly matches this value. Set this to the base URL of the authorization server that issues tokens for your application. If you do not provide an issuer then any issuer will be accepted.
algorithms
Optional
The allowed signing algorithms.
Passed directly to jwcrypto.jwt.JWT(algs=...). Any token whose alg header is not in this list is rejected before signature verification. An empty list disables the restriction; any algorithm that is cryptographically compatible with the matched JWKS key is then accepted.
jwks_cache_time
Optional
The number of seconds for which the JWKS URL contents can be cached.
Defaults to 86400 (one day). Set to 0 to disable caching and always fetch fresh keys.
authorization_url
Optional
The authorization URL used in the auto-generated API documentation.
This value is only used when generating OpenAPI documentation and has no effect on authentication behaviour at runtime.
claims
Optional
Additional claim validation applied after the JWT signature is verified.
Provide a list of claim names to require their presence in the decoded payload. A 401 is returned if any listed claim is missing:
import clearskies
wsgi = clearskies.contexts.WsgiRef(
clearskies.endpoints.Callable(
lambda: {"hello": "world"},
authentication=clearskies.authentication.Jwks(
jwks_url="https://example.com/.well-known/jwks.json",
claims=["role", "sub"],
),
)
)
wsgi()
For custom logic, provide a callable instead. The clearskies DI system injects jwt_claims (the decoded payload as a dict) plus any other name resolvable from the DI container, so only declare the parameters your function actually needs.
The callable may return True or None to accept the token, False to reject it with a 401, or a list of strings to dynamically specify which claims must be present:
import clearskies
def no_blocked_users(jwt_claims):
return "blocked" not in jwt_claims
wsgi = clearskies.contexts.WsgiRef(
clearskies.endpoints.Callable(
lambda: {"hello": "world"},
authentication=clearskies.authentication.Jwks(
jwks_url="https://example.com/.well-known/jwks.json",
claims=no_blocked_users,
),
)
)
wsgi()
You can also return a dynamic list of required claims based on the payload:
import clearskies
def require_extra_claims_for_admins(jwt_claims):
if jwt_claims.get("role") == "admin":
return ["department", "employee_id"]
return True
wsgi = clearskies.contexts.WsgiRef(
clearskies.endpoints.Callable(
lambda: {"hello": "world"},
authentication=clearskies.authentication.Jwks(
jwks_url="https://example.com/.well-known/jwks.json",
claims=require_extra_claims_for_admins,
),
)
)
wsgi()
documentation_security_name
Optional
The name of the security scheme in the auto-generated API documentation.
Defaults to jwt. Override this if your documentation needs to distinguish between multiple JWT-based authentication schemes on the same set of endpoints.