caselaw-auth-server
Server-side OIDC for Python products, interchangeable with the
caselaw-auth npm package's
/server entry point.
A session cookie sealed by either implementation unseals in the other, byte for
byte. Both are written against packages/contract/contract.json, and both load
packages/contract/conformance/cases.json at test time — which is what keeps
them from drifting.
pip install caselaw-auth-server
Why
caselaw-auth/server is TypeScript, and half this estate is not: caselaw-access
is FastAPI and citations-api is a Python HTTP server. Without a Python
implementation, those two would have stayed on browser-side sessions purely
because of the language their backend happens to be written in — with the
refresh token in localStorage, where page script can read it.
Usage
from caselaw_auth_server import create_server_auth, create_pkce_pair, random_token
auth = create_server_auth(
issuer=os.environ["AUTH_ISSUER"], # .../realms/caselaw
client_id=os.environ["AUTH_CLIENT_ID"],
redirect_uri=os.environ["AUTH_REDIRECT_URI"],
session_secret=os.environ["AUTH_SESSION_SECRET"],
client_secret=os.environ.get("AUTH_CLIENT_SECRET"), # confidential clients
)
Start a sign-in:
verifier, challenge = create_pkce_pair()
state = random_token()
# state and verifier must survive the round trip and stay unreadable by page
# script — a short httpOnly cookie, not anything the browser can see.
url = auth.authorization_url(state=state, code_challenge=challenge)
Complete it:
tokens = auth.exchange_code(code=code, code_verifier=verifier)
claims = auth.verify_token(tokens["access_token"])
session = auth.session_from_claims(claims, id_token=tokens.get("id_token"))
cookie = auth.seal_session(session)
Read it on any request — an HMAC check, no network call:
session = auth.unseal_session(request.cookies.get("caselaw_session"))
if not session or "admin" not in session["roles"]:
raise Forbidden()
End it. id_token_hint is what makes this a real single sign-out:
auth.end_session_url(id_token=session.get("idToken"), return_to=site)
Mapping to the TypeScript package
Method names are idiomatic per language. Session keys are camelCase in both, because they are wire format rather than API surface: renaming them per language would break the property the whole contract exists to guarantee.
| TypeScript | Python |
|---|---|
createServerAuth(config) |
create_server_auth(...) |
authorizationUrl(options) |
authorization_url(...) |
exchangeCode(options) |
exchange_code(...) |
verifyToken(token, options) |
verify_token(token, ...) |
rolesFromClaims(claims) |
roles_from_claims(claims) |
sessionFromClaims(claims, options) |
session_from_claims(claims, ...) |
sealSession(session, name) |
seal_session(session, name) |
unsealSession(value) |
unseal_session(value) |
cookieOptions(maxAge, options) |
cookie_options(max_age, ...) |
endSessionUrl(options) |
end_session_url(...) |
createPkcePair() |
create_pkce_pair() |
Errors are typed here rather than string-matched: InvalidToken,
AuthBackendUnavailable and SessionTooLarge, all under AuthError. The split
between the first two is deliberate — collapsing them sends users to
re-authenticate during an outage, which cannot help and doubles the load on the
thing already failing.
Behaviour worth knowing
session_from_claimsdoes not keep the access token. Where one is genuinely needed, put it in a cookie of its own; a token in a cookie leaks with the cookie.- The session carries its own TTL, not the token's few minutes.
seal_sessionraises above 4096 bytes rather than returning a cookie the browser will silently discard.verify_tokendoes not checkaudby default. Keycloak putsaccountthere for a public client's access token and names the client inazp.- JWKS is cached and retried once; a token that is itself invalid is never retried.
Tests
pip install -e ".[dev]"
pytest
The suite loads the shared conformance cases by relative path. Running it from a checkout of this repository is therefore required — the cases are not vendored into the package.
Related
- caselaw-auth — the realm, themes, and both implementations
- docs/SERVER_SIDE_AUTH.md — the full walkthrough
Metadata
Release files for caselaw-auth-server 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| caselaw_auth_server-0.3.0.tar.gz | 11.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| caselaw_auth_server-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.0 kB
Release files / caselaw_auth_server-0.3.0.tar.gz
| Download URL | caselaw_auth_server-0.3.0.tar.gz |
|---|---|
| Size | 11.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1ef2e812452c1e746b4c75157e199c4a4680b53cb29cef739ccde0cc697452b
|
|
BLAKE2b-256 checksum How to use checksums |
c59fd0b273320428ea5d3df393b3bae78055d61e9498c2f3652efc6b42c517c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logRelease files / caselaw_auth_server-0.3.0-py3-none-any.whl
| Download URL | caselaw_auth_server-0.3.0-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d796e36faf1ddb797e8800002208ba6a9c18839afc7b84a52d2770109cf3e487
|
|
BLAKE2b-256 checksum How to use checksums |
6abb224b809ec8b661ac520ae04a042c042a027123b1de033465442667c66da1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency log