OAuth2.0 and OpenID Connect Client Library
Project description
py-identity-model
Production-grade OIDC/OAuth2.0 client library for Python. Dual sync/async API with comprehensive RFC coverage. Inspired by Duende.IdentityModel.
Installation
pip install py-identity-model
Or with uv:
uv add py-identity-model
Requirements: Python 3.12+
Features
Protocol Support
| Feature | RFC/Spec | Status |
|---|---|---|
| OpenID Connect Discovery 1.0 | OIDC Discovery | Done |
| JSON Web Key Sets | RFC 7517 | Done |
| JWT Validation | RFC 7519 | Done |
| Client Credentials Grant | RFC 6749 | Done |
| Authorization Code + PKCE | RFC 7636 | Done |
| Refresh Token Grant | RFC 6749 | Done |
| Token Introspection | RFC 7662 | Done |
| Token Revocation | RFC 7009 | Done |
| Device Authorization | RFC 8628 | Done |
| Token Exchange | RFC 8693 | Done |
| DPoP | RFC 9449 | Done |
| Pushed Authorization Requests | RFC 9126 | Done |
| JWT Secured Authorization Request | RFC 9101 | Done |
| FAPI 2.0 Security Profile | FAPI 2.0 | Done |
| UserInfo Endpoint | OIDC Core | Done |
| OAuth Callback State Validation | RFC 6749 | Done |
| Policy-Based Configuration | — | Done |
Architecture
- Dual API: Synchronous (
py_identity_model) and asynchronous (py_identity_model.aio) - Thread-safe: Sync uses thread-local HTTP clients; async uses singleton with lock protection
- Modular: Shared business logic in
core/, thin sync/async HTTP wrappers - Connection pooling: httpx-based with configurable timeouts and retry logic
Quick Start
Discovery
from py_identity_model import DiscoveryDocumentRequest, get_discovery_document
response = get_discovery_document(DiscoveryDocumentRequest(address="https://issuer.example.com"))
if response.is_successful:
print(f"Token Endpoint: {response.token_endpoint}")
Token Validation
from py_identity_model import TokenValidationConfig, validate_token
config = TokenValidationConfig(perform_disco=True, audience="my-api")
claims = validate_token(jwt=token, token_validation_config=config, disco_doc_address="https://issuer.example.com")
Async API
from py_identity_model.aio import get_discovery_document, validate_token
from py_identity_model import DiscoveryDocumentRequest
response = await get_discovery_document(DiscoveryDocumentRequest(address=url))
Client Credentials
from py_identity_model import ClientCredentialsTokenRequest, request_client_credentials_token
token = request_client_credentials_token(ClientCredentialsTokenRequest(
client_id="my-client", client_secret="secret",
address=token_endpoint, scope="api"
))
Examples
Each protocol feature has a standalone example in examples/:
| Example | Feature |
|---|---|
| sync_examples.py | Discovery, JWKS, token validation, client credentials |
| async_examples.py | Async versions of all core operations |
| auth_code_pkce_example.py | Authorization Code + PKCE flow |
| introspection_example.py | Token introspection (RFC 7662) |
| revocation_example.py | Token revocation (RFC 7009) |
| dpop_example.py | DPoP proof creation (RFC 9449) |
| par_example.py | Pushed Authorization Requests (RFC 9126) |
| jar_example.py | JWT Secured Authorization Request (RFC 9101) |
| fapi_example.py | FAPI 2.0 Security Profile |
| device_auth_example.py | Device Authorization Grant (RFC 8628) |
| token_exchange_example.py | Token Exchange (RFC 8693) |
Framework Integration
py-identity-model is framework-agnostic — call validate_token (or py_identity_model.aio.validate_token) from any Python framework's auth layer. For FastAPI, examples/fastapi/ ships a complete, production-shaped integration you can copy into your own project:
TokenValidationMiddleware— Bearer token extraction, JWKS-cached validation, configurable excluded paths, pluggable custom-claim validators- Dependency injection —
CurrentUser,Claims,Tokenannotated types for typed access to the authenticated principal from route handlers - Authorization guards —
require_claim(type, value)andrequire_scope(scope)factories that plug intoDepends(...)to enforce RBAC/ABAC at the route level - Token refresh helper — Proactive refresh of expiring access tokens for service-to-service flows
from fastapi import Depends, FastAPI
from .middleware import TokenValidationMiddleware
from .dependencies import CurrentUser, require_scope
app = FastAPI()
app.add_middleware(
TokenValidationMiddleware,
discovery_url="https://issuer.example.com/.well-known/openid-configuration",
audience="my-api",
excluded_paths=["/health", "/docs", "/openapi.json"],
)
@app.get("/me")
async def me(user: CurrentUser) -> dict:
return {"sub": user.identity.name}
@app.get("/admin", dependencies=[Depends(require_scope("api.admin"))])
async def admin() -> dict:
return {"status": "ok"}
| Integration | Path | Highlights |
|---|---|---|
| FastAPI | examples/fastapi/ |
Middleware, DI, authorization guards, token refresh |
| Descope (FastAPI) | examples/descope/ |
Extends the FastAPI base with Descope-specific issuer/tenant handling |
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
HTTP_TIMEOUT |
30.0 | Request timeout (seconds) |
HTTP_RETRY_COUNT |
3 | Retries for rate-limited requests |
HTTP_RETRY_BASE_DELAY |
1.0 | Base delay for exponential backoff |
SSL_CERT_FILE |
— | CA bundle path (recommended) |
REQUESTS_CA_BUNDLE |
— | CA bundle path (legacy compat) |
Documentation
Full docs: jamescrowley321.github.io/py-identity-model
License
Apache License 2.0
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 py_identity_model-2.19.2.tar.gz.
File metadata
- Download URL: py_identity_model-2.19.2.tar.gz
- Upload date:
- Size: 81.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","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 |
8a6a132c1557675b29c24efd56436fa2473d2b8b05073240f963f2f0f385ad15
|
|
| MD5 |
df861fe6b65b42b8a89cbf1a68e5c013
|
|
| BLAKE2b-256 |
04061846fdafe3beb9ae7946f0c82a7403d65ff8305bca1c5164455cbece3f14
|
File details
Details for the file py_identity_model-2.19.2-py3-none-any.whl.
File metadata
- Download URL: py_identity_model-2.19.2-py3-none-any.whl
- Upload date:
- Size: 115.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","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 |
e0b82e727a4bb02bc6be27337441129d75c3e45d4c994e36408ee0e0b6ad1f8b
|
|
| MD5 |
eb60e6377e44f71b7b669d957f39e121
|
|
| BLAKE2b-256 |
166cb9069f3e154cb53381440cb77534e641371d5012e1ea181751b43d9bc38e
|