From 38b796f9ce852d642054ec128cb28468d7987d0b Mon Sep 17 00:00:00 2001
From: Manish Kumar <110275771+code4uuuu@users.noreply.github.com>
Date: Thu, 9 Jan 2025 14:07:27 +0530
Subject: [PATCH] fix/MNT-24542 class cast exception (#3092)
* [MNT-24542] fix class cast exception
* pre-commit-issue
* removed else part
* address review comments
* fix-conflict
---
.../SpringBasedIdentityServiceFacade.java | 698 +++++++++---------
1 file changed, 354 insertions(+), 344 deletions(-)
diff --git a/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacade.java b/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacade.java
index cab996c09b..8eb0eff806 100644
--- a/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacade.java
+++ b/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/SpringBasedIdentityServiceFacade.java
@@ -1,344 +1,354 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2005 - 2025 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
- * provided under the following open source license terms:
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
-
-package org.alfresco.repo.security.authentication.identityservice;
-
-import static java.util.Objects.requireNonNull;
-
-import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceMetadataKey.AUDIENCE;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.time.Instant;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Predicate;
-
-import com.nimbusds.oauth2.sdk.ParseException;
-import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
-import com.nimbusds.openid.connect.sdk.UserInfoRequest;
-import com.nimbusds.openid.connect.sdk.UserInfoResponse;
-import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.core.convert.converter.Converter;
-import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;
-import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
-import org.springframework.security.oauth2.client.endpoint.DefaultPasswordTokenResponseClient;
-import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
-import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
-import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
-import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest;
-import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequestEntityConverter;
-import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
-import org.springframework.security.oauth2.client.registration.ClientRegistration;
-import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
-import org.springframework.security.oauth2.core.AbstractOAuth2Token;
-import org.springframework.security.oauth2.core.AuthorizationGrantType;
-import org.springframework.security.oauth2.core.OAuth2AccessToken;
-import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType;
-import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
-import org.springframework.security.oauth2.core.OAuth2RefreshToken;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.client.RestOperations;
-
-class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
-{
- private static final Log LOGGER = LogFactory.getLog(SpringBasedIdentityServiceFacade.class);
- private static final Instant SOME_INSIGNIFICANT_DATE_IN_THE_PAST = Instant.MIN.plusSeconds(12345);
- private final Map clients;
- private final ClientRegistration clientRegistration;
- private final JwtDecoder jwtDecoder;
-
- SpringBasedIdentityServiceFacade(RestOperations restOperations, ClientRegistration clientRegistration,
- JwtDecoder jwtDecoder)
- {
- requireNonNull(restOperations);
- this.clientRegistration = requireNonNull(clientRegistration);
- this.jwtDecoder = requireNonNull(jwtDecoder);
- this.clients = Map.of(
- AuthorizationGrantType.AUTHORIZATION_CODE, createAuthorizationCodeClient(restOperations),
- AuthorizationGrantType.REFRESH_TOKEN, createRefreshTokenClient(restOperations),
- AuthorizationGrantType.PASSWORD, createPasswordClient(restOperations, clientRegistration));
- }
-
- @Override
- public AccessTokenAuthorization authorize(AuthorizationGrant authorizationGrant)
- {
- final AbstractOAuth2AuthorizationGrantRequest request = createRequest(authorizationGrant);
- final OAuth2AccessTokenResponseClient client = getClient(request);
-
- final OAuth2AccessTokenResponse response;
- try
- {
- response = client.getTokenResponse(request);
- }
- catch (OAuth2AuthorizationException e)
- {
- LOGGER.debug("Failed to authorize against Authorization Server. Reason: " + e.getError() + ".");
- throw new AuthorizationException("Failed to obtain access token. " + e.getError(), e);
- }
- catch (RuntimeException e)
- {
- LOGGER.warn("Failed to authorize against Authorization Server. Reason: " + e.getMessage());
- throw new AuthorizationException("Failed to obtain access token.", e);
- }
-
- return new SpringAccessTokenAuthorization(response);
- }
-
- @Override
- public Optional getUserInfo(String tokenParameter, String principalAttribute)
- {
- return Optional.ofNullable(tokenParameter)
- .filter(Predicate.not(String::isEmpty))
- .flatMap(token -> Optional.ofNullable(clientRegistration)
- .map(ClientRegistration::getProviderDetails)
- .map(ClientRegistration.ProviderDetails::getUserInfoEndpoint)
- .map(ClientRegistration.ProviderDetails.UserInfoEndpoint::getUri)
- .flatMap(uri -> {
- try
- {
- return Optional.of(
- new UserInfoRequest(new URI(uri), new BearerAccessToken(token)).toHTTPRequest().send());
- }
- catch (IOException | URISyntaxException e)
- {
- LOGGER.warn("Failed to get user information. Reason: " + e.getMessage());
- return Optional.empty();
- }
- })
- .flatMap(httpResponse -> {
- try
- {
- return Optional.of(UserInfoResponse.parse(httpResponse));
- }
- catch (ParseException e)
- {
- LOGGER.warn("Failed to parse user info response. Reason: " + e.getMessage());
- return Optional.empty();
- }
- })
- .map(UserInfoResponse::toSuccessResponse)
- .map(UserInfoSuccessResponse::getUserInfo))
- .map(userInfo -> new OIDCUserInfo(userInfo.getStringClaim(principalAttribute), userInfo.getGivenName(),
- userInfo.getFamilyName(), userInfo.getEmailAddress()));
- }
-
- @Override
- public ClientRegistration getClientRegistration()
- {
- return clientRegistration;
- }
-
- @Override
- public DecodedAccessToken decodeToken(String token)
- {
- final Jwt validToken;
- try
- {
- validToken = jwtDecoder.decode(token);
- }
- catch (RuntimeException e)
- {
- throw new TokenDecodingException("Failed to decode token. " + e.getMessage(), e);
- }
- if (LOGGER.isDebugEnabled())
- {
- LOGGER.debug("Bearer token outcome: " + validToken.getClaims());
- }
- return new SpringDecodedAccessToken(validToken);
- }
-
- private AbstractOAuth2AuthorizationGrantRequest createRequest(AuthorizationGrant grant)
- {
- if (grant.isPassword())
- {
- return new OAuth2PasswordGrantRequest(clientRegistration, grant.getUsername(), grant.getPassword());
- }
-
- if (grant.isRefreshToken())
- {
- final OAuth2AccessToken expiredAccessToken = new OAuth2AccessToken(
- TokenType.BEARER,
- "JUST_FOR_FULFILLING_THE_SPRING_API",
- SOME_INSIGNIFICANT_DATE_IN_THE_PAST,
- SOME_INSIGNIFICANT_DATE_IN_THE_PAST.plusSeconds(1));
- final OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(grant.getRefreshToken(), null);
-
- return new OAuth2RefreshTokenGrantRequest(clientRegistration, expiredAccessToken, refreshToken,
- clientRegistration.getScopes());
- }
-
- if (grant.isAuthorizationCode())
- {
- final OAuth2AuthorizationExchange authzExchange = new OAuth2AuthorizationExchange(
- OAuth2AuthorizationRequest.authorizationCode()
- .clientId(clientRegistration.getClientId())
- .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
- .redirectUri(grant.getRedirectUri())
- .scopes(clientRegistration.getScopes())
- .build(),
- OAuth2AuthorizationResponse.success(grant.getAuthorizationCode())
- .redirectUri(grant.getRedirectUri())
- .build()
- );
- return new OAuth2AuthorizationCodeGrantRequest(clientRegistration, authzExchange);
- }
-
- throw new UnsupportedOperationException("Unsupported grant type.");
- }
-
- private OAuth2AccessTokenResponseClient getClient(AbstractOAuth2AuthorizationGrantRequest request)
- {
- final AuthorizationGrantType grantType = request.getGrantType();
- final OAuth2AccessTokenResponseClient client = clients.get(grantType);
- if (client == null)
- {
- throw new UnsupportedOperationException("Unsupported grant type `" + grantType + "`.");
- }
- return client;
- }
-
- private static OAuth2AccessTokenResponseClient createAuthorizationCodeClient(
- RestOperations rest)
- {
- final DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
- client.setRestOperations(rest);
- return client;
- }
-
- private static OAuth2AccessTokenResponseClient createRefreshTokenClient(
- RestOperations rest)
- {
- final DefaultRefreshTokenTokenResponseClient client = new DefaultRefreshTokenTokenResponseClient();
- client.setRestOperations(rest);
- return client;
- }
-
- private static OAuth2AccessTokenResponseClient createPasswordClient(RestOperations rest,
- ClientRegistration clientRegistration)
- {
- final DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
- client.setRestOperations(rest);
- Optional.of(clientRegistration)
- .map(ClientRegistration::getProviderDetails)
- .map(ProviderDetails::getConfigurationMetadata)
- .map(metadata -> metadata.get(AUDIENCE.getValue()))
- .filter(String.class::isInstance)
- .map(String.class::cast)
- .ifPresent(audienceValue -> {
- final OAuth2PasswordGrantRequestEntityConverter requestEntityConverter = new OAuth2PasswordGrantRequestEntityConverter();
- requestEntityConverter.addParametersConverter(audienceParameterConverter(audienceValue));
- client.setRequestEntityConverter(requestEntityConverter);
- });
- return client;
- }
-
- private static Converter> audienceParameterConverter(
- String audienceValue)
- {
- return (grantRequest) -> {
- MultiValueMap parameters = new LinkedMultiValueMap<>();
- parameters.set("audience", audienceValue);
-
- return parameters;
- };
- }
-
- private static class SpringAccessTokenAuthorization implements AccessTokenAuthorization
- {
- private final OAuth2AccessTokenResponse tokenResponse;
-
- private SpringAccessTokenAuthorization(OAuth2AccessTokenResponse tokenResponse)
- {
- this.tokenResponse = requireNonNull(tokenResponse);
- }
-
- @Override
- public AccessToken getAccessToken()
- {
- return new SpringAccessToken(tokenResponse.getAccessToken());
- }
-
- @Override
- public String getRefreshTokenValue()
- {
- return Optional.of(tokenResponse)
- .map(OAuth2AccessTokenResponse::getRefreshToken)
- .map(AbstractOAuth2Token::getTokenValue)
- .orElse(null);
- }
- }
-
- private static class SpringAccessToken implements AccessToken
- {
- private final AbstractOAuth2Token token;
-
- private SpringAccessToken(AbstractOAuth2Token token)
- {
- this.token = requireNonNull(token);
- }
-
- @Override
- public String getTokenValue()
- {
- return token.getTokenValue();
- }
-
- @Override
- public Instant getExpiresAt()
- {
- return token.getExpiresAt();
- }
- }
-
- private static class SpringDecodedAccessToken extends SpringAccessToken implements DecodedAccessToken
- {
- private final Jwt jwt;
-
- private SpringDecodedAccessToken(Jwt jwt)
- {
- super(jwt);
- this.jwt = jwt;
- }
-
- @Override
- public Object getClaim(String claim)
- {
- return jwt.getClaim(claim);
- }
- }
-}
\ No newline at end of file
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2025 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.repo.security.authentication.identityservice;
+
+import static java.util.Objects.requireNonNull;
+
+import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceMetadataKey.AUDIENCE;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import com.nimbusds.oauth2.sdk.ErrorObject;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
+import com.nimbusds.openid.connect.sdk.UserInfoRequest;
+import com.nimbusds.openid.connect.sdk.UserInfoResponse;
+import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;
+import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.DefaultPasswordTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
+import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest;
+import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequestEntityConverter;
+import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
+import org.springframework.security.oauth2.core.AbstractOAuth2Token;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType;
+import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestOperations;
+
+class SpringBasedIdentityServiceFacade implements IdentityServiceFacade
+{
+ private static final Log LOGGER = LogFactory.getLog(SpringBasedIdentityServiceFacade.class);
+ private static final Instant SOME_INSIGNIFICANT_DATE_IN_THE_PAST = Instant.MIN.plusSeconds(12345);
+ private final Map clients;
+ private final ClientRegistration clientRegistration;
+ private final JwtDecoder jwtDecoder;
+
+ SpringBasedIdentityServiceFacade(RestOperations restOperations, ClientRegistration clientRegistration,
+ JwtDecoder jwtDecoder)
+ {
+ requireNonNull(restOperations);
+ this.clientRegistration = requireNonNull(clientRegistration);
+ this.jwtDecoder = requireNonNull(jwtDecoder);
+ this.clients = Map.of(
+ AuthorizationGrantType.AUTHORIZATION_CODE, createAuthorizationCodeClient(restOperations),
+ AuthorizationGrantType.REFRESH_TOKEN, createRefreshTokenClient(restOperations),
+ AuthorizationGrantType.PASSWORD, createPasswordClient(restOperations, clientRegistration));
+ }
+
+ @Override
+ public AccessTokenAuthorization authorize(AuthorizationGrant authorizationGrant)
+ {
+ final AbstractOAuth2AuthorizationGrantRequest request = createRequest(authorizationGrant);
+ final OAuth2AccessTokenResponseClient client = getClient(request);
+
+ final OAuth2AccessTokenResponse response;
+ try
+ {
+ response = client.getTokenResponse(request);
+ }
+ catch (OAuth2AuthorizationException e)
+ {
+ LOGGER.debug("Failed to authorize against Authorization Server. Reason: " + e.getError() + ".");
+ throw new AuthorizationException("Failed to obtain access token. " + e.getError(), e);
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.warn("Failed to authorize against Authorization Server. Reason: " + e.getMessage());
+ throw new AuthorizationException("Failed to obtain access token.", e);
+ }
+
+ return new SpringAccessTokenAuthorization(response);
+ }
+
+ @Override
+ public Optional getUserInfo(String tokenParameter, String principalAttribute)
+ {
+ return Optional.ofNullable(tokenParameter)
+ .filter(Predicate.not(String::isEmpty))
+ .flatMap(token -> Optional.ofNullable(clientRegistration)
+ .map(ClientRegistration::getProviderDetails)
+ .map(ClientRegistration.ProviderDetails::getUserInfoEndpoint)
+ .map(ClientRegistration.ProviderDetails.UserInfoEndpoint::getUri)
+ .flatMap(uri -> {
+ try
+ {
+ return Optional.of(
+ new UserInfoRequest(new URI(uri), new BearerAccessToken(token)).toHTTPRequest().send());
+ }
+ catch (IOException | URISyntaxException e)
+ {
+ LOGGER.warn("Failed to get user information. Reason: " + e.getMessage());
+ return Optional.empty();
+ }
+ })
+ .flatMap(httpResponse -> {
+ try
+ {
+ UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse);
+
+ if (userInfoResponse instanceof UserInfoErrorResponse userInfoErrorResponse)
+ {
+ String errorMessage = Optional.ofNullable(userInfoErrorResponse.getErrorObject())
+ .map(ErrorObject::getDescription)
+ .orElse("No error description found");
+ LOGGER.warn("User Info Request failed: " + errorMessage);
+ throw new UserInfoException(errorMessage);
+ }
+ return Optional.of(userInfoResponse);
+ }
+ catch (ParseException e)
+ {
+ LOGGER.warn("Failed to parse user info response. Reason: " + e.getMessage());
+ return Optional.empty();
+ }
+ })
+ .map(UserInfoResponse::toSuccessResponse)
+ .map(UserInfoSuccessResponse::getUserInfo))
+ .map(userInfo -> new OIDCUserInfo(userInfo.getStringClaim(principalAttribute), userInfo.getGivenName(),
+ userInfo.getFamilyName(), userInfo.getEmailAddress()));
+ }
+
+ @Override
+ public ClientRegistration getClientRegistration()
+ {
+ return clientRegistration;
+ }
+
+ @Override
+ public DecodedAccessToken decodeToken(String token)
+ {
+ final Jwt validToken;
+ try
+ {
+ validToken = jwtDecoder.decode(token);
+ }
+ catch (RuntimeException e)
+ {
+ throw new TokenDecodingException("Failed to decode token. " + e.getMessage(), e);
+ }
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Bearer token outcome: " + validToken.getClaims());
+ }
+ return new SpringDecodedAccessToken(validToken);
+ }
+
+ private AbstractOAuth2AuthorizationGrantRequest createRequest(AuthorizationGrant grant)
+ {
+ if (grant.isPassword())
+ {
+ return new OAuth2PasswordGrantRequest(clientRegistration, grant.getUsername(), grant.getPassword());
+ }
+
+ if (grant.isRefreshToken())
+ {
+ final OAuth2AccessToken expiredAccessToken = new OAuth2AccessToken(
+ TokenType.BEARER,
+ "JUST_FOR_FULFILLING_THE_SPRING_API",
+ SOME_INSIGNIFICANT_DATE_IN_THE_PAST,
+ SOME_INSIGNIFICANT_DATE_IN_THE_PAST.plusSeconds(1));
+ final OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(grant.getRefreshToken(), null);
+
+ return new OAuth2RefreshTokenGrantRequest(clientRegistration, expiredAccessToken, refreshToken,
+ clientRegistration.getScopes());
+ }
+
+ if (grant.isAuthorizationCode())
+ {
+ final OAuth2AuthorizationExchange authzExchange = new OAuth2AuthorizationExchange(
+ OAuth2AuthorizationRequest.authorizationCode()
+ .clientId(clientRegistration.getClientId())
+ .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
+ .redirectUri(grant.getRedirectUri())
+ .scopes(clientRegistration.getScopes())
+ .build(),
+ OAuth2AuthorizationResponse.success(grant.getAuthorizationCode())
+ .redirectUri(grant.getRedirectUri())
+ .build());
+ return new OAuth2AuthorizationCodeGrantRequest(clientRegistration, authzExchange);
+ }
+
+ throw new UnsupportedOperationException("Unsupported grant type.");
+ }
+
+ private OAuth2AccessTokenResponseClient getClient(AbstractOAuth2AuthorizationGrantRequest request)
+ {
+ final AuthorizationGrantType grantType = request.getGrantType();
+ final OAuth2AccessTokenResponseClient client = clients.get(grantType);
+ if (client == null)
+ {
+ throw new UnsupportedOperationException("Unsupported grant type `" + grantType + "`.");
+ }
+ return client;
+ }
+
+ private static OAuth2AccessTokenResponseClient createAuthorizationCodeClient(
+ RestOperations rest)
+ {
+ final DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
+ client.setRestOperations(rest);
+ return client;
+ }
+
+ private static OAuth2AccessTokenResponseClient createRefreshTokenClient(
+ RestOperations rest)
+ {
+ final DefaultRefreshTokenTokenResponseClient client = new DefaultRefreshTokenTokenResponseClient();
+ client.setRestOperations(rest);
+ return client;
+ }
+
+ private static OAuth2AccessTokenResponseClient createPasswordClient(RestOperations rest,
+ ClientRegistration clientRegistration)
+ {
+ final DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
+ client.setRestOperations(rest);
+ Optional.of(clientRegistration)
+ .map(ClientRegistration::getProviderDetails)
+ .map(ProviderDetails::getConfigurationMetadata)
+ .map(metadata -> metadata.get(AUDIENCE.getValue()))
+ .filter(String.class::isInstance)
+ .map(String.class::cast)
+ .ifPresent(audienceValue -> {
+ final OAuth2PasswordGrantRequestEntityConverter requestEntityConverter = new OAuth2PasswordGrantRequestEntityConverter();
+ requestEntityConverter.addParametersConverter(audienceParameterConverter(audienceValue));
+ client.setRequestEntityConverter(requestEntityConverter);
+ });
+ return client;
+ }
+
+ private static Converter> audienceParameterConverter(
+ String audienceValue)
+ {
+ return (grantRequest) -> {
+ MultiValueMap parameters = new LinkedMultiValueMap<>();
+ parameters.set("audience", audienceValue);
+
+ return parameters;
+ };
+ }
+
+ private static class SpringAccessTokenAuthorization implements AccessTokenAuthorization
+ {
+ private final OAuth2AccessTokenResponse tokenResponse;
+
+ private SpringAccessTokenAuthorization(OAuth2AccessTokenResponse tokenResponse)
+ {
+ this.tokenResponse = requireNonNull(tokenResponse);
+ }
+
+ @Override
+ public AccessToken getAccessToken()
+ {
+ return new SpringAccessToken(tokenResponse.getAccessToken());
+ }
+
+ @Override
+ public String getRefreshTokenValue()
+ {
+ return Optional.of(tokenResponse)
+ .map(OAuth2AccessTokenResponse::getRefreshToken)
+ .map(AbstractOAuth2Token::getTokenValue)
+ .orElse(null);
+ }
+ }
+
+ private static class SpringAccessToken implements AccessToken
+ {
+ private final AbstractOAuth2Token token;
+
+ private SpringAccessToken(AbstractOAuth2Token token)
+ {
+ this.token = requireNonNull(token);
+ }
+
+ @Override
+ public String getTokenValue()
+ {
+ return token.getTokenValue();
+ }
+
+ @Override
+ public Instant getExpiresAt()
+ {
+ return token.getExpiresAt();
+ }
+ }
+
+ private static class SpringDecodedAccessToken extends SpringAccessToken implements DecodedAccessToken
+ {
+ private final Jwt jwt;
+
+ private SpringDecodedAccessToken(Jwt jwt)
+ {
+ super(jwt);
+ this.jwt = jwt;
+ }
+
+ @Override
+ public Object getClaim(String claim)
+ {
+ return jwt.getClaim(claim);
+ }
+ }
+}