Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24, Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login, self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the blog series, safe markdown links in the frontends, and many smaller bugs — every project was built, run and exercised end to end. Adds scripts/publish.sh + a CI publish job that splits every folder into its own repo at git.devai.io/templates/<folder>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
30 lines
1 KiB
Python
30 lines
1 KiB
Python
import os
|
|
|
|
import jwt
|
|
from fastapi import HTTPException, Request
|
|
|
|
ISSUER = f"https://{os.environ['AUTH0_DOMAIN']}/" # Auth0 issuers end with a slash
|
|
AUDIENCE = os.environ["AUTH0_AUDIENCE"]
|
|
# Caches the key set; an unknown key id (a rotation) triggers a refetch, at most every 30s.
|
|
_jwks = jwt.PyJWKClient(f"{ISSUER}.well-known/jwks.json")
|
|
|
|
|
|
def current_user_id(request: Request) -> str:
|
|
"""FastAPI dependency: the Auth0 user id (`sub`) from a valid access token."""
|
|
header = request.headers.get("authorization", "")
|
|
if not header.lower().startswith("bearer "):
|
|
raise HTTPException(401, "missing bearer token")
|
|
token = header[7:]
|
|
try:
|
|
key = _jwks.get_signing_key_from_jwt(token).key
|
|
claims = jwt.decode(
|
|
token,
|
|
key,
|
|
algorithms=["RS256"],
|
|
audience=AUDIENCE,
|
|
issuer=ISSUER,
|
|
options={"require": ["exp", "iss", "aud", "sub"]},
|
|
)
|
|
except jwt.PyJWTError:
|
|
raise HTTPException(401, "invalid or expired token")
|
|
return claims["sub"]
|