File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1818from apps .users .domain import User
1919from apps .users .password_validation import PasswordValidator
2020from config import settings
21+ from infrastructure .http .domain import MindloggerContentSource
2122
2223__all__ = ["AuthenticationService" ]
2324
25+ _WEB_ADMIN_CLIENTS = (MindloggerContentSource .web , MindloggerContentSource .admin )
26+
2427
2528class AuthenticationService :
2629 def __init__ (self , session ) -> None :
2730 self .session = session
2831
32+ @staticmethod
33+ def token_expiration_minutes (
34+ client : MindloggerContentSource | None ,
35+ default_minutes : int ,
36+ web_admin_minutes : int | None ,
37+ ) -> int :
38+ """Resolve a token lifetime for a client.
39+
40+ Web/admin clients get ``web_admin_minutes`` when it is configured;
41+ everything else (mobile, or an unknown/legacy client with no `client`
42+ claim) keeps ``default_minutes``. An unset ``web_admin_minutes`` means
43+ the per-client shortening is off, so all clients fall back to the
44+ default — this is what keeps the feature a no-op until ops opts in.
45+ """
46+ if client in _WEB_ADMIN_CLIENTS and web_admin_minutes is not None :
47+ return web_admin_minutes
48+ return default_minutes
49+
2950 @staticmethod
3051 def create_access_token (data : dict ) -> str :
3152 to_encode = data .copy ()
Original file line number Diff line number Diff line change 1+ import pytest
2+
3+ from apps .authentication .services .security import AuthenticationService
4+ from infrastructure .http .domain import MindloggerContentSource
5+
6+ DEFAULT = 30
7+ WEB_ADMIN = 15
8+
9+
10+ @pytest .mark .parametrize (
11+ "client" ,
12+ (MindloggerContentSource .web , MindloggerContentSource .admin ),
13+ )
14+ def test_web_admin_use_short_lifetime_when_configured (client : MindloggerContentSource ):
15+ assert AuthenticationService .token_expiration_minutes (client , DEFAULT , WEB_ADMIN ) == WEB_ADMIN
16+
17+
18+ @pytest .mark .parametrize (
19+ "client" ,
20+ (MindloggerContentSource .web , MindloggerContentSource .admin ),
21+ )
22+ def test_web_admin_fall_back_to_default_when_unset (client : MindloggerContentSource ):
23+ assert AuthenticationService .token_expiration_minutes (client , DEFAULT , None ) == DEFAULT
24+
25+
26+ @pytest .mark .parametrize ("web_admin_minutes" , (WEB_ADMIN , None ))
27+ @pytest .mark .parametrize ("client" , (MindloggerContentSource .mobile , None ))
28+ def test_mobile_and_unknown_always_use_default (client , web_admin_minutes ):
29+ assert AuthenticationService .token_expiration_minutes (client , DEFAULT , web_admin_minutes ) == DEFAULT
Original file line number Diff line number Diff line change @@ -7,6 +7,9 @@ class AccessTokenSettings(BaseModel):
77 secret_key : str
88 # Set in minutes
99 expiration : int = 30
10+ # Shorter lifetime (minutes) for web/admin clients. None = same as `expiration`
11+ # (feature off). See AuthenticationService.token_expiration_minutes.
12+ web_admin_expiration : int | None = None
1013
1114 @field_validator ("secret_key" )
1215 @classmethod
@@ -20,6 +23,9 @@ class RefreshTokenSettings(BaseModel):
2023 secret_key : str
2124 # Set in minutes
2225 expiration : int = 540
26+ # Shorter lifetime (minutes) for web/admin clients. None = same as `expiration`
27+ # (feature off). See AuthenticationService.token_expiration_minutes.
28+ web_admin_expiration : int | None = None
2329
2430 transition_key : str | None = None
2531 transition_expire_date : datetime .date | None = None
You can’t perform that action at this time.
0 commit comments