Implement metadata for RestrictedForward, set by /auth/api/forward endpoint when returning the app. Use this to implement support for time-based reauth requirement.

This commit is contained in:
Leo Vasanko
2025-12-02 23:39:31 +00:00
parent aed48de38e
commit 10ce0126b0
8 changed files with 220 additions and 32 deletions
+54 -7
View File
@@ -2,31 +2,76 @@ import logging
from fastapi import HTTPException
from ..util import permutil
from ..util import permutil, sessionutil
logger = logging.getLogger(__name__)
class AuthException(HTTPException):
"""Exception raised during authentication/authorization with metadata for the UI.
Attributes:
status_code: HTTP status code (401 for auth, 403 for authz)
detail: Error message
mode: UI mode ('login' or 'reauth')
clear_session: Whether to clear the session cookie (True for invalid sessions)
metadata: Additional data to pass to the frontend
"""
def __init__(
self,
status_code: int,
detail: str,
mode: str,
clear_session: bool = False,
**metadata,
):
super().__init__(status_code=status_code, detail=detail)
self.mode = mode
self.clear_session = clear_session
self.metadata = metadata
async def verify(
auth: str | None,
perm: list[str],
match=permutil.has_all,
host: str | None = None,
max_age: str | None = None,
):
"""Validate session token and optional list of required permissions.
Returns the session context.
Raises HTTPException on failure:
401: unauthenticated / invalid session
403: required permissions missing
Raises AuthException on failure with metadata for UI rendering.
"""
if not auth:
raise HTTPException(status_code=401, detail="Authentication required")
raise AuthException(
status_code=401,
detail="Authentication required",
mode="login",
)
ctx = await permutil.session_context(auth, host)
if not ctx:
raise HTTPException(status_code=401, detail="Session not found")
raise AuthException(
status_code=401,
detail="Your session has expired. Please sign in again.",
mode="login",
clear_session=True,
)
# Check max_age requirement if specified
if max_age:
try:
if not sessionutil.check_session_age(ctx.session, max_age):
raise AuthException(
status_code=401,
detail="Additional authentication required",
mode="reauth",
)
except ValueError as e:
# Invalid max_age format - log but don't fail the request
logger.warning(f"Invalid max_age format '{max_age}': {e}")
if not match(ctx, perm):
# Determine which permissions are missing for clearer diagnostics
@@ -39,6 +84,8 @@ async def verify(
perm,
ctx.role.permissions,
)
raise HTTPException(status_code=403, detail="Permission required")
raise AuthException(
status_code=403, mode="forbidden", detail="Permission required"
)
return ctx