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 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-11 16:06:51 +00:00
parent 889ea98e41
commit 932abb8d7a
2 changed files with 40 additions and 15 deletions

View File

@@ -31,8 +31,9 @@ def get_user_from_cookie(request: Request) -> Optional[UserContext]:
""" """
Extract user context from session cookie. Extract user context from session cookie.
The cookie format is expected to be base64-encoded JSON: Supports two cookie formats:
{"username": "user", "actor_id": "@user@server.com"} 1. artdag_session: base64-encoded JSON {"username": "user", "actor_id": "@user@server.com"}
2. auth_token: raw JWT token (used by L1 servers)
Args: Args:
request: FastAPI request request: FastAPI request
@@ -40,19 +41,38 @@ def get_user_from_cookie(request: Request) -> Optional[UserContext]:
Returns: Returns:
UserContext if valid cookie found, None otherwise UserContext if valid cookie found, None otherwise
""" """
# Try artdag_session cookie first (base64-encoded JSON)
cookie = request.cookies.get("artdag_session") cookie = request.cookies.get("artdag_session")
if not cookie: if cookie:
return None 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: # Try auth_token cookie (raw JWT, used by L1)
# Decode base64 cookie token = request.cookies.get("auth_token")
data = json.loads(base64.b64decode(cookie)) if token:
return UserContext( claims = decode_jwt_claims(token)
username=data.get("username", ""), if claims:
actor_id=data.get("actor_id", ""), username = claims.get("username") or claims.get("sub", "")
) actor_id = claims.get("actor_id") or claims.get("actor")
except (json.JSONDecodeError, ValueError, KeyError): if not actor_id and username:
return None 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]: 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 # Attempt to decode JWT claims
claims = decode_jwt_claims(token) claims = decode_jwt_claims(token)
if claims: 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( return UserContext(
username=claims.get("username", ""), username=username,
actor_id=claims.get("actor_id", ""), actor_id=actor_id or "",
token=token, token=token,
) )