Python library for OpenID Connect (OIDC) authentication with DPoP (Demonstrating Proof-of-Possession) support, featuring JWT validation, token caching, and both sync/async operations.
Project description
axa-fr-oidc
| Python | Project |
|---|---|
A Python library for OpenID Connect (OIDC) authentication with DPoP (Demonstrating Proof-of-Possession) support, featuring JWT validation, token caching, and both sync/async operations.
Table of Contents (ToC)
- Table of Contents (ToC)
- Features
- Installation
- Quick Start
- Advanced Usage (Low-Level API)
- API Reference
- Advanced Configuration
- Development
- Contributing
Features
- 🔐 OIDC Authentication - Full OpenID Connect authentication support
- 🔑 DPoP Support - Demonstrating Proof-of-Possession for enhanced security
- ✅ JWT Validation - Comprehensive token validation with JWKS
- 💾 Token Caching - Built-in memory cache for tokens and JWKS
- ⚡ Async/Sync - Supports both synchronous and asynchronous operations
- 🎯 Type Safe - Fully typed with Python type hints
- 🔒 Flexible Auth Methods -
client_secret_jwt,client_secret_post, andclient_secret_basicwith automatic fallback
Installation
Using uv (recommended)
uv add axa-fr-oidc
Using pip
pip install axa-fr-oidc
Quick Start
Simple Usage with OidcClient (Recommended)
The OidcClient provides a simplified, high-level API for common OIDC operations:
from axa_fr_oidc import OidcClient
# Create a client with client credentials
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
scopes=["openid", "profile"],
audience="your-api-audience",
)
# Get an access token (automatically cached and refreshed)
access_token = client.get_access_token()
# Force a fresh token from the authorization server (bypasses cache)
fresh_token = client.get_access_token(force_renew_token=True)
# Validate a token
result = client.validate_token(access_token)
if result.success:
print(f"Token is valid! Subject: {result.payload['sub']}")
else:
print(f"Token is invalid: {result.error}")
# Clean up resources
client.close_sync()
Using Context Managers
from axa_fr_oidc import OidcClient
# Sync context manager
with OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
) as client:
token = client.get_access_token()
# Async context manager
async with OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
) as client:
token = await client.get_access_token_async()
Async Operations
import asyncio
from axa_fr_oidc import OidcClient
async def main():
async with OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
) as client:
# Async token retrieval
token = await client.get_access_token_async()
# Async token validation
result = await client.validate_token_async(token)
print(result.success, result.payload)
asyncio.run(main())
Private Key Authentication
from axa_fr_oidc import OidcClient
# Load your private key
with open("private_key.pem", "r") as f:
private_key_pem = f.read()
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
private_key=private_key_pem,
algorithm="RS256",
scopes=["openid", "profile"],
)
token = client.get_access_token()
Validating DPoP Tokens
from axa_fr_oidc import OidcClient
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
)
# Validate a DPoP-bound token
result = client.validate_token(
token=access_token,
dpop=dpop_proof,
path="/api/resource",
http_method="POST",
)
print(result.success, result.error)
Token Exchange
from axa_fr_oidc import OidcClient
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
)
# Exchange a token (RFC 8693)
new_token = client.token_exchange(
subject_token=user_token,
requested_token_type="urn:ietf:params:oauth:token-type:access_token",
)
Custom HTTP Configuration (Proxies, SSL, Timeouts)
The client supports custom HTTP configurations including proxy settings, SSL verification, and timeouts:
from axa_fr_oidc import OidcClient
# Using a proxy
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
proxy="http://proxy.example.com:8080",
)
# Using an HTTPS proxy
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
proxy="https://secure-proxy.example.com:8443",
)
# Disable SSL verification (not recommended for production)
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
verify=False,
)
# Set custom timeout (in seconds)
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
timeout=30.0,
)
# Combine multiple HTTP configurations
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
proxy="http://proxy.example.com:8080",
verify=True,
timeout=10.0,
)
token = client.get_access_token()
Client Secret Authentication Methods
When using client_secret, you can control how the credentials are sent to the
token endpoint via the auth_method parameter.
auth_method |
Behaviour |
|---|---|
"client_secret_jwt" (default) |
Signs an HS256 JWT assertion (RFC 7523). Automatically falls back to client_secret_post on 401 if the server does not have this method enabled for the client. |
"client_secret_post" |
Sends client_id + client_secret in the POST body. Broadly supported. |
"client_secret_basic" |
Sends credentials as an HTTP Basic Auth header. Broadly supported. |
from axa_fr_oidc import OidcClient
from axa_fr_oidc.constants import (
CLIENT_SECRET_AUTH_METHOD_JWT, # "client_secret_jwt" (default)
CLIENT_SECRET_AUTH_METHOD_POST, # "client_secret_post"
CLIENT_SECRET_AUTH_METHOD_BASIC, # "client_secret_basic"
)
# Default: tries client_secret_jwt, falls back to client_secret_post on 401
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
)
# Explicitly use client_secret_post (no fallback overhead)
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
auth_method=CLIENT_SECRET_AUTH_METHOD_POST,
)
# Explicitly use client_secret_basic
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
auth_method=CLIENT_SECRET_AUTH_METHOD_BASIC,
)
token = client.get_access_token()
For more details, see the Client Secret Auth Methods Guide.
Extract Properties from a JWT Token
from axa_fr_oidc import JWTAuthorization
authorization_header = "<your-jwt-token>"
jwt_auth = JWTAuthorization(authorization_header)
print(jwt_auth.get_property("sub")) # Print the subject of the token
print(jwt_auth.get_property("exp")) # Print the expiration time of the token
Advanced Usage (Low-Level API)
For users who need more control over the authentication process, the library provides low-level components that can be customized individually.
Using OpenIdConnect and OidcAuthentication Directly
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
from httpx import AsyncClient, Client
# Create HTTP clients
http_client = Client()
http_async_client = AsyncClient()
# Create HTTP service
http_service = XHttpServiceGet(
http_client=http_client,
http_async_client=http_async_client
)
# Create cache
memory_cache = MemoryCache()
# Create authentication handler
auth = OidcAuthentication(
issuer="https://issuer.url",
scopes=["openid", "profile"],
api_audience="your-api-audience",
service=http_service,
memory_cache=memory_cache,
algorithms=["RS256", "ES256"],
)
# Create OpenID Connect client
oidc = OpenIdConnect(
authentication=auth,
memory_cache=memory_cache,
client_id="your-client-id",
client_secret="your-client-secret"
)
# Get access token
access_token = oidc.get_access_token()
# Validate token
result = auth.validate(access_token, None, None, None)
print(result.success, result.error)
Async Operations with Low-Level API
All low-level components support async/await:
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
from httpx import AsyncClient, Client
async def main():
http_service = XHttpServiceGet(
http_client=Client(),
http_async_client=AsyncClient()
)
memory_cache = MemoryCache()
auth = OidcAuthentication(
issuer="https://issuer.url",
scopes=["openid", "profile"],
api_audience="your-api-audience",
service=http_service,
memory_cache=memory_cache
)
oidc = OpenIdConnect(
authentication=auth,
memory_cache=memory_cache,
client_id="your-client-id",
client_secret="your-client-secret"
)
# Async token retrieval
access_token = await oidc.get_access_token_async()
# Async token validation
result = await auth.validate_async(access_token, None, None, None)
print(result.success, result.payload)
# Run with asyncio
import asyncio
asyncio.run(main())
Using Private Key Authentication (Low-Level)
For client credentials flow with private key (JWT bearer) using the low-level API:
from axa_fr_oidc import OidcAuthentication, OpenIdConnect, MemoryCache, XHttpServiceGet
from httpx import AsyncClient, Client
# Load your private key
with open("private_key.pem", "r") as f:
private_key_pem = f.read()
http_service = XHttpServiceGet(
http_client=Client(),
http_async_client=AsyncClient()
)
memory_cache = MemoryCache()
auth = OidcAuthentication(
issuer="https://issuer.url",
scopes=["openid", "profile"],
api_audience="your-api-audience",
service=http_service,
memory_cache=memory_cache
)
oidc = OpenIdConnect(
authentication=auth,
memory_cache=memory_cache,
client_id="your-client-id",
private_key=private_key_pem,
algorithm="RS256" # or other supported algorithms
)
access_token = oidc.get_access_token()
Note: For most use cases, consider using the simpler
OidcClientinstead. See the Quick Start section for examples.
Custom Configuration
You can customize various timeouts and cache settings:
Using OidcClient
from axa_fr_oidc import OidcClient
client = OidcClient(
issuer="https://issuer.url",
client_id="your-client-id",
client_secret="your-client-secret",
scopes=["openid", "profile"],
audience="your-api-audience",
algorithms=["RS256", "ES256"], # Allowed algorithms for validation
issuer_cache_expiration_seconds=7200, # Cache JWKS and token_endpoint for 2 hours (default: 3600)
)
Using Low-Level API
from axa_fr_oidc import (
OidcAuthentication,
MemoryCache,
XHttpServiceGet,
)
from httpx import AsyncClient, Client
auth = OidcAuthentication(
issuer="https://issuer.url",
scopes=["openid", "profile"],
api_audience="your-api-audience",
service=XHttpServiceGet(
http_client=Client(),
http_async_client=AsyncClient()
),
memory_cache=MemoryCache(),
algorithms=["RS256", "ES256"], # Supported algorithms
)
API Reference
High-Level Client (Recommended)
OidcClient- Simplified, all-in-one client for OIDC operationsget_access_token(force_renew_token=False)/get_access_token_async(force_renew_token=False)- Get an access token (setforce_renew_token=Trueto bypass cache)validate_token()/validate_token_async()- Validate an access tokentoken_exchange()- Exchange tokens (RFC 8693)get_token_endpoint()/get_token_endpoint_async()- Get the token endpoint URLclear_cache()- Clear all cached dataclose()/close_sync()- Release resources- Supports context managers (
with/async with)
Low-Level Classes
OidcAuthentication- OIDC token validation and JWKS managementOpenIdConnect- Client for obtaining access tokensMemoryCache- In-memory cache for tokens and JWKSXHttpServiceGet- HTTP service wrapper for sync/async requestsJWTAuthorization- Utility for extracting JWT claimsAuthenticationResult- Result object from validation operations
Interfaces
All main classes have corresponding interfaces for dependency injection:
IOidcAuthentication- Interface for OidcAuthenticationIOpenIdConnect- Interface for OpenIdConnectIMemoryCache- Interface for MemoryCacheIHttpServiceGet- Interface for XHttpServiceGetIGenericAuthorization- Interface for JWTAuthorization
Constants
The library exports useful constants for configuration:
from axa_fr_oidc import (
DEFAULT_DPOP_MAX_AGE_SECONDS, # 300 (5 minutes)
DEFAULT_CLOCK_SKEW_SECONDS, # 300 (5 minutes)
DEFAULT_JTI_LIFETIME_SECONDS, # 300 (5 minutes)
DEFAULT_JWT_ALGORITHM, # "RS256"
DEFAULT_JWT_EXPIRATION_SECONDS, # 300 (5 minutes)
DEFAULT_HTTP_TIMEOUT_SECONDS, # 5 seconds
SUPPORTED_ALGORITHMS, # ["RS256", "HS256"]
DPOP_TOKEN_TYPE, # "dpop+jwt"
GRANT_TYPE_CLIENT_CREDENTIALS, # "client_credentials"
CLIENT_ASSERTION_TYPE_JWT_BEARER, # "urn:ietf:params:oauth:..."
CONTENT_TYPE_FORM_URLENCODED, # "application/x-www-form-urlencoded"
OIDC_WELL_KNOWN_PATH, # "/.well-known/openid-configuration"
CLIENT_SECRET_AUTH_METHOD_JWT, # "client_secret_jwt"
CLIENT_SECRET_AUTH_METHOD_POST, # "client_secret_post"
CLIENT_SECRET_AUTH_METHOD_BASIC, # "client_secret_basic"
)
Advanced Configuration
Client Secret Authentication Methods
For detailed information on configuring the client-secret auth method
(client_secret_jwt, client_secret_post, client_secret_basic) and the
automatic fallback behaviour, see the
Client Secret Auth Methods Guide.
Proxy, SSL, and Timeout Configuration
For detailed information on configuring HTTP proxies, SSL verification, and timeouts, see the Proxy Configuration Guide.
Quick examples:
# Using a proxy
client = OidcClient(
issuer="https://auth.example.com",
client_id="your-client-id",
client_secret="your-client-secret",
proxy="http://proxy.example.com:8080",
)
# With custom timeout
client = OidcClient(
issuer="https://auth.example.com",
client_id="your-client-id",
client_secret="your-client-secret",
timeout=30.0,
)
Development
Setup Development Environment
# Clone the repository
git clone https://github.com/your-org/axa-fr-oidc.git
cd axa-fr-oidc
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync --group dev
Using the Makefile
The project includes a Makefile for convenient development commands:
# Show all available commands
make help
# Install dependencies
make install # Production dependencies only
make install-dev # All development dependencies
make install-quality # Quality check tools only
make install-test # Test dependencies only
# Code quality
make lint # Run ruff linter (includes docstring checks)
make lint-fix # Run ruff linter with auto-fix
make format # Run ruff formatter
make format-check # Check formatting without changes
make type-check # Run mypy type checker
# Security
make security # Run bandit security checks
make security-audit # Run pip-audit for dependency vulnerabilities
# Testing
make test # Run tests
make test-cov # Run tests with coverage report
# Combined commands
make quality # Run all quality checks (lint, format, type-check, security)
make all # Run quality checks and tests
# Cleanup
make clean # Remove build artifacts and cache files
Running Tests
# Using make
make test
# Or directly with uv
uv run pytest
Running Quality Checks
# Run all quality checks at once
make quality
# Or run individual checks
make lint
make type-check
make security
Installing Specific Dependency Groups
# Install only test dependencies
uv sync --group test
# Install only linting tools
uv sync --group lint
# Install only security tools
uv sync --group security
# Install everything for development
uv sync --group dev
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file axa_fr_oidc-1.4.0.tar.gz.
File metadata
- Download URL: axa_fr_oidc-1.4.0.tar.gz
- Upload date:
- Size: 22.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00e5c6a6e33dc8bc3644eb7ce35a53ff1864f3eb7afcc51e9d23d7b81ece0724
|
|
| MD5 |
ecc0fb55aa125d245c45b26a94de5765
|
|
| BLAKE2b-256 |
e2bcb9419f5ca2ecc99fb37c7b713846af92ee22c87ea97f9b5ab3cd0d657fdc
|
File details
Details for the file axa_fr_oidc-1.4.0-py3-none-any.whl.
File metadata
- Download URL: axa_fr_oidc-1.4.0-py3-none-any.whl
- Upload date:
- Size: 27.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3237896be25cd9fe7fc28000641bdb05a1393bc4d5f04b65e04d27ba66417e1
|
|
| MD5 |
1da177755d36eba6c947e6f162a26055
|
|
| BLAKE2b-256 |
32e5675e72351ae1c5c03801fcff8f57e9aaed4432343c463cf308ea0528d2f8
|