Coverage for src/lexigram/auth/web/middleware/jwt_authenticator.py: 79%
38 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:58 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:58 +0800
1"""JWT token authentication component."""
3from __future__ import annotations
5from typing import Any
7from lexigram.contracts import AuthProviderProtocol
8from lexigram.logging import LoggerProtocol as Logger
9from lexigram.logging import get_logger
11logger = get_logger(__name__)
14class JwtAuthenticator:
15 """Handles JWT token validation and user resolution."""
17 def __init__(
18 self,
19 auth_provider: AuthProviderProtocol,
20 config: Any | None = None,
21 logger: Logger | None = None,
22 ) -> None:
23 self.auth_provider = auth_provider
24 from lexigram.auth.web.middleware.token_extractor import TokenExtractor
26 self.token_extractor = TokenExtractor(
27 config
28 or type(
29 "Config", (), {"header_name": "Authorization", "scheme": "Bearer"}
30 )()
31 )
33 if logger is None:
34 from lexigram.logging import get_logger
36 logger = get_logger(__name__)
37 self.logger = logger.bind(middleware="JwtAuthenticator")
39 async def authenticate(self, request: Any) -> Any | None:
40 """Validate JWT token from request and return user if valid."""
41 token = self.token_extractor.extract_token(request)
42 if not token:
43 return None
45 try:
46 token_result = await self.auth_provider.verify_token(token)
47 except (ValueError, TypeError, RuntimeError) as e:
48 self.logger.exception(
49 "Unexpected error during JWT authentication",
50 exc_info=e,
51 )
52 return None
54 if token_result is None or token_result.is_err():
55 self.logger.info(
56 "JWT verification failed: %s",
57 token_result.unwrap_err() if token_result is not None else "no result",
58 )
59 return None
61 verified = token_result.unwrap()
62 user_id = verified.user_id
63 if not user_id:
64 return None
66 # Fetch fresh user data from store
67 user = await self.auth_provider.get_user(user_id)
68 if not user:
69 return None
71 # Pre-fetch delegations for sync check_access
72 if self.auth_provider.delegation_manager:
73 request.state.delegations = (
74 await self.auth_provider.delegation_manager.get_active_delegations(
75 user.user_id,
76 )
77 )
79 return user
82__all__ = [
83 "JwtAuthenticator",
84 "logger",
85]