diff --git a/app/routers/auth.py b/app/routers/auth.py index 350b9bb..4691caf 100644 --- a/app/routers/auth.py +++ b/app/routers/auth.py @@ -1,14 +1,15 @@ """ Authentication routes for L2 server. -Handles login, registration, and logout. +Handles login, registration, logout, and token verification. """ import hashlib from datetime import datetime, timezone -from fastapi import APIRouter, Request, Form +from fastapi import APIRouter, Request, Form, HTTPException, Depends from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from artdag_common import render from artdag_common.middleware import wants_html @@ -17,6 +18,7 @@ from ..config import settings from ..dependencies import get_templates, get_user_from_cookie router = APIRouter() +security = HTTPBearer(auto_error=False) @router.get("/login", response_class=HTMLResponse) @@ -173,3 +175,49 @@ async def logout(request: Request): response = RedirectResponse(url="/", status_code=302) response.delete_cookie("auth_token") return response + + +@router.get("/verify") +async def verify_token( + request: Request, + credentials: HTTPAuthorizationCredentials = Depends(security), +): + """ + Verify a token is valid. + + Called by L1 servers to verify tokens during auth callback. + Returns user info if valid, 401 if not. + """ + import db + from auth import verify_token as verify_jwt, get_token_claims + + # Get token from Authorization header or query param + token = None + if credentials: + token = credentials.credentials + else: + # Try Authorization header manually (for clients that don't use Bearer format) + auth_header = request.headers.get("Authorization", "") + if auth_header.startswith("Bearer "): + token = auth_header[7:] + + if not token: + raise HTTPException(401, "No token provided") + + # Verify JWT signature and expiry + username = verify_jwt(token) + if not username: + raise HTTPException(401, "Invalid or expired token") + + # Check if token is revoked + claims = get_token_claims(token) + if claims: + token_hash = hashlib.sha256(token.encode()).hexdigest() + if await db.is_token_revoked(token_hash): + raise HTTPException(401, "Token has been revoked") + + return { + "valid": True, + "username": username, + "claims": claims, + }