private Mono<OidcIdToken> createOidcToken(ClientRegistration clientRegistration, OAuth2AccessTokenResponse accessTokenResponse) { ReactiveJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration); String rawIdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN); return jwtDecoder.decode(rawIdToken) .map(jwt -> new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims())); } }
@Test public void authenticateWhenNotJwtExceptionThenPropagates() { BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1"); when(this.jwtDecoder.decode(any())).thenReturn(Mono.error(new RuntimeException("Oops"))); assertThatCode(() -> this.manager.authenticate(token).block()) .isInstanceOf(RuntimeException.class); }
@Test public void authenticateWhenJwtThenSuccess() { BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1"); when(this.jwtDecoder.decode(token.getToken())).thenReturn(Mono.just(this.jwt)); Authentication authentication = this.manager.authenticate(token).block(); assertThat(authentication).isNotNull(); assertThat(authentication.isAuthenticated()).isTrue(); assertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("SCOPE_message:read", "SCOPE_message:write"); } }
@Test public void authenticateWhenEmptyJwtThenEmpty() { BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1"); when(this.jwtDecoder.decode(token.getToken())).thenReturn(Mono.empty()); assertThat(this.manager.authenticate(token).block()).isNull(); }
@Test public void authenticateWhenJwtExceptionThenOAuth2AuthenticationException() { BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1"); when(this.jwtDecoder.decode(any())).thenReturn(Mono.error(new JwtException("Oops"))); assertThatCode(() -> this.manager.authenticate(token).block()) .isInstanceOf(OAuth2AuthenticationException.class); }
@Test public void getWhenCustomDecoderThenAuthenticatesAccordingly() { this.spring.register(CustomDecoderConfig.class, RootController.class).autowire(); ReactiveJwtDecoder jwtDecoder = this.spring.getContext().getBean(ReactiveJwtDecoder.class); when(jwtDecoder.decode(anyString())).thenReturn(Mono.just(this.jwt)); this.client.get() .headers(headers -> headers.setBearerAuth("token")) .exchange() .expectStatus().isOk(); verify(jwtDecoder).decode(anyString()); }
@Test public void authenticateWhenIdTokenValidationErrorThenOAuth2AuthenticationException() { OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("foo") .tokenType(OAuth2AccessToken.TokenType.BEARER) .additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue())) .build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); when(this.jwtDecoder.decode(any())).thenThrow(new JwtException("ID Token Validation Error")); this.manager.setJwtDecoderFactory(c -> this.jwtDecoder); assertThatThrownBy(() -> this.manager.authenticate(loginToken()).block()) .isInstanceOf(OAuth2AuthenticationException.class) .hasMessageContaining("[invalid_id_token] ID Token Validation Error"); }
@Test public void authenticationWhenOAuth2UserNotFoundThenEmpty() { OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("foo") .tokenType(OAuth2AccessToken.TokenType.BEARER) .additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.")) .build(); Map<String, Object> claims = new HashMap<>(); claims.put(IdTokenClaimNames.ISS, "https://issuer.example.com"); claims.put(IdTokenClaimNames.SUB, "rob"); claims.put(IdTokenClaimNames.AUD, Arrays.asList("client-id")); Instant issuedAt = Instant.now(); Instant expiresAt = Instant.from(issuedAt).plusSeconds(3600); Jwt idToken = new Jwt("id-token", issuedAt, expiresAt, claims, claims); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); when(this.userService.loadUser(any())).thenReturn(Mono.empty()); when(this.jwtDecoder.decode(any())).thenReturn(Mono.just(idToken)); this.manager.setJwtDecoderFactory(c -> this.jwtDecoder); assertThat(this.manager.authenticate(loginToken()).block()).isNull(); }
@Test public void authenticationWhenOAuth2UserFoundThenSuccess() { OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("foo") .tokenType(OAuth2AccessToken.TokenType.BEARER) .additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue())) .build(); Map<String, Object> claims = new HashMap<>(); claims.put(IdTokenClaimNames.ISS, "https://issuer.example.com"); claims.put(IdTokenClaimNames.SUB, "rob"); claims.put(IdTokenClaimNames.AUD, Arrays.asList("client-id")); Instant issuedAt = Instant.now(); Instant expiresAt = Instant.from(issuedAt).plusSeconds(3600); Jwt idToken = new Jwt("id-token", issuedAt, expiresAt, claims, claims); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); DefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList("ROLE_USER"), this.idToken); when(this.userService.loadUser(any())).thenReturn(Mono.just(user)); when(this.jwtDecoder.decode(any())).thenReturn(Mono.just(idToken)); this.manager.setJwtDecoderFactory(c -> this.jwtDecoder); OAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager.authenticate(loginToken()).block(); assertThat(result.getPrincipal()).isEqualTo(user); assertThat(result.getAuthorities()).containsOnlyElementsOf(user.getAuthorities()); assertThat(result.isAuthenticated()).isTrue(); }
@Test public void authenticateWhenTokenSuccessResponseThenAdditionalParametersAddedToUserRequest() { ClientRegistration clientRegistration = this.registration.build(); Map<String, Object> additionalParameters = new HashMap<>(); additionalParameters.put(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()); additionalParameters.put("param1", "value1"); additionalParameters.put("param2", "value2"); OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("foo") .tokenType(OAuth2AccessToken.TokenType.BEARER) .additionalParameters(additionalParameters) .build(); Map<String, Object> claims = new HashMap<>(); claims.put(IdTokenClaimNames.ISS, "https://issuer.example.com"); claims.put(IdTokenClaimNames.SUB, "rob"); claims.put(IdTokenClaimNames.AUD, Arrays.asList(clientRegistration.getClientId())); Instant issuedAt = Instant.now(); Instant expiresAt = Instant.from(issuedAt).plusSeconds(3600); Jwt idToken = new Jwt("id-token", issuedAt, expiresAt, claims, claims); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); DefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList("ROLE_USER"), this.idToken); ArgumentCaptor<OidcUserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OidcUserRequest.class); when(this.userService.loadUser(userRequestArgCaptor.capture())).thenReturn(Mono.just(user)); when(this.jwtDecoder.decode(any())).thenReturn(Mono.just(idToken)); this.manager.setJwtDecoderFactory(c -> this.jwtDecoder); this.manager.authenticate(loginToken()).block(); assertThat(userRequestArgCaptor.getValue().getAdditionalParameters()) .containsAllEntriesOf(accessTokenResponse.getAdditionalParameters()); }
@Test public void authenticationWhenRefreshTokenThenRefreshTokenInAuthorizedClient() { OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("foo") .tokenType(OAuth2AccessToken.TokenType.BEARER) .additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue())) .refreshToken("refresh-token") .build(); Map<String, Object> claims = new HashMap<>(); claims.put(IdTokenClaimNames.ISS, "https://issuer.example.com"); claims.put(IdTokenClaimNames.SUB, "rob"); claims.put(IdTokenClaimNames.AUD, Arrays.asList("client-id")); Instant issuedAt = Instant.now(); Instant expiresAt = Instant.from(issuedAt).plusSeconds(3600); Jwt idToken = new Jwt("id-token", issuedAt, expiresAt, claims, claims); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); DefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList("ROLE_USER"), this.idToken); when(this.userService.loadUser(any())).thenReturn(Mono.just(user)); when(this.jwtDecoder.decode(any())).thenReturn(Mono.just(idToken)); this.manager.setJwtDecoderFactory(c -> this.jwtDecoder); OAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager.authenticate(loginToken()).block(); assertThat(result.getPrincipal()).isEqualTo(user); assertThat(result.getAuthorities()).containsOnlyElementsOf(user.getAuthorities()); assertThat(result.isAuthenticated()).isTrue(); assertThat(result.getRefreshToken().getTokenValue()).isNotNull(); }
@Test public void issuerWhenResponseIsTypicalThenReturnedDecoderValidatesIssuer() { prepareOpenIdConfigurationResponse(); this.server.enqueue(new MockResponse().setBody(JWK_SET)); ReactiveJwtDecoder decoder = ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer); assertThatCode(() -> decoder.decode(ISSUER_MISMATCH).block()) .isInstanceOf(JwtValidationException.class) .hasMessageContaining("This iss claim is not equal to the configured issuer"); }
private Mono<OidcIdToken> createOidcToken(ClientRegistration clientRegistration, OAuth2AccessTokenResponse accessTokenResponse) { ReactiveJwtDecoder jwtDecoder = this.decoderFactory.apply(clientRegistration); String rawIdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN); return jwtDecoder.decode(rawIdToken) .map(jwt -> new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims())) .doOnNext(idToken -> OidcTokenValidator.validateIdToken(idToken, clientRegistration)); }