API Reference¶
This page documents the public API of the oidc-jwt-verifier library.
Public API¶
The library exports three main components:
AuthConfig- Configuration dataclass for JWT verification settingsAuthError- Exception type for authentication/authorization failuresJWTVerifier- Main verifier class for validating JWTs
Configuration¶
AuthConfig(issuer: str, audience: str | Sequence[str], jwks_url: str, allowed_algs: Sequence[str] = ('RS256',), leeway_s: int = 0, jwks_timeout_s: int = 3, jwks_cache_ttl_s: int = 300, jwks_max_cached_keys: int = 16, required_scopes: Sequence[str] = (), required_permissions: Sequence[str] = (), scope_claim: str = 'scope', permissions_claim: str = 'permissions')
dataclass
¶
Immutable configuration for JWT verification.
This dataclass holds all settings required by JWTVerifier to validate
JWTs against an OIDC provider. The configuration is frozen (immutable)
and uses slots for memory efficiency.
All string inputs are stripped of leading/trailing whitespace during validation. Sequences are normalized to tuples.
Attributes:
| Name | Type | Description |
|---|---|---|
issuer |
str
|
The expected |
audience |
str | Sequence[str]
|
One or more expected |
jwks_url |
str
|
The URL to fetch the JSON Web Key Set from. This URL is used for all key lookups; the verifier never derives JWKS URLs from token headers. |
allowed_algs |
Sequence[str]
|
Permitted signing algorithms. Defaults to |
leeway_s |
int
|
Clock skew tolerance in seconds for |
jwks_timeout_s |
int
|
HTTP timeout in seconds for JWKS fetches. Defaults to 3. |
jwks_cache_ttl_s |
int
|
Time-to-live in seconds for cached JWKS data. Must be in the range (0, 86400]. Defaults to 300. |
jwks_max_cached_keys |
int
|
Maximum number of signing keys to cache. Must be in the range (0, 1024]. Defaults to 16. |
required_scopes |
Sequence[str]
|
Scopes that must be present in the token for
authorization to succeed. Checked against the |
required_permissions |
Sequence[str]
|
Permissions that must be present in the token.
Checked against the |
scope_claim |
str
|
The claim name containing OAuth 2.0 scopes. Defaults
to |
permissions_claim |
str
|
The claim name containing permissions (commonly
used by Auth0). Defaults to |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any validation constraint is violated during
construction. Specific conditions include:
- Empty or whitespace-only |
Examples:
Minimal configuration for Auth0:
>>> config = AuthConfig(
... issuer="https://example.auth0.com/",
... audience="https://api.example.com",
... jwks_url="https://example.auth0.com/.well-known/jwks.json",
... )
>>> config.audiences
('https://api.example.com',)
>>> config.allowed_algorithms
('RS256',)
Configuration with multiple audiences and scope requirements:
>>> config = AuthConfig(
... issuer="https://example.auth0.com/",
... audience=["https://api.example.com", "https://api2.example.com"],
... jwks_url="https://example.auth0.com/.well-known/jwks.json",
... allowed_algs=["RS256", "RS384"],
... required_scopes=["read:users", "write:users"],
... )
>>> config.audiences
('https://api.example.com', 'https://api2.example.com')
>>> config.required_scope_set
{'read:users', 'write:users'}
Invalid configuration raises ValueError:
>>> AuthConfig(
... issuer="",
... audience="api",
... jwks_url="https://example.com/.well-known/jwks.json",
... )
Traceback (most recent call last):
...
ValueError: issuer must be non-empty
audiences: tuple[str, ...]
property
¶
Return the configured audiences as a tuple.
This property provides consistent tuple access regardless of whether
the audience attribute was initialized with a single string or
a sequence.
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
A tuple of audience strings. |
Examples:
allowed_algorithms: tuple[str, ...]
property
¶
Return the allowed algorithms as a tuple.
This property provides consistent tuple access regardless of whether
the allowed_algs attribute was initialized with a single string
or a sequence.
Returns:
| Type | Description |
|---|---|
tuple[str, ...]
|
A tuple of algorithm name strings. |
Examples:
required_scope_set: set[str]
property
¶
Return the required scopes as a set for efficient membership testing.
Empty strings in the required_scopes sequence are filtered out.
Returns:
| Type | Description |
|---|---|
set[str]
|
A set of non-empty scope strings. |
Examples:
required_permission_set: set[str]
property
¶
Return the required permissions as a set for efficient membership testing.
Empty strings in the required_permissions sequence are filtered out.
Returns:
| Type | Description |
|---|---|
set[str]
|
A set of non-empty permission strings. |
Examples:
__post_init__() -> None
¶
Validate and normalize configuration values after initialization.
This method runs automatically after dataclass initialization. It strips whitespace from string values, normalizes sequences to tuples, and validates all constraints.
Raises:
| Type | Description |
|---|---|
ValueError
|
If any validation constraint is violated. |
Source code in oidc_jwt_verifier/config.py
Errors¶
AuthError(*, code: str, message: str, status_code: int, required_scopes: Iterable[str] = (), required_permissions: Iterable[str] = ())
¶
Bases: Exception
Exception raised on authentication or authorization failure.
This exception provides structured error information including a stable error code for programmatic handling, an HTTP status code (401 for authentication failures, 403 for authorization failures), and a method to generate RFC 6750-compliant WWW-Authenticate header values.
The exception message is accessible via the standard str() conversion
or the message attribute.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
A stable string identifier for the error type. Common values
include |
|
message |
A human-readable description of the error. This is also set as the exception message. |
|
status_code |
The HTTP status code to return. Must be 401 (Unauthorized) for authentication errors or 403 (Forbidden) for authorization errors. |
|
required_scopes |
A tuple of scope strings that were required but
missing from the token. Populated for |
|
required_permissions |
A tuple of permission strings that were required
but missing from the token. Populated for |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
Creating an authentication error (401):
>>> error = AuthError(
... code="token_expired",
... message="Token is expired",
... status_code=401,
... )
>>> str(error)
'Token is expired'
>>> error.code
'token_expired'
>>> error.status_code
401
Creating an authorization error (403) with scope requirements:
>>> error = AuthError(
... code="insufficient_scope",
... message="Insufficient scope",
... status_code=403,
... required_scopes=["read:users", "write:users"],
... )
>>> error.required_scopes
('read:users', 'write:users')
Generating a WWW-Authenticate header:
>>> error = AuthError(
... code="invalid_token",
... message="Malformed token",
... status_code=401,
... )
>>> error.www_authenticate_header(realm="api")
'Bearer realm="api", error="invalid_token", error_description="Malformed token"'
Invalid status code raises ValueError:
>>> AuthError(code="error", message="msg", status_code=500)
Traceback (most recent call last):
...
ValueError: status_code must be 401 or 403
Initialize an authentication or authorization error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
A stable string identifier for the error type. |
required |
message
|
str
|
A human-readable error description. |
required |
status_code
|
int
|
The HTTP status code (must be 401 or 403). |
required |
required_scopes
|
Iterable[str]
|
Scopes that were required but missing. Defaults to an empty tuple. |
()
|
required_permissions
|
Iterable[str]
|
Permissions that were required but missing. Defaults to an empty tuple. |
()
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in oidc_jwt_verifier/errors.py
www_authenticate_header(*, realm: str | None = None) -> str
¶
Generate an RFC 6750-compliant WWW-Authenticate header value.
Constructs a Bearer authentication challenge suitable for use as the value of an HTTP WWW-Authenticate header. The challenge includes the error type (mapped to RFC 6750 error codes) and a description.
RFC 6750 defines two relevant error codes:
- invalid_token: Used for 401 errors (authentication failures).
- insufficient_scope: Used for 403 errors (authorization failures).
If required_scopes is non-empty, a scope parameter is
included listing the missing scopes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
realm
|
str | None
|
Optional protection space identifier. If provided, it appears first in the challenge parameters. Common values include the API name or domain. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A string suitable for use as the WWW-Authenticate header value. |
str
|
The format is |
Examples:
Basic authentication error:
>>> error = AuthError(
... code="invalid_token",
... message="Token is expired",
... status_code=401,
... )
>>> error.www_authenticate_header()
'Bearer error="invalid_token", error_description="Token is expired"'
With realm:
>>> error.www_authenticate_header(realm="my-api")
'Bearer realm="my-api", error="invalid_token", error_description="Token is expired"'
Authorization error with required scopes:
>>> error = AuthError(
... code="insufficient_scope",
... message="Insufficient scope",
... status_code=403,
... required_scopes=["read:users"],
... )
>>> header = error.www_authenticate_header()
>>> "insufficient_scope" in header
True
>>> "read:users" in header
True
Source code in oidc_jwt_verifier/errors.py
Verifier¶
JWTVerifier(config: AuthConfig)
¶
Stateful JWT verifier for OIDC access tokens.
This class performs complete JWT verification including:
- Header validation: Rejects tokens with dangerous headers
(
jku,x5u,crit) and ensures the algorithm is in the allowlist. - Key retrieval: Fetches the signing key from the JWKS using the
token's
kidheader. - Signature verification: Validates the cryptographic signature.
- Claim validation: Checks
iss,aud,exp, andnbfclaims against configuration. - Authorization enforcement: Verifies required scopes and permissions are present (returns 403 on failure).
The verifier maintains a cached JWKS client for efficient key lookups across multiple token verifications.
Attributes:
| Name | Type | Description |
|---|---|---|
_config |
The authentication configuration. |
|
_jwks |
The JWKS client for signing key retrieval. |
Examples:
Basic token verification:
>>> from oidc_jwt_verifier import AuthConfig, AuthError, JWTVerifier
>>> config = AuthConfig(
... issuer="https://example.auth0.com/",
... audience="https://api.example.com",
... jwks_url="https://example.auth0.com/.well-known/jwks.json",
... )
>>> verifier = JWTVerifier(config)
>>> claims = verifier.verify_access_token(token)
>>> claims["sub"]
'auth0|123456789'
Handling verification errors:
>>> try:
... claims = verifier.verify_access_token(expired_token)
... except AuthError as e:
... print(f"Error: {e.code}, Status: {e.status_code}")
... print(e.www_authenticate_header())
Error: token_expired, Status: 401
Bearer error="invalid_token", error_description="Token is expired"
Verifying tokens with scope requirements:
>>> config = AuthConfig(
... issuer="https://example.auth0.com/",
... audience="https://api.example.com",
... jwks_url="https://example.auth0.com/.well-known/jwks.json",
... required_scopes=["read:users"],
... )
>>> verifier = JWTVerifier(config)
>>> # Token without required scopes raises AuthError with 403
>>> claims = verifier.verify_access_token(token_without_scopes)
Traceback (most recent call last):
...
AuthError: Insufficient scope
Initialize a JWT verifier with the given configuration.
Creates a JWKS client configured with the caching parameters from the provided configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AuthConfig
|
The authentication configuration specifying the issuer, audience, JWKS URL, allowed algorithms, and authorization requirements. |
required |
Examples:
>>> from oidc_jwt_verifier import AuthConfig
>>> config = AuthConfig(
... issuer="https://example.auth0.com/",
... audience="https://api.example.com",
... jwks_url="https://example.auth0.com/.well-known/jwks.json",
... )
>>> verifier = JWTVerifier(config)
Source code in oidc_jwt_verifier/verifier.py
verify_access_token(token: str) -> dict[str, Any]
¶
Verify an access token and return its claims.
Performs the complete verification chain:
- Validates the token is non-empty.
- Parses and validates the token header (rejects
jku,x5u,crit; validatesalgandkid). - Fetches the signing key from the JWKS.
- Decodes and verifies the token signature.
- Validates standard claims (
iss,aud,exp,nbf). - Enforces required scopes and permissions.
The method supports Auth0-style multi-audience tokens where the
aud claim is an array. Verification succeeds if any configured
audience matches any audience in the token.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
The encoded JWT access token string. Leading and trailing whitespace is stripped. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The decoded token payload as a dictionary. Contains all |
dict[str, Any]
|
claims from the token including registered claims ( |
dict[str, Any]
|
|
Raises:
| Type | Description |
|---|---|
AuthError
|
On any verification failure. The error's
Specific error codes include:
- |
Examples:
Successful verification:
>>> claims = verifier.verify_access_token(valid_token)
>>> claims["sub"]
'auth0|123456789'
>>> claims["aud"]
'https://api.example.com'
Missing token:
>>> verifier.verify_access_token("")
Traceback (most recent call last):
...
AuthError: Missing access token
Expired token:
>>> verifier.verify_access_token(expired_token)
Traceback (most recent call last):
...
AuthError: Token is expired
Source code in oidc_jwt_verifier/verifier.py
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | |