11using CodeBeam . UltimateAuth . Core . Abstractions ;
22using CodeBeam . UltimateAuth . Core . Contracts ;
3+ using CodeBeam . UltimateAuth . Core . Domain ;
34
45namespace CodeBeam . UltimateAuth . Server . Flows ;
56
@@ -14,35 +15,73 @@ public SessionTouchService(ISessionStoreFactory kernelFactory)
1415
1516 // It's designed for PureOpaque sessions, which do not issue new refresh tokens on refresh.
1617 // That's why the service access store direcly: There is no security flow here, only validate and touch session.
17- public async Task < SessionRefreshResult > RefreshAsync ( SessionValidationResult validation , SessionTouchPolicy policy , SessionTouchMode sessionTouchMode , DateTimeOffset now , CancellationToken ct = default )
18+ public async Task < SessionRefreshResult > RefreshAsync ( SessionValidationResult validation , SessionTouchPolicy policy , SessionTouchMode sessionTouchMode ,
19+ DateTimeOffset now , CancellationToken ct = default )
1820 {
19- if ( ! validation . IsValid || validation . ChainId is null )
21+ if ( ! validation . IsValid || validation . SessionId is null || validation . ChainId is null )
22+ {
2023 return SessionRefreshResult . ReauthRequired ( ) ;
21-
22- if ( ! policy . TouchInterval . HasValue )
23- return SessionRefreshResult . Success ( validation . SessionId ! . Value , didTouch : false ) ;
24+ }
2425
2526 var kernel = _kernelFactory . Create ( validation . Tenant ) ;
2627
28+ bool isValid = false ;
2729 bool didTouch = false ;
2830
29- await kernel . ExecuteAsync ( async _ =>
31+ await kernel . ExecuteAsync ( async innerCt =>
3032 {
31- var chain = await kernel . GetChainAsync ( validation . ChainId . Value ) ;
33+ var chain = await kernel . GetChainAsync ( validation . ChainId . Value , innerCt ) ;
34+ var session = await kernel . GetSessionAsync ( validation . SessionId . Value , innerCt ) ;
35+
36+ if ( chain is null || session is null )
37+ return ;
3238
33- if ( chain is null || chain . IsRevoked )
39+ if ( chain . GetState ( now , policy . IdleTimeout ) != SessionState . Active || session . GetState ( now ) != SessionState . Active )
3440 return ;
3541
36- if ( now - chain . LastSeenAt < policy . TouchInterval . Value )
42+ if ( chain . ActiveSessionId != session . SessionId ||
43+ session . ChainId != chain . ChainId ||
44+ session . Tenant != validation . Tenant ||
45+ chain . Tenant != validation . Tenant ||
46+ session . UserKey != chain . UserKey )
3747 return ;
3848
39- var expectedVersion = chain . Version ;
40- var touched = chain . Touch ( now ) ;
49+ if ( validation . UserKey != session . UserKey || validation . ChainId != session . ChainId )
50+ return ;
51+
52+ isValid = true ;
53+
54+ bool shouldTouch = sessionTouchMode == SessionTouchMode . Force || ! policy . TouchInterval . HasValue || now - chain . LastSeenAt >= policy . TouchInterval . Value ;
55+
56+ if ( ! shouldTouch )
57+ return ;
58+
59+ var touchedChain = chain . Touch ( now ) ;
60+
61+ if ( policy . SlidingExpiration )
62+ {
63+ var proposedExpiry = now . Add ( policy . Lifetime ) ;
64+
65+ var newExpiry = chain . AbsoluteExpiresAt is { } absolute
66+ && proposedExpiry > absolute
67+ ? absolute
68+ : proposedExpiry ;
69+
70+ var extendedSession = session . ExtendExpiration ( newExpiry ) ;
71+
72+ if ( ! ReferenceEquals ( extendedSession , session ) )
73+ {
74+ await kernel . SaveSessionAsync ( extendedSession , session . Version , innerCt ) ;
75+ }
76+ }
77+
78+ await kernel . SaveChainAsync ( touchedChain , chain . Version , innerCt ) ;
4179
42- await kernel . SaveChainAsync ( touched , expectedVersion ) ;
4380 didTouch = true ;
4481 } , ct ) ;
4582
46- return SessionRefreshResult . Success ( validation . SessionId ! . Value , didTouch ) ;
83+ return isValid
84+ ? SessionRefreshResult . Success ( validation . SessionId . Value , didTouch )
85+ : SessionRefreshResult . ReauthRequired ( ) ;
4786 }
4887}
0 commit comments