Coverage for src / lexigram / contracts / auth / token.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-19 05:41 +0800

1"""Token management protocols. 

2 

3Protocols for JWT token creation, validation, and refresh. 

4""" 

5 

6from __future__ import annotations 

7 

8from dataclasses import dataclass 

9from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable 

10 

11if TYPE_CHECKING: 

12 from datetime import datetime 

13 

14 from lexigram.contracts.auth.exceptions import TokenError 

15 from lexigram.contracts.auth.user import AuthenticatedUserProtocol 

16 from lexigram.contracts.core.result import Result 

17 

18 

19@dataclass(frozen=True) 

20class VerifiedToken: 

21 """Decoded and validated JWT token payload. 

22 

23 Returned when token verification succeeds. All fields are extracted from 

24 the verified JWT claims so callers never need to inspect raw ``dict`` 

25 payloads. 

26 

27 Attributes: 

28 user_id: Subject claim (``sub``), identifying the token owner. 

29 email: Email address of the token owner. 

30 name: Display name of the token owner. 

31 roles: List of role identifiers granted to the user. 

32 permissions: List of permission identifiers granted to the user. 

33 expires_at: Absolute UTC datetime at which the token expires. 

34 key_id: Key ID (``kid`` header) used to sign this token. 

35 token_type: Token kind — ``"access"`` or ``"refresh"``. 

36 audience: Audience claim (``aud``), or ``None`` if absent. 

37 """ 

38 

39 user_id: str 

40 email: str 

41 name: str 

42 roles: list[str] 

43 permissions: list[str] 

44 expires_at: datetime 

45 key_id: str 

46 token_type: str # "access" or "refresh" 

47 audience: str | None = None 

48 

49 

50@runtime_checkable 

51class TokenManagerProtocol(Protocol): 

52 """Protocol for token management implementations. 

53 

54 Defines the contract for JWT token creation, verification, 

55 and refresh operations. 

56 

57 Example: 

58 ```python 

59 class JWTTokenManager: 

60 def create_token(self, user: AuthenticatedUserProtocol) -> AuthToken: 

61 payload = {"sub": user.user_id, "roles": user.roles} 

62 access = jwt.encode(payload, self._secret, algorithm="HS256") 

63 return AuthToken(access_token=access, ...) 

64 ``` 

65 """ 

66 

67 def create_token(self, user: AuthenticatedUserProtocol) -> Any: 

68 """Create an authentication token for a user. 

69 

70 Args: 

71 user: The authenticated user. 

72 

73 Returns: 

74 AuthToken containing access and refresh tokens. 

75 """ 

76 ... 

77 

78 def verify_token(self, token: str) -> Result[VerifiedToken, TokenError]: 

79 """Verify and decode a token. 

80 

81 Returns a ``Result`` rather than raising domain exceptions so that 

82 the caller can pattern-match on success vs. failure explicitly. 

83 Infrastructure errors (cache unavailable, network failure) are still 

84 raised as exceptions and must not be wrapped in ``Result``. 

85 

86 Args: 

87 token: The JWT token string. 

88 

89 Returns: 

90 ``Ok(VerifiedToken)`` if the token is valid and not revoked, 

91 or ``Err(TokenError)`` for any expected domain failure. 

92 """ 

93 ... 

94 

95 def refresh_token(self, refresh_token: str) -> Result[Any, TokenError]: 

96 """Refresh an access token using a refresh token. 

97 

98 Args: 

99 refresh_token: The refresh token string. 

100 

101 Returns: 

102 ``Ok(AuthToken)`` if the refresh token is valid, or 

103 ``Err(TokenError)`` for expected domain failures. 

104 """ 

105 ... 

106 

107 

108__all__ = [ 

109 "TokenManagerProtocol", 

110 "VerifiedToken", 

111]