From 932abb8d7a1ac6d9a363aa4b879dab774e82d252 Mon Sep 17 00:00:00 2001 From: gilesb Date: Sun, 11 Jan 2026 16:06:51 +0000 Subject: [PATCH] Fix auth to handle JWT tokens without actor_id - Default actor_id to @username when not in token claims - Support both artdag_session (base64 JSON) and auth_token (JWT) cookies - Check both 'username' and 'sub' claims for username - Check both 'actor_id' and 'actor' claims for actor_id This fixes authentication when L2 tokens don't include actor_id. Co-Authored-By: Claude Opus 4.5 --- .../__pycache__/auth.cpython-310.pyc | Bin 0 -> 6751 bytes artdag_common/middleware/auth.py | 55 +++++++++++++----- 2 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 artdag_common/middleware/__pycache__/auth.cpython-310.pyc diff --git a/artdag_common/middleware/__pycache__/auth.cpython-310.pyc b/artdag_common/middleware/__pycache__/auth.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a262852ded9cf12140f6429bd9d9817d96a8657 GIT binary patch literal 6751 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 literal 0 HcmV?d00001 diff --git a/artdag_common/middleware/auth.py b/artdag_common/middleware/auth.py index 482e80a..f9d97b1 100644 --- a/artdag_common/middleware/auth.py +++ b/artdag_common/middleware/auth.py @@ -31,8 +31,9 @@ def get_user_from_cookie(request: Request) -> Optional[UserContext]: """ Extract user context from session cookie. - The cookie format is expected to be base64-encoded JSON: - {"username": "user", "actor_id": "@user@server.com"} + Supports two cookie formats: + 1. artdag_session: base64-encoded JSON {"username": "user", "actor_id": "@user@server.com"} + 2. auth_token: raw JWT token (used by L1 servers) Args: request: FastAPI request @@ -40,19 +41,38 @@ def get_user_from_cookie(request: Request) -> Optional[UserContext]: Returns: UserContext if valid cookie found, None otherwise """ + # Try artdag_session cookie first (base64-encoded JSON) cookie = request.cookies.get("artdag_session") - if not cookie: - return None + if cookie: + try: + data = json.loads(base64.b64decode(cookie)) + username = data.get("username", "") + actor_id = data.get("actor_id", "") + if not actor_id and username: + actor_id = f"@{username}" + return UserContext( + username=username, + actor_id=actor_id, + ) + except (json.JSONDecodeError, ValueError, KeyError): + pass - try: - # Decode base64 cookie - data = json.loads(base64.b64decode(cookie)) - return UserContext( - username=data.get("username", ""), - actor_id=data.get("actor_id", ""), - ) - except (json.JSONDecodeError, ValueError, KeyError): - return None + # Try auth_token cookie (raw JWT, used by L1) + token = request.cookies.get("auth_token") + if token: + claims = decode_jwt_claims(token) + if claims: + username = claims.get("username") or claims.get("sub", "") + actor_id = claims.get("actor_id") or claims.get("actor") + if not actor_id and username: + actor_id = f"@{username}" + return UserContext( + username=username, + actor_id=actor_id or "", + token=token, + ) + + return None def get_user_from_header(request: Request) -> Optional[UserContext]: @@ -76,9 +96,14 @@ def get_user_from_header(request: Request) -> Optional[UserContext]: # Attempt to decode JWT claims claims = decode_jwt_claims(token) if claims: + username = claims.get("username") or claims.get("sub", "") + actor_id = claims.get("actor_id") or claims.get("actor") + # Default actor_id to @username if not provided + if not actor_id and username: + actor_id = f"@{username}" return UserContext( - username=claims.get("username", ""), - actor_id=claims.get("actor_id", ""), + username=username, + actor_id=actor_id or "", token=token, )