auth fixes

This commit is contained in:
2026-05-16 14:38:10 +00:00
parent 84038c25ec
commit a48d80d160
12 changed files with 224 additions and 56 deletions
+25 -11
View File
@@ -12,21 +12,25 @@ bearer_scheme = HTTPBearer(auto_error=False)
async def _get_or_create_dev_user(session: AsyncSession) -> User:
"""Return or create the fixed development user."""
result = await session.execute(
select(User).where(User.authentik_sub == "dev-user")
)
user = result.scalar_one_or_none()
if user is None:
user = User(
authentik_sub="dev-user",
email="dev@localhost",
display_name="Dev User",
is_active=True,
result = await session.execute(
select(User).where(User.email == "dev@localhost")
)
session.add(user)
await session.commit()
await session.refresh(user)
user = result.scalar_one_or_none()
if user is None:
user = User(
authentik_sub="dev-user",
email="dev@localhost",
display_name="Dev User",
is_active=True,
)
session.add(user)
await session.commit()
await session.refresh(user)
return user
@@ -44,7 +48,7 @@ async def get_current_user(
)
try:
claims = decode_token(token.credentials)
claims = await decode_token(token.credentials)
except Exception as exc:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@@ -69,6 +73,16 @@ async def get_current_user(
user = result.scalar_one_or_none()
if user is None:
result = await session.execute(
select(User).where(User.email == email)
)
existing_user = result.scalar_one_or_none()
if existing_user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"User with email {email} already exists",
)
user = User(
authentik_sub=authentik_sub,
email=email,
@@ -105,7 +119,7 @@ async def validate_traefik_auth(
)
try:
claims = decode_token(token.credentials)
claims = await decode_token(token.credentials)
except Exception as exc:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
+24 -19
View File
@@ -1,31 +1,17 @@
from typing import Any
import httpx
import jwt
from app.config import settings
_jwks_cache: dict[str, Any] | None = None
def decode_token(token: str) -> dict[str, Any]:
"""Decode a JWT token.
When authentik_issuer_url is configured, validates the token
against the OIDC discovery document JWKS.
Otherwise, decodes without verification (local development only).
"""
async def decode_token(token: str) -> dict[str, Any]:
if settings.authentik_issuer_url:
import httpx
issuer = settings.authentik_issuer_url.rstrip("/")
discovery_url = f"{issuer}/.well-known/openid-configuration"
with httpx.Client() as client:
resp = client.get(discovery_url)
resp.raise_for_status()
discovery = resp.json()
jwks_uri = discovery["jwks_uri"]
jwks_resp = client.get(jwks_uri)
jwks_resp.raise_for_status()
jwks = jwks_resp.json()
jwks = await _get_jwks(issuer)
signing_key = jwt.algorithms.RSAAlgorithm.from_jwk(
_find_matching_key(jwks, token)
@@ -42,8 +28,27 @@ def decode_token(token: str) -> dict[str, Any]:
return jwt.decode(token, options={"verify_signature": False})
async def _get_jwks(issuer: str) -> dict[str, Any]:
global _jwks_cache
if _jwks_cache is not None:
return _jwks_cache
discovery_url = f"{issuer}/.well-known/openid-configuration"
async with httpx.AsyncClient() as client:
resp = await client.get(discovery_url)
resp.raise_for_status()
discovery = resp.json()
jwks_uri = discovery["jwks_uri"]
jwks_resp = await client.get(jwks_uri)
jwks_resp.raise_for_status()
_jwks_cache = jwks_resp.json()
return _jwks_cache
def _find_matching_key(jwks: dict[str, Any], token: str) -> dict[str, Any]:
"""Find the key in JWKS that matches the token's kid header."""
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get("kid")
for key in jwks.get("keys", []):
+1 -1
View File
@@ -21,7 +21,7 @@ class Settings(BaseSettings):
database_url: str = "postgresql://postgres:postgres@localhost:5432/headquarter"
# CORS
cors_origins: str = "http://localhost:5173"
cors_origins: str = "http://localhost:5173,http://localhost:3000"
# Deployment
root_domain: str = "localhost"
+1 -1
View File
@@ -32,7 +32,7 @@ app = FastAPI(
lifespan=lifespan,
)
allow_origins = ["*"] if settings.debug else []
allow_origins = settings.cors_origins.split(",") if settings.cors_origins else []
app.add_middleware(
CORSMiddleware,
allow_origins=allow_origins,