feat: simplify auth flow - replace JWT with session cookies
Replace complex JWT + refresh token authentication with simple session-based auth using signed cookies. **Removed:** - JWT token service (jwt_service.py) - Refresh token store (refresh_store.py) - Refresh token model and database table - JWKS fetching and OIDC token verification - python-jose dependency **Added:** - Session service (session.py) with HMAC-SHA256 signed cookies - Auth dependencies module for shared auth logic - Session-based auth endpoints **Updated:** - All API endpoints to use session-based auth - Config: removed JWT settings, added SESSION_SECRET/SESSION_TTL_HOURS - Tests: rewritten for session-based flow - Frontend: no changes needed (already uses cookies) Quality gates: ruff ✓, mypy ✓, typecheck ✓, lint ✓
This commit is contained in:
+11
-24
@@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import httpx
|
||||
from jose import jwt # type: ignore[import-untyped]
|
||||
|
||||
from src.config import Settings
|
||||
|
||||
@@ -11,7 +11,6 @@ def build_login_redirect_url(
|
||||
settings: Settings,
|
||||
redirect_uri: str,
|
||||
state: str,
|
||||
nonce: str,
|
||||
) -> str:
|
||||
query = urlencode(
|
||||
{
|
||||
@@ -20,7 +19,6 @@ def build_login_redirect_url(
|
||||
"redirect_uri": redirect_uri,
|
||||
"scope": "openid profile email",
|
||||
"state": state,
|
||||
"nonce": nonce,
|
||||
}
|
||||
)
|
||||
return f"{settings.resolved_authentik_authorize_url}?{query}"
|
||||
@@ -51,27 +49,16 @@ async def exchange_code_for_tokens(
|
||||
}
|
||||
|
||||
|
||||
async def fetch_jwks(*, settings: Settings, client: httpx.AsyncClient) -> dict[str, list[dict[str, str]]]:
|
||||
response = await client.get(settings.resolved_authentik_jwks_url)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
return {"keys": payload["keys"]}
|
||||
|
||||
|
||||
def verify_provider_access_token(
|
||||
async def fetch_user_info(
|
||||
*,
|
||||
settings: Settings,
|
||||
token: str,
|
||||
jwks: dict[str, list[dict[str, str]]],
|
||||
) -> dict[str, str | int]:
|
||||
unverified_header = jwt.get_unverified_header(token)
|
||||
key_id = unverified_header["kid"]
|
||||
jwk_key = next(key for key in jwks["keys"] if key.get("kid") == key_id)
|
||||
claims = jwt.decode(
|
||||
token,
|
||||
jwk_key,
|
||||
algorithms=[jwk_key.get("alg", "HS256")],
|
||||
audience=settings.authentik_audience,
|
||||
issuer=settings.resolved_authentik_issuer,
|
||||
access_token: str,
|
||||
client: httpx.AsyncClient,
|
||||
) -> dict[str, Any]:
|
||||
"""Fetch user info from Authentik userinfo endpoint."""
|
||||
response = await client.get(
|
||||
f"{settings.authentik_base_url}/application/o/userinfo/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
)
|
||||
return dict(claims)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
Reference in New Issue
Block a user