Add l2_server claim to JWT tokens for L1 verification

L1 needs to know which L2 server issued the token so it can verify
the token with the correct server. Now tokens include:
- l2_server: The L2 server URL (e.g., https://artdag.rose-ash.com)
- username: Also include username for compatibility (in addition to sub)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-08 17:10:33 +00:00
parent a4c6efd154
commit 5eb525d107
2 changed files with 17 additions and 6 deletions

15
auth.py
View File

@@ -135,16 +135,27 @@ async def authenticate_user(data_dir: Path, username: str, password: str) -> Opt
)
def create_access_token(username: str) -> Token:
"""Create a JWT access token."""
def create_access_token(username: str, l2_server: str = None) -> Token:
"""Create a JWT access token.
Args:
username: The username
l2_server: The L2 server URL (e.g., https://artdag.rose-ash.com)
Required for L1 to verify tokens with the correct L2.
"""
expires = datetime.now(timezone.utc) + timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
payload = {
"sub": username,
"username": username, # Also include as username for compatibility
"exp": expires,
"iat": datetime.now(timezone.utc)
}
# Include l2_server so L1 knows which L2 to verify with
if l2_server:
payload["l2_server"] = l2_server
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
return Token(

View File

@@ -329,7 +329,7 @@ async def ui_login_submit(request: Request):
if not user:
return HTMLResponse('<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">Invalid username or password</div>')
token = create_access_token(user.username)
token = create_access_token(user.username, l2_server=f"https://{DOMAIN}")
response = HTMLResponse(f'''
<div class="bg-green-900/50 border border-green-700 text-green-300 px-4 py-3 rounded-lg mb-4">Login successful! Redirecting...</div>
@@ -413,7 +413,7 @@ async def ui_register_submit(request: Request):
except ValueError as e:
return HTMLResponse(f'<div class="bg-red-900/50 border border-red-700 text-red-300 px-4 py-3 rounded-lg mb-4">{str(e)}</div>')
token = create_access_token(user.username)
token = create_access_token(user.username, l2_server=f"https://{DOMAIN}")
response = HTMLResponse(f'''
<div class="bg-green-900/50 border border-green-700 text-green-300 px-4 py-3 rounded-lg mb-4">Registration successful! Redirecting...</div>
@@ -1129,7 +1129,7 @@ async def register(req: UserCreate):
except ValueError as e:
raise HTTPException(400, str(e))
return create_access_token(user.username)
return create_access_token(user.username, l2_server=f"https://{DOMAIN}")
@app.post("/auth/login", response_model=Token)
@@ -1139,7 +1139,7 @@ async def login(req: UserLogin):
if not user:
raise HTTPException(401, "Invalid username or password")
return create_access_token(user.username)
return create_access_token(user.username, l2_server=f"https://{DOMAIN}")
@app.get("/auth/me")