Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ private void validateAuthenticatedAt(OidcUser existingOidcUser, OidcIdToken idTo
if (idToken.getAuthenticatedAt() == null) {
return;
}
if (!idToken.getAuthenticatedAt().equals(existingOidcUser.getIdToken().getAuthenticatedAt())) {
if (idToken.getAuthenticatedAt().isAfter(idToken.getIssuedAt().plus(this.clockSkew))) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, "Invalid authenticated at time",
REFRESH_TOKEN_RESPONSE_ERROR_URI);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ private void validateAuthenticatedAt(OidcUser existingOidcUser, OidcIdToken idTo
return;
}

if (!idToken.getAuthenticatedAt().equals(existingOidcUser.getIdToken().getAuthenticatedAt())) {
if (idToken.getAuthenticatedAt().isAfter(idToken.getIssuedAt().plus(this.clockSkew))) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, "Invalid authenticated at time",
REFRESH_TOKEN_RESPONSE_ERROR_URI);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ void onAuthorizationSuccessWhenIdTokenAudNotContainThenException() {
}

@Test
void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {
void onAuthorizationSuccessWhenIdTokenAuthTimeAfterIssuedAtThenException() {
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
DefaultOidcUser principal = TestOidcUsers.create();
OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,
Expand All @@ -331,7 +331,7 @@ void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {
claims.put("iss", principal.getIssuer());
claims.put("sub", principal.getSubject());
claims.put("aud", principal.getAudience());
claims.put("auth_time", principal.getIssuedAt());
claims.put("auth_time", principal.getIssuedAt().plus(Duration.ofMinutes(2)));
claims.put("nonce", principal.getNonce());
Jwt jwt = mock(Jwt.class);
given(jwt.getTokenValue()).willReturn("id-token-1234");
Expand All @@ -352,6 +352,46 @@ void onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {
.verifyErrorMessage("[invalid_id_token] Invalid authenticated at time");
}

@Test
void onAuthorizationSuccessWhenIdTokenAuthTimeBeforeIssuedAtThenSecurityContextRefreshed() {
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
DefaultOidcUser principal = TestOidcUsers.create();
OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,
principal.getAuthorities(), clientRegistration.getRegistrationId());
OAuth2AccessToken accessToken = createAccessToken();
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),
accessToken, null);
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
Map<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,
OidcParameterNames.ID_TOKEN, "id-token-1234");
Map<String, Object> claims = new HashMap<>();
claims.put("iss", principal.getIssuer());
claims.put("sub", principal.getSubject());
claims.put("aud", principal.getAudience());
claims.put("auth_time", principal.getIssuedAt());
claims.put("nonce", principal.getNonce());
Jwt jwt = mock(Jwt.class);
given(jwt.getTokenValue()).willReturn("id-token-1234");
given(jwt.getIssuedAt()).willReturn(principal.getIssuedAt().plus(Duration.ofMinutes(1)));
given(jwt.getClaims()).willReturn(claims);
ReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);
given(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));
ReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);
given(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);
given(userService.loadUser(any())).willReturn(Mono.just(principal));
WebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();
RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();
handler.setJwtDecoderFactory(reactiveJwtDecoderFactory);
handler.setUserService(userService);
handler.setServerSecurityContextRepository(serverSecurityContextRepository);
StepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))
.verifyComplete();
StepVerifier.create(serverSecurityContextRepository.load(exchange).map(SecurityContext::getAuthentication))
.expectNext(authenticationToken)
.verifyComplete();
}

@Test
void onAuthorizationSuccessWhenIdTokenNonceNotSameThenException() {
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,10 @@ public void onApplicationEventWhenIdTokenAudienceDoesNotMatchThenThrowsOAuth2Aut
}

@Test
public void onApplicationEventWhenIdTokenAuthenticatedAtDoesNotMatchThenThrowsOAuth2AuthenticationException() {
Instant authTime = this.oidcUser.getAuthenticatedAt().plus(5, ChronoUnit.SECONDS);
Jwt jwt = createJwt().claim(IdTokenClaimNames.AUTH_TIME, authTime).build();
public void onApplicationEventWhenIdTokenAuthenticatedAtAfterIssuedAtThenThrowsOAuth2AuthenticationException() {
Instant issuedAt = Instant.now();
Instant authTime = issuedAt.plus(2, ChronoUnit.MINUTES);
Jwt jwt = createJwt().issuedAt(issuedAt).claim(IdTokenClaimNames.AUTH_TIME, authTime).build();
SecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);
given(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);
given(this.jwtDecoder.decode(anyString())).willReturn(jwt);
Expand All @@ -440,6 +441,23 @@ public void onApplicationEventWhenIdTokenAuthenticatedAtDoesNotMatchThenThrowsOA
verifyNoInteractions(this.userService, this.applicationEventPublisher);
}

@Test
public void onApplicationEventWhenIdTokenAuthenticatedAtBeforeIssuedAtThenOidcUserRefreshedEventPublished() {
Instant authTime = this.oidcUser.getAuthenticatedAt().plus(5, ChronoUnit.SECONDS);
Jwt jwt = createJwt().claim(IdTokenClaimNames.AUTH_TIME, authTime).build();
SecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);
given(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);
given(this.jwtDecoder.decode(anyString())).willReturn(jwt);
given(this.userService.loadUser(any(OidcUserRequest.class))).willReturn(this.oidcUser);

OAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(
this.accessTokenResponse, this.authorizedClient);
this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);

verify(this.applicationEventPublisher).publishEvent(any(OidcUserRefreshedEvent.class));
verifyNoMoreInteractions(this.applicationEventPublisher);
}

@Test
public void onApplicationEventWhenIdTokenAuthenticatedAtMatchesThenOidcUserRefreshedEventPublished() {
Instant authTime = this.authentication.getPrincipal().getAttribute(IdTokenClaimNames.AUTH_TIME);
Expand Down