1818
1919import java .time .Duration ;
2020import java .time .Instant ;
21+ import java .time .temporal .ChronoUnit ;
2122import java .util .Collections ;
2223import java .util .HashMap ;
24+ import java .util .LinkedHashSet ;
2325import java .util .List ;
2426import java .util .Map ;
2527import java .util .Set ;
3739import org .springframework .security .oauth2 .client .registration .TestClientRegistrations ;
3840import org .springframework .security .oauth2 .client .userinfo .ReactiveOAuth2UserService ;
3941import org .springframework .security .oauth2 .core .OAuth2AccessToken ;
42+ import org .springframework .security .oauth2 .core .oidc .OidcIdToken ;
4043import org .springframework .security .oauth2 .core .oidc .OidcScopes ;
4144import org .springframework .security .oauth2 .core .oidc .endpoint .OidcParameterNames ;
4245import org .springframework .security .oauth2 .core .oidc .user .DefaultOidcUser ;
@@ -99,7 +102,8 @@ void setClockSkewWhenNullThenException() {
99102 @ Test
100103 void onAuthorizationSuccessWhenIdTokenValidThenSecurityContextRefreshed () {
101104 ClientRegistration clientRegistration = TestClientRegistrations .clientRegistration ().build ();
102- DefaultOidcUser principal = TestOidcUsers .create ();
105+ Instant authTime = Instant .now ();
106+ DefaultOidcUser principal = createOidcUser (authTime );
103107 OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken (principal ,
104108 principal .getAuthorities (), clientRegistration .getRegistrationId ());
105109 OAuth2AccessToken accessToken = createAccessToken ();
@@ -112,6 +116,7 @@ void onAuthorizationSuccessWhenIdTokenValidThenSecurityContextRefreshed() {
112116 claims .put ("iss" , principal .getIssuer ());
113117 claims .put ("sub" , principal .getSubject ());
114118 claims .put ("aud" , principal .getAudience ());
119+ claims .put ("auth_time" , authTime );
115120 claims .put ("nonce" , principal .getNonce ());
116121 Jwt jwt = mock (Jwt .class );
117122 given (jwt .getTokenValue ()).willReturn ("id-token-1234" );
@@ -316,9 +321,10 @@ void onAuthorizationSuccessWhenIdTokenAudNotContainThenException() {
316321 }
317322
318323 @ Test
319- void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException () {
324+ void onAuthorizationSuccessWhenIdTokenAuthTimeBeforeCurrentAuthTimeThenException () {
320325 ClientRegistration clientRegistration = TestClientRegistrations .clientRegistration ().build ();
321- DefaultOidcUser principal = TestOidcUsers .create ();
326+ Instant authTime = Instant .now ();
327+ DefaultOidcUser principal = createOidcUser (authTime );
322328 OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken (principal ,
323329 principal .getAuthorities (), clientRegistration .getRegistrationId ());
324330 OAuth2AccessToken accessToken = createAccessToken ();
@@ -331,7 +337,7 @@ void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {
331337 claims .put ("iss" , principal .getIssuer ());
332338 claims .put ("sub" , principal .getSubject ());
333339 claims .put ("aud" , principal .getAudience ());
334- claims .put ("auth_time" , principal . getIssuedAt ( ));
340+ claims .put ("auth_time" , authTime . minus ( 5 , ChronoUnit . MINUTES ));
335341 claims .put ("nonce" , principal .getNonce ());
336342 Jwt jwt = mock (Jwt .class );
337343 given (jwt .getTokenValue ()).willReturn ("id-token-1234" );
@@ -352,6 +358,47 @@ void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {
352358 .verifyErrorMessage ("[invalid_id_token] Invalid authenticated at time" );
353359 }
354360
361+ @ Test
362+ void onAuthorizationSuccessWhenIdTokenAuthTimeAfterCurrentAuthTimeThenSecurityContextRefreshed () {
363+ ClientRegistration clientRegistration = TestClientRegistrations .clientRegistration ().build ();
364+ Instant authTime = Instant .now ();
365+ DefaultOidcUser principal = createOidcUser (authTime );
366+ OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken (principal ,
367+ principal .getAuthorities (), clientRegistration .getRegistrationId ());
368+ OAuth2AccessToken accessToken = createAccessToken ();
369+ OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient (clientRegistration , principal .getName (),
370+ accessToken , null );
371+ MockServerWebExchange exchange = MockServerWebExchange .from (MockServerHttpRequest .get ("/" ).build ());
372+ Map <String , Object > attributes = Map .of (ServerWebExchange .class .getName (), exchange ,
373+ OidcParameterNames .ID_TOKEN , "id-token-1234" );
374+ Map <String , Object > claims = new HashMap <>();
375+ claims .put ("iss" , principal .getIssuer ());
376+ claims .put ("sub" , principal .getSubject ());
377+ claims .put ("aud" , principal .getAudience ());
378+ claims .put ("auth_time" , authTime .plus (5 , ChronoUnit .MINUTES ));
379+ claims .put ("nonce" , principal .getNonce ());
380+ Jwt jwt = mock (Jwt .class );
381+ given (jwt .getTokenValue ()).willReturn ("id-token-1234" );
382+ given (jwt .getIssuedAt ()).willReturn (principal .getIssuedAt ());
383+ given (jwt .getClaims ()).willReturn (claims );
384+ ReactiveJwtDecoder jwtDecoder = mock (ReactiveJwtDecoder .class );
385+ given (jwtDecoder .decode (any ())).willReturn (Mono .just (jwt ));
386+ ReactiveJwtDecoderFactory <ClientRegistration > reactiveJwtDecoderFactory = mock (ReactiveJwtDecoderFactory .class );
387+ given (reactiveJwtDecoderFactory .createDecoder (any ())).willReturn (jwtDecoder );
388+ ReactiveOAuth2UserService <OidcUserRequest , OidcUser > userService = mock (ReactiveOAuth2UserService .class );
389+ given (userService .loadUser (any ())).willReturn (Mono .just (principal ));
390+ WebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository ();
391+ RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler ();
392+ handler .setJwtDecoderFactory (reactiveJwtDecoderFactory );
393+ handler .setUserService (userService );
394+ handler .setServerSecurityContextRepository (serverSecurityContextRepository );
395+ StepVerifier .create (handler .onAuthorizationSuccess (authorizedClient , authenticationToken , attributes ))
396+ .verifyComplete ();
397+ StepVerifier .create (serverSecurityContextRepository .load (exchange ).map (SecurityContext ::getAuthentication ))
398+ .expectNext (authenticationToken )
399+ .verifyComplete ();
400+ }
401+
355402 @ Test
356403 void onAuthorizationSuccessWhenIdTokenNonceNotSameThenException () {
357404 ClientRegistration clientRegistration = TestClientRegistrations .clientRegistration ().build ();
@@ -395,4 +442,21 @@ private static OAuth2AccessToken createAccessToken() {
395442 Set .of (OidcScopes .OPENID ));
396443 }
397444
445+ private static DefaultOidcUser createOidcUser (Instant authTime ) {
446+ Instant issuedAt = Instant .now ();
447+ Instant expiresAt = issuedAt .plusSeconds (3600 );
448+ // @formatter:off
449+ OidcIdToken idToken = OidcIdToken .withTokenValue ("id-token" )
450+ .issuedAt (issuedAt )
451+ .expiresAt (expiresAt )
452+ .subject ("subject" )
453+ .issuer ("http://localhost/issuer" )
454+ .audience (Collections .unmodifiableSet (new LinkedHashSet <>(Collections .singletonList ("client-id" ))))
455+ .authorizedParty ("client" )
456+ .authTime (authTime )
457+ .build ();
458+ // @formatter:on
459+ return new DefaultOidcUser (null , idToken );
460+ }
461+
398462}
0 commit comments