@Override public Map<String, String> convert(OAuth2AccessTokenResponse tokenResponse) { Map<String, String> parameters = new HashMap<>(); long expiresIn = -1; if (tokenResponse.getAccessToken().getExpiresAt() != null) { expiresIn = ChronoUnit.SECONDS.between(Instant.now(), tokenResponse.getAccessToken().getExpiresAt()); } parameters.put(OAuth2ParameterNames.ACCESS_TOKEN, tokenResponse.getAccessToken().getTokenValue()); parameters.put(OAuth2ParameterNames.TOKEN_TYPE, tokenResponse.getAccessToken().getTokenType().getValue()); parameters.put(OAuth2ParameterNames.EXPIRES_IN, String.valueOf(expiresIn)); if (!CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) { parameters.put(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(tokenResponse.getAccessToken().getScopes(), " ")); } if (tokenResponse.getRefreshToken() != null) { parameters.put(OAuth2ParameterNames.REFRESH_TOKEN, tokenResponse.getRefreshToken().getTokenValue()); } if (!CollectionUtils.isEmpty(tokenResponse.getAdditionalParameters())) { tokenResponse.getAdditionalParameters().entrySet().stream() .forEach(e -> parameters.put(e.getKey(), e.getValue().toString())); } return parameters; } }
public static OAuth2RefreshToken refreshToken() { return new OAuth2RefreshToken("refresh-token", Instant.now()); } }
private Builder(OAuth2AccessTokenResponse response) { OAuth2AccessToken accessToken = response.getAccessToken(); this.tokenValue = accessToken.getTokenValue(); this.tokenType = accessToken.getTokenType(); this.expiresAt = accessToken.getExpiresAt(); this.issuedAt = accessToken.getIssuedAt(); this.scopes = accessToken.getScopes(); this.refreshToken = response.getRefreshToken() == null ? null : response.getRefreshToken().getTokenValue(); this.additionalParameters = response.getAdditionalParameters(); }
/** * Builds a new {@link OAuth2AccessTokenResponse}. * * @return a {@link OAuth2AccessTokenResponse} */ public OAuth2AccessTokenResponse build() { Instant issuedAt = getIssuedAt(); Instant expiresAt = getExpiresAt(); OAuth2AccessTokenResponse accessTokenResponse = new OAuth2AccessTokenResponse(); accessTokenResponse.accessToken = new OAuth2AccessToken( this.tokenType, this.tokenValue, issuedAt, expiresAt, this.scopes); if (StringUtils.hasText(this.refreshToken)) { accessTokenResponse.refreshToken = new OAuth2RefreshToken(this.refreshToken, issuedAt); } accessTokenResponse.additionalParameters = Collections.unmodifiableMap( CollectionUtils.isEmpty(this.additionalParameters) ? Collections.emptyMap() : this.additionalParameters); return accessTokenResponse; }
@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 filterWhenClientRegistrationIdAndServerWebExchangeFromContextThenServerWebExchangeFromContext() { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); when(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).thenReturn(Mono.just(authorizedClient)); when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(Mono.just(this.registration)); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .attributes(clientRegistrationId(this.registration.getRegistrationId())) .build(); this.function.filter(request, this.exchange) .subscriberContext(serverWebExchange()) .block(); verify(this.authorizedClientRepository).loadAuthorizedClient(eq(this.registration.getRegistrationId()), any(), eq(this.serverWebExchange)); }
private Mono<OAuth2AuthorizedClient> authorizeWithRefreshToken(ClientRequest request, ExchangeFunction next, OAuth2AuthorizedClient authorizedClient) { ClientRegistration clientRegistration = authorizedClient .getClientRegistration(); String tokenUri = clientRegistration .getProviderDetails().getTokenUri(); ClientRequest refreshRequest = ClientRequest.create(HttpMethod.POST, URI.create(tokenUri)) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(refreshTokenBody(authorizedClient.getRefreshToken().getTokenValue())) .build(); return next.exchange(refreshRequest) .flatMap(response -> response.body(oauth2AccessTokenResponse())) .map(accessTokenResponse -> new OAuth2AuthorizedClient(authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken())) .map(result -> { Authentication principal = (Authentication) request.attribute( AUTHENTICATION_ATTR_NAME).orElse(new PrincipalNameAuthentication(authorizedClient.getPrincipalName())); HttpServletRequest httpRequest = (HttpServletRequest) request.attributes().get( HTTP_SERVLET_REQUEST_ATTR_NAME); HttpServletResponse httpResponse = (HttpServletResponse) request.attributes().get( HTTP_SERVLET_RESPONSE_ATTR_NAME); this.authorizedClientRepository.saveAuthorizedClient(result, principal, httpRequest, httpResponse); return result; }) .publishOn(Schedulers.elastic()); }
@Test public void filterWhenClientRegistrationIdThenAuthorizedClientResolved() { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); when(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).thenReturn(Mono.just(authorizedClient)); when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(Mono.just(this.registration)); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .attributes(clientRegistrationId(this.registration.getRegistrationId())) .build(); this.function.filter(request, this.exchange).block(); List<ClientRequest> requests = this.exchange.getRequests(); assertThat(requests).hasSize(1); ClientRequest request0 = requests.get(0); assertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); assertThat(request0.url().toASCIIString()).isEqualTo("https://example.com"); assertThat(request0.method()).isEqualTo(HttpMethod.GET); assertThat(getBody(request0)).isEmpty(); }
@Test public void oauth2AccessTokenResponseWhenValidThenCreated() throws Exception { BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors .oauth2AccessTokenResponse(); MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.setBody("{\n" + " \"access_token\":\"2YotnFZFEjr1zCsicMWpAA\",\n" + " \"token_type\":\"Bearer\",\n" + " \"expires_in\":3600,\n" + " \"refresh_token\":\"tGzv3JOkF0XG5Qx2TlKWIA\",\n" + " \"example_parameter\":\"example_value\"\n" + " }"); Instant now = Instant.now(); OAuth2AccessTokenResponse result = extractor.extract(response, this.context).block(); assertThat(result.getAccessToken().getTokenValue()).isEqualTo("2YotnFZFEjr1zCsicMWpAA"); assertThat(result.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); assertThat(result.getAccessToken().getExpiresAt()).isBetween(now.plusSeconds(3600), now.plusSeconds(3600 + 2)); assertThat(result.getRefreshToken().getTokenValue()).isEqualTo("tGzv3JOkF0XG5Qx2TlKWIA"); assertThat(result.getAdditionalParameters()).containsEntry("example_parameter", "example_value"); }
@Test public void filterWhenDefaultClientRegistrationIdThenAuthorizedClientResolved() { this.function.setDefaultClientRegistrationId(this.registration.getRegistrationId()); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); when(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).thenReturn(Mono.just(authorizedClient)); when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(Mono.just(this.registration)); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .build(); this.function.filter(request, this.exchange).block(); List<ClientRequest> requests = this.exchange.getRequests(); assertThat(requests).hasSize(1); ClientRequest request0 = requests.get(0); assertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); assertThat(request0.url().toASCIIString()).isEqualTo("https://example.com"); assertThat(request0.method()).isEqualTo(HttpMethod.GET); assertThat(getBody(request0)).isEmpty(); }
@Test public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"scope\": \"openid profile\",\n" + " \"refresh_token\": \"refresh-token-1234\",\n" + " \"custom_parameter_1\": \"custom-value-1\",\n" + " \"custom_parameter_2\": \"custom-value-2\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); Instant expiresAtBefore = Instant.now().plusSeconds(3600); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block(); Instant expiresAtAfter = Instant.now().plusSeconds(3600); assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token-1234"); assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo( OAuth2AccessToken.TokenType.BEARER); assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile"); assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234"); assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1"); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2"); }
@Test public void filterWhenNotExpiredThenShouldRefreshFalse() { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .attributes(oauth2AuthorizedClient(authorizedClient)) .build(); this.function.filter(request, this.exchange).block(); List<ClientRequest> requests = this.exchange.getRequests(); assertThat(requests).hasSize(1); ClientRequest request0 = requests.get(0); assertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); assertThat(request0.url().toASCIIString()).isEqualTo("https://example.com"); assertThat(request0.method()).isEqualTo(HttpMethod.GET); assertThat(getBody(request0)).isEmpty(); }
@Test public void buildWhenAllAttributesProvidedThenAllAttributesAreSet() { Instant expiresAt = Instant.now().plusSeconds(5); Set<String> scopes = new LinkedHashSet<>(Arrays.asList("scope1", "scope2")); Map<String, Object> additionalParameters = new HashMap<>(); additionalParameters.put("param1", "value1"); additionalParameters.put("param2", "value2"); OAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse .withToken(TOKEN_VALUE) .tokenType(OAuth2AccessToken.TokenType.BEARER) .expiresIn(expiresAt.toEpochMilli()) .scopes(scopes) .refreshToken(REFRESH_TOKEN_VALUE) .additionalParameters(additionalParameters) .build(); assertThat(tokenResponse.getAccessToken()).isNotNull(); assertThat(tokenResponse.getAccessToken().getTokenValue()).isEqualTo(TOKEN_VALUE); assertThat(tokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); assertThat(tokenResponse.getAccessToken().getIssuedAt()).isNotNull(); assertThat(tokenResponse.getAccessToken().getExpiresAt()).isAfterOrEqualTo(expiresAt); assertThat(tokenResponse.getAccessToken().getScopes()).isEqualTo(scopes); assertThat(tokenResponse.getRefreshToken().getTokenValue()).isEqualTo(REFRESH_TOKEN_VALUE); assertThat(tokenResponse.getAdditionalParameters()).isEqualTo(additionalParameters); }
@Test public void filterWhenNotExpiredThenShouldRefreshFalse() { this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.clientRegistrationRepository, this.authorizedClientRepository); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .attributes(oauth2AuthorizedClient(authorizedClient)) .build(); this.function.filter(request, this.exchange).block(); List<ClientRequest> requests = this.exchange.getRequests(); assertThat(requests).hasSize(1); ClientRequest request0 = requests.get(0); assertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); assertThat(request0.url().toASCIIString()).isEqualTo("https://example.com"); assertThat(request0.method()).isEqualTo(HttpMethod.GET); assertThat(getBody(request0)).isEmpty(); }
@Test // gh-6087 public void oauth2AccessTokenResponseWhenMultipleAttributeTypesThenCreated() throws Exception { BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors .oauth2AccessTokenResponse(); MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.setBody("{\n" + " \"access_token\":\"2YotnFZFEjr1zCsicMWpAA\",\n" + " \"token_type\":\"Bearer\",\n" + " \"expires_in\":3600,\n" + " \"refresh_token\":\"tGzv3JOkF0XG5Qx2TlKWIA\",\n" + " \"subjson\":{}, \n" + " \"list\":[] \n" + " }"); Instant now = Instant.now(); OAuth2AccessTokenResponse result = extractor.extract(response, this.context).block(); assertThat(result.getAccessToken().getTokenValue()).isEqualTo("2YotnFZFEjr1zCsicMWpAA"); assertThat(result.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); assertThat(result.getAccessToken().getExpiresAt()).isBetween(now.plusSeconds(3600), now.plusSeconds(3600 + 2)); assertThat(result.getRefreshToken().getTokenValue()).isEqualTo("tGzv3JOkF0XG5Qx2TlKWIA"); assertThat(result.getAdditionalParameters().get("subjson")).isInstanceOfAny(Map.class); assertThat(result.getAdditionalParameters().get("list")).isInstanceOfAny(List.class); } }
@Test public void filterWhenClientRegistrationIdFromAuthenticationThenAuthorizedClientResolved() { this.function.setDefaultOAuth2AuthorizedClient(true); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", this.accessToken.getIssuedAt()); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken); when(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).thenReturn(Mono.just(authorizedClient)); when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(Mono.just(this.registration)); ClientRequest request = ClientRequest.create(GET, URI.create("https://example.com")) .build(); OAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList("ROLE_USER"), Collections .singletonMap("user", "rob"), "user"); OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(user, user.getAuthorities(), "client-id"); this.function .filter(request, this.exchange) .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication)) .block(); List<ClientRequest> requests = this.exchange.getRequests(); assertThat(requests).hasSize(1); ClientRequest request0 = requests.get(0); assertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0"); assertThat(request0.url().toASCIIString()).isEqualTo("https://example.com"); assertThat(request0.method()).isEqualTo(HttpMethod.GET); assertThat(getBody(request0)).isEmpty(); }
private Mono<OAuth2AuthorizedClient> authorizeWithRefreshToken(ExchangeFunction next, OAuth2AuthorizedClient authorizedClient, OAuth2AuthorizedClientResolver.Request r) { ServerWebExchange exchange = r.getExchange(); Authentication authentication = r.getAuthentication(); ClientRegistration clientRegistration = authorizedClient .getClientRegistration(); String tokenUri = clientRegistration .getProviderDetails().getTokenUri(); ClientRequest refreshRequest = ClientRequest.create(HttpMethod.POST, URI.create(tokenUri)) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(refreshTokenBody(authorizedClient.getRefreshToken().getTokenValue())) .build(); return next.exchange(refreshRequest) .flatMap(refreshResponse -> refreshResponse.body(oauth2AccessTokenResponse())) .map(accessTokenResponse -> new OAuth2AuthorizedClient(authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken())) .flatMap(result -> this.authorizedClientRepository.saveAuthorizedClient(result, authentication, exchange) .thenReturn(result)); }
accessTokenExpiresAt); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", issuedAt); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken);
@Test public void readInternalWhenSuccessfulTokenResponseThenReadOAuth2AccessTokenResponse() throws Exception { String tokenResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"scope\": \"read write\",\n" + " \"refresh_token\": \"refresh-token-1234\",\n" + " \"custom_parameter_1\": \"custom-value-1\",\n" + " \"custom_parameter_2\": \"custom-value-2\"\n" + "}\n"; MockClientHttpResponse response = new MockClientHttpResponse( tokenResponse.getBytes(), HttpStatus.OK); OAuth2AccessTokenResponse accessTokenResponse = this.messageConverter.readInternal( OAuth2AccessTokenResponse.class, response); assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token-1234"); assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBeforeOrEqualTo(Instant.now().plusSeconds(3600)); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234"); assertThat(accessTokenResponse.getAdditionalParameters()).containsExactly( entry("custom_parameter_1", "custom-value-1"), entry("custom_parameter_2", "custom-value-2")); }
accessTokenExpiresAt); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", issuedAt); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName", this.accessToken, refreshToken);