FastAPI OIDC/OAuth2 middleware and relying-party router, built on py-identity-model
Project description
fastapi-identity-model
OIDC/OAuth2 integration for FastAPI, built on
py-identity-model — an OpenID
Foundation–certified relying-party library.
It gives you two composable pieces:
| Piece | Use case |
|---|---|
TokenValidationMiddleware + Depends helpers |
Protect an API / resource server that receives Authorization: Bearer <token> |
build_oidc_router |
Add a browser login flow (authorization code + PKCE) to your app (relying party) |
Install
pip install fastapi-identity-model
# with a server for the demo:
pip install "fastapi-identity-model[server]"
Resource server — validate incoming Bearer tokens
from fastapi import FastAPI, Depends
from fastapi_identity_model import (
TokenValidationMiddleware, OIDCSettings, get_current_user, require_scope,
)
settings = OIDCSettings.from_env() # OIDC_DISCOVERY_URL, OIDC_CLIENT_ID, OIDC_REDIRECT_URI, ...
app = FastAPI()
app.add_middleware(
TokenValidationMiddleware,
discovery_url=settings.discovery_url,
audience=settings.audience,
excluded_paths=settings.excluded_paths,
)
@app.get("/api/me")
async def me(user = Depends(get_current_user)):
return {"sub": user.identity.name, "authenticated": user.identity.is_authenticated}
@app.get("/api/data", dependencies=[Depends(require_scope("api.read"))])
async def data():
return {"data": "protected"}
The middleware validates the token (signature, issuer, audience, expiry) via
py-identity-model and attaches a ClaimsPrincipal to request.state.user.
Invalid tokens → 401; an unexpected server-side failure → 500 (never
masked as a 401).
Relying party — browser login flow
import os
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware
from fastapi_identity_model import OIDCSettings, build_oidc_router
settings = OIDCSettings(
discovery_url="https://api.descope.com/v1/apps/<project_id>/.well-known/openid-configuration",
client_id="<client_id>",
redirect_uri="http://localhost:8000/auth/callback",
scope="openid profile email",
)
app = FastAPI()
# Use a strong secret from the environment — never a committed literal, or
# anyone can forge a session cookie. same_site="lax" is required so the
# provider's redirect back to /auth/callback carries the session cookie.
app.add_middleware(
SessionMiddleware,
secret_key=os.environ["SESSION_SECRET"],
same_site="lax",
https_only=True, # behind TLS
)
app.include_router(build_oidc_router(settings), prefix="/auth")
@app.get("/me")
async def me(request: Request):
return request.session.get("oidc", {}) # {"sub", "claims", "userinfo"} after login
Routes added: GET /auth/login → provider, GET /auth/callback (code exchange,
ID-token validation, nonce check, UserInfo sub verification), POST /auth/logout.
Session & security
The router uses Starlette's SessionMiddleware, which signs but does not
encrypt the cookie. Stored identity claims are tamper-proof but readable by the
client. Raw tokens are stored only when you pass build_oidc_router(settings, store_tokens=True) — enable that only with an encrypted or server-side session
store. For production, back the session with a server-side store.
Token refresh
from fastapi_identity_model import TokenManager
tm = TokenManager(discovery_url=..., client_id=..., client_secret=...)
tm.set_tokens(access_token=..., refresh_token=..., expires_in=3600)
access = await tm.get_access_token() # auto-refreshes shortly before expiry
License
Apache-2.0 — see the repository LICENSE.
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 fastapi_identity_model-0.1.0.tar.gz.
File metadata
- Download URL: fastapi_identity_model-0.1.0.tar.gz
- Upload date:
- Size: 21.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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 |
6ad35d54bc2a0940008b069ac7e92638ac78126af4ca77ecb31c7195575b9fe1
|
|
| MD5 |
27ada9af5a7f3db7be8f19e0a058fa2f
|
|
| BLAKE2b-256 |
3e7ed15031d7123076341824c91e7a610ed2577b2cdcce46302451bbccfb6953
|
File details
Details for the file fastapi_identity_model-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_identity_model-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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 |
03a3f0bfbec7accce48bc593d4371367fbe6f3aa68471e15c77c8356b241345a
|
|
| MD5 |
75221a9c59b8eb4a038d6d1f636bc0c6
|
|
| BLAKE2b-256 |
4894806116e45a98ba8cb9600dfe3b2dc142e3c5cb2d69f7d3eeb12825af8e5d
|