@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; } }
@Test public void constructorWhenAllParametersProvidedAndValidThenCreated() { OAuth2AccessToken accessToken = new OAuth2AccessToken( TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT, EXPIRES_AT, SCOPES); assertThat(accessToken.getTokenType()).isEqualTo(TOKEN_TYPE); assertThat(accessToken.getTokenValue()).isEqualTo(TOKEN_VALUE); assertThat(accessToken.getIssuedAt()).isEqualTo(ISSUED_AT); assertThat(accessToken.getExpiresAt()).isEqualTo(EXPIRES_AT); assertThat(accessToken.getScopes()).isEqualTo(SCOPES); }
@Override public OAuth2AccessTokenResponse getTokenResponse(OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) { Assert.notNull(clientCredentialsGrantRequest, "clientCredentialsGrantRequest cannot be null"); RequestEntity<?> request = this.requestEntityConverter.convert(clientCredentialsGrantRequest); ResponseEntity<OAuth2AccessTokenResponse> response; try { response = this.restOperations.exchange(request, OAuth2AccessTokenResponse.class); } catch (RestClientException ex) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: " + ex.getMessage(), null); throw new OAuth2AuthorizationException(oauth2Error, ex); } OAuth2AccessTokenResponse tokenResponse = response.getBody(); if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) { // As per spec, in Section 5.1 Successful Access Token Response // https://tools.ietf.org/html/rfc6749#section-5.1 // If AccessTokenResponse.scope is empty, then default to the scope // originally requested by the client in the Token Request tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse) .scopes(clientCredentialsGrantRequest.getClientRegistration().getScopes()) .build(); } return tokenResponse; }
@Override public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) { Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null"); RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest); ResponseEntity<OAuth2AccessTokenResponse> response; try { response = this.restOperations.exchange(request, OAuth2AccessTokenResponse.class); } catch (RestClientException ex) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: " + ex.getMessage(), null); throw new OAuth2AuthorizationException(oauth2Error, ex); } OAuth2AccessTokenResponse tokenResponse = response.getBody(); if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) { // As per spec, in Section 5.1 Successful Access Token Response // https://tools.ietf.org/html/rfc6749#section-5.1 // If AccessTokenResponse.scope is empty, then default to the scope // originally requested by the client in the Token Request tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse) .scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes()) .build(); } return tokenResponse; }
@Test public void constructorWhenCreatedThenIsSerializableAndDeserializable() { OAuth2AccessToken accessToken = new OAuth2AccessToken( TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT, EXPIRES_AT, SCOPES); byte[] serialized = SerializationUtils.serialize(accessToken); accessToken = (OAuth2AccessToken) SerializationUtils.deserialize(serialized); assertThat(serialized).isNotNull(); assertThat(accessToken.getTokenType()).isEqualTo(TOKEN_TYPE); assertThat(accessToken.getTokenValue()).isEqualTo(TOKEN_VALUE); assertThat(accessToken.getIssuedAt()).isEqualTo(ISSUED_AT); assertThat(accessToken.getExpiresAt()).isEqualTo(EXPIRES_AT); assertThat(accessToken.getScopes()).isEqualTo(SCOPES); } }
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) { // Auto-disabled if UserInfo Endpoint URI is not provided if (StringUtils.isEmpty(userRequest.getClientRegistration().getProviderDetails() .getUserInfoEndpoint().getUri())) { return false; } // The Claims requested by the profile, email, address, and phone scope values // are returned from the UserInfo Endpoint (as described in Section 5.3.2), // when a response_type value is used that results in an Access Token being issued. // However, when no Access Token is issued, which is the case for the response_type=id_token, // the resulting Claims are returned in the ID Token. // The Authorization Code Grant Flow, which is response_type=code, results in an Access Token being issued. if (AuthorizationGrantType.AUTHORIZATION_CODE.equals( userRequest.getClientRegistration().getAuthorizationGrantType())) { // Return true if there is at least one match between the authorized scope(s) and UserInfo scope(s) return CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.userInfoScopes); } return false; }
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(); }
@Test public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenReturnAccessTokenResponseUsingRequestedScope() throws Exception { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); this.clientRegistration.scope("openid", "profile", "email", "address"); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block(); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile", "email", "address"); }
@Test public void getTokenResponseWhenSuccessResponseIncludesScopeThenReturnAccessTokenResponseUsingResponseScope() throws Exception { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"scope\": \"openid profile\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); this.clientRegistration.scope("openid", "profile", "email", "address"); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block(); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile"); }
@Test public void getTokenResponseWhenNoScopeThenClientRegistrationScopesDefaulted() { ClientRegistration registration = this.clientRegistration.build(); enqueueJson("{\n" + " \"access_token\":\"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3\",\n" + " \"token_type\":\"bearer\",\n" + " \"expires_in\":3600,\n" + " \"refresh_token\":\"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk\"\n" + "}"); OAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration); OAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block(); assertThat(response.getAccessToken().getScopes()).isEqualTo(registration.getScopes()); }
@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 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")); }
@Test public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasDefaultScope() { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"refresh_token\": \"refresh-token-1234\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest()); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); }
@Test public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasDefaultScope() { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(this.clientRegistration); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); }
@Test public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"refresh_token\": \"refresh-token-1234\",\n" + " \"scope\": \"read\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest()); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read"); }
@Test public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() { String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"scope\": \"read\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(this.clientRegistration); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read"); }
@Override public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) { return Mono.defer(() -> { ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration(); OAuth2AuthorizationExchange authorizationExchange = authorizationGrantRequest.getAuthorizationExchange(); String tokenUri = clientRegistration.getProviderDetails().getTokenUri(); BodyInserters.FormInserter<String> body = body(authorizationExchange); return this.webClient.post() .uri(tokenUri) .accept(MediaType.APPLICATION_JSON) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(body) .exchange() .flatMap(response -> response.body(oauth2AccessTokenResponse())) .map(response -> { if (response.getAccessToken().getScopes().isEmpty()) { response = OAuth2AccessTokenResponse.withResponse(response) .scopes(authorizationExchange.getAuthorizationRequest().getScopes()) .build(); } return response; }); }); }
@Test public void buildWhenResponseThenAllAttributesAreSet() { 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(); OAuth2AccessTokenResponse withResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse) .build(); assertThat(withResponse.getAccessToken().getTokenValue()).isEqualTo(tokenResponse.getAccessToken().getTokenValue()); assertThat(withResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); assertThat(withResponse.getAccessToken().getIssuedAt()).isEqualTo(tokenResponse.getAccessToken().getIssuedAt()); assertThat(withResponse.getAccessToken().getExpiresAt()).isEqualTo(tokenResponse.getAccessToken().getExpiresAt()); assertThat(withResponse.getAccessToken().getScopes()).isEqualTo(tokenResponse.getAccessToken().getScopes()); assertThat(withResponse.getRefreshToken().getTokenValue()).isEqualTo(tokenResponse.getRefreshToken().getTokenValue()); assertThat(withResponse.getAdditionalParameters()).isEqualTo(tokenResponse.getAdditionalParameters()); }
assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile", "email", "address");
@Test public void getTokenResponseWhenSuccessResponseIncludesScopeThenReturnAccessTokenResponseUsingResponseScope() throws Exception { MockWebServer server = new MockWebServer(); String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\",\n" + " \"scope\": \"openid profile\"\n" + "}\n"; server.enqueue(new MockResponse() .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .setBody(accessTokenSuccessResponse)); server.start(); String tokenUri = server.url("/oauth2/token").toString(); this.clientRegistrationBuilder.tokenUri(tokenUri); OAuth2AuthorizationRequest authorizationRequest = request().scope("openid", "profile", "email", "address").build(); OAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange( authorizationRequest, this.authorizationResponse); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse( new OAuth2AuthorizationCodeGrantRequest( this.clientRegistrationBuilder.build(), authorizationExchange)); server.shutdown(); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile"); }