""" L2 Server Dependency Injection. Provides common dependencies for routes. """ from typing import Optional from fastapi import Request, HTTPException, Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from .config import settings security = HTTPBearer(auto_error=False) def get_templates(request: Request): """Get Jinja2 templates from app state.""" return request.app.state.templates async def _verify_opaque_grant(token: str) -> Optional[dict]: """Verify an opaque grant token via account server.""" import httpx if not settings.internal_account_url: return None verify_url = f"{settings.internal_account_url.rstrip('/')}/auth/internal/verify-grant" try: async with httpx.AsyncClient(timeout=5.0) as client: resp = await client.get(verify_url, params={"token": token}) if resp.status_code != 200: return None data = resp.json() if not data.get("valid"): return None except Exception: return None username = data.get("username", "") return { "username": username, "actor_id": f"https://{settings.domain}/users/{username}", "token": token, "sub": username, } async def get_current_user(request: Request) -> Optional[dict]: """ Get current user from cookie or header. Returns user dict or None if not authenticated. """ from auth import verify_token, get_token_claims # Try cookie first token = request.cookies.get("auth_token") # Try Authorization header if not token: auth_header = request.headers.get("Authorization", "") if auth_header.startswith("Bearer "): token = auth_header[7:] if not token: return None # Verify JWT token username = verify_token(token) if username: claims = get_token_claims(token) if claims: return { "username": username, "actor_id": f"https://{settings.domain}/users/{username}", "token": token, **claims, } # JWT failed — try as opaque grant token return await _verify_opaque_grant(token) async def require_auth(request: Request) -> dict: """ Require authentication. Raises HTTPException 401 if not authenticated. """ user = await get_current_user(request) if not user: raise HTTPException(401, "Authentication required") return user def get_user_from_cookie(request: Request) -> Optional[str]: """Get username from cookie (for HTML pages).""" from auth import verify_token token = request.cookies.get("auth_token") if not token: return None return verify_token(token)