Python
Two patterns. Both are ~30 lines of code.
Pattern 1 — Sidecar (recommended)
Your FastAPI / Django / Flask app calls IdentSphere over HTTP for auth flows. The JWT cookie that IdentSphere sets is reused on subsequent requests.
Setup
pip install fastapi httpx python-jose[cryptography] uvicorn
# auth.py
import os
import httpx
from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.responses import Response
from jose import jwt, JWTError
IDENTSPHERE_URL = os.environ.get("IDENTSPHERE_URL", "http://identsphere:4000")
app = FastAPI()
# ─── proxy login through to IdentSphere ─────────────────────────────────
@app.post("/auth/login")
async def login(request: Request):
body = await request.json()
async with httpx.AsyncClient() as client:
upstream = await client.post(f"{IDENTSPHERE_URL}/v1/auth/login", json=body)
response = Response(content=upstream.content, status_code=upstream.status_code)
# forward the session cookies back to the browser
for cookie in upstream.headers.get_list("set-cookie"):
response.headers.append("set-cookie", cookie)
return response
# ─── validate JWT on protected routes ────────────────────────────
_jwks_cache = None
async def _jwks():
global _jwks_cache
if _jwks_cache is None:
async with httpx.AsyncClient() as client:
r = await client.get(f"{IDENTSPHERE_URL}/.well-known/jwks.json")
_jwks_cache = r.json()
return _jwks_cache
async def current_user(request: Request) -> dict:
token = request.cookies.get("identsphere_at")
if not token:
raise HTTPException(401, "not authenticated")
try:
jwks = await _jwks()
claims = jwt.decode(token, jwks, algorithms=["RS256"], options={"verify_aud": False})
return claims # contains sub, org, email, exp, etc.
except JWTError:
raise HTTPException(401, "invalid token")
# ─── now use it ──────────────────────────────────────────────────
@app.get("/api/me")
async def me(user: dict = Depends(current_user)):
return {"user_id": user["sub"], "org_id": user["org"], "email": user.get("email")}
That's it. Your Python app handles its own business logic; IdentSphere handles register / login / refresh / MFA / passkey / OAuth.
Pattern 2 — JWT validation only
If you don't even need to proxy login, just expose IdentSphere's auth endpoints directly to the browser (different subdomain / path) and have your Python backend ONLY validate tokens.
# minimal validate-only setup
import os, httpx
from jose import jwt, JWTError
from fastapi import FastAPI, Request, HTTPException, Depends
IDENTSPHERE_URL = os.environ["IDENTSPHERE_URL"]
app = FastAPI()
_jwks = None
async def get_jwks():
global _jwks
if _jwks is None:
r = httpx.get(f"{IDENTSPHERE_URL}/.well-known/jwks.json")
_jwks = r.json()
return _jwks
async def auth(request: Request):
token = request.cookies.get("identsphere_at") or request.headers.get("authorization", "").removeprefix("Bearer ")
if not token:
raise HTTPException(401)
try:
return jwt.decode(token, await get_jwks(), algorithms=["RS256"], options={"verify_aud": False})
except JWTError:
raise HTTPException(401)
@app.get("/api/me")
async def me(claims: dict = Depends(auth)):
return {"user_id": claims["sub"], "org_id": claims["org"]}
Calling IdentSphere-managed resources from Python
Need to fetch user profile, change a password, list sessions? Just call IdentSphere's REST API, forwarding the cookies:
async def fetch_profile(request: Request) -> dict:
cookies = {k: v for k, v in request.cookies.items() if k.startswith("identsphere_")}
async with httpx.AsyncClient() as client:
r = await client.get(f"{IDENTSPHERE_URL}/v1/users/me", cookies=cookies)
return r.json()
Every endpoint documented in the API reference works this way.
Django
Same pattern. Wrap in a DjangoView and use Django's session middleware
to forward cookies, or use django-cors-headers to let the browser
talk directly to IdentSphere.
Flask
Same pattern. Use Flask's request.cookies + requests library.
Caveat: revocation lag
Pattern 2 (validate-only) has a built-in revocation lag of up to (JWKS cache TTL + access token lifetime). Default total is ~15 min. If you need tighter revocation:
- Drop JWKS cache TTL to a few minutes
- Reduce
IDENTSPHERE_ACCESS_EXPIRY_SECS(default 900) to e.g. 60 seconds - Or use Pattern 1, which checks the session table on every request
Full FastAPI starter
A complete working FastAPI starter using IdentSphere lives in the GitHub repo at
examples/fastapi-python-starter/ (coming v1.1).