"""DRF authentication class backed by :class:`mcp_server.models.UserToken`. Wraps :func:`mcp_server.auth.resolve_mcp_user` so a single verification routine serves both surfaces: * the FastMCP middleware on ``/mcp/`` (via ``MCPAuthMiddleware``); and * the Django REST surface on ``/library/api/*`` and ``/mcp_server/api/teams/*`` (via this class). Scope: this class authenticates the caller — it does *not* honour the token's ``allowed_libraries`` / ``allowed_tools`` fields. Those apply only to the MCP tool surface. On the REST endpoints, access is gated by ``Team.owner`` and ``Library.owner_username`` rather than per-token scope claims; treating ``allowed_libraries`` as authoritative there would either force Daedalus to mint an effectively-unrestricted token (redundant with the user identity) or invent a per-endpoint scope mapping with no natural shape. The accepted header is ``Authorization: Bearer ``. """ from __future__ import annotations from rest_framework import authentication, exceptions from .auth import MCPAuthError, resolve_mcp_user class UserTokenAuthentication(authentication.BaseAuthentication): """Authenticate DRF requests with a ``UserToken`` bearer.""" keyword = "Bearer" def authenticate(self, request): header = authentication.get_authorization_header(request).decode("iso-8859-1") if not header: return None parts = header.split() if len(parts) < 2 or parts[0] != self.keyword: # Not our scheme. Let other authenticators try. return None if len(parts) > 2: raise exceptions.AuthenticationFailed( "Invalid Authorization header: too many components." ) try: user, token = resolve_mcp_user(parts[1]) except MCPAuthError as exc: raise exceptions.AuthenticationFailed(str(exc)) return user, token def authenticate_header(self, request): return self.keyword