Files
mono/l2/app/dependencies.py
giles b788f1f778
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 13m49s
Fix CPU HLS streaming (yuv420p) and opt-in middleware for fragments
- Add -pix_fmt yuv420p to multi_res_output.py libx264 path so browsers
  can decode CPU-encoded segments (was producing yuv444p / High 4:4:4).
- Switch silent auth check and coop fragment middlewares from opt-out
  blocklists to opt-in: only run for GET requests with Accept: text/html.
  Prevents unnecessary nav-tree/auth-menu HTTP calls on every HLS segment,
  IPFS proxy, and API request.
- Add opaque grant token verification to L1/L2 dependencies.
- Migrate client CLI to device authorization flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:33:53 +00:00

107 lines
2.7 KiB
Python

"""
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)