mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-14 06:13:30 +00:00
Polish spring-security-oauth2-client main code
Manually polish `spring-security-oauth-cleint` following the formatting and checkstyle fixes. Issue gh-8945
This commit is contained in:
parent
ad1dbf425f
commit
7a715f9086
@ -46,7 +46,6 @@ public final class AuthorizationCodeOAuth2AuthorizedClientProvider implements OA
|
|||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(
|
||||||
context.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {
|
context.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {
|
||||||
// ClientAuthorizationRequiredException is caught by
|
// ClientAuthorizationRequiredException is caught by
|
||||||
|
@ -47,7 +47,6 @@ public final class AuthorizationCodeReactiveOAuth2AuthorizedClientProvider
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(
|
||||||
context.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {
|
context.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {
|
||||||
// ClientAuthorizationRequiredException is caught by
|
// ClientAuthorizationRequiredException is caught by
|
||||||
|
@ -115,11 +115,9 @@ public final class AuthorizedClientServiceOAuth2AuthorizedClientManager implemen
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
||||||
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
||||||
|
|
||||||
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
||||||
OAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();
|
||||||
Authentication principal = authorizeRequest.getPrincipal();
|
Authentication principal = authorizeRequest.getPrincipal();
|
||||||
|
|
||||||
OAuth2AuthorizationContext.Builder contextBuilder;
|
OAuth2AuthorizationContext.Builder contextBuilder;
|
||||||
if (authorizedClient != null) {
|
if (authorizedClient != null) {
|
||||||
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
|
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
|
||||||
@ -138,14 +136,8 @@ public final class AuthorizedClientServiceOAuth2AuthorizedClientManager implemen
|
|||||||
contextBuilder = OAuth2AuthorizationContext.withClientRegistration(clientRegistration);
|
contextBuilder = OAuth2AuthorizationContext.withClientRegistration(clientRegistration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OAuth2AuthorizationContext authorizationContext = contextBuilder.principal(principal)
|
OAuth2AuthorizationContext authorizationContext = buildAuthorizationContext(authorizeRequest, principal,
|
||||||
.attributes((attributes) -> {
|
contextBuilder);
|
||||||
Map<String, Object> contextAttributes = this.contextAttributesMapper.apply(authorizeRequest);
|
|
||||||
if (!CollectionUtils.isEmpty(contextAttributes)) {
|
|
||||||
attributes.putAll(contextAttributes);
|
|
||||||
}
|
|
||||||
}).build();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);
|
authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);
|
||||||
}
|
}
|
||||||
@ -153,7 +145,6 @@ public final class AuthorizedClientServiceOAuth2AuthorizedClientManager implemen
|
|||||||
this.authorizationFailureHandler.onAuthorizationFailure(ex, principal, Collections.emptyMap());
|
this.authorizationFailureHandler.onAuthorizationFailure(ex, principal, Collections.emptyMap());
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null) {
|
if (authorizedClient != null) {
|
||||||
this.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,
|
this.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,
|
||||||
Collections.emptyMap());
|
Collections.emptyMap());
|
||||||
@ -167,10 +158,21 @@ public final class AuthorizedClientServiceOAuth2AuthorizedClientManager implemen
|
|||||||
return authorizationContext.getAuthorizedClient();
|
return authorizationContext.getAuthorizedClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authorizedClient;
|
return authorizedClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizationContext buildAuthorizationContext(OAuth2AuthorizeRequest authorizeRequest,
|
||||||
|
Authentication principal, OAuth2AuthorizationContext.Builder contextBuilder) {
|
||||||
|
OAuth2AuthorizationContext authorizationContext = contextBuilder.principal(principal)
|
||||||
|
.attributes((attributes) -> {
|
||||||
|
Map<String, Object> contextAttributes = this.contextAttributesMapper.apply(authorizeRequest);
|
||||||
|
if (!CollectionUtils.isEmpty(contextAttributes)) {
|
||||||
|
attributes.putAll(contextAttributes);
|
||||||
|
}
|
||||||
|
}).build();
|
||||||
|
return authorizationContext;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link OAuth2AuthorizedClientProvider} used for authorizing (or
|
* Sets the {@link OAuth2AuthorizedClientProvider} used for authorizing (or
|
||||||
* re-authorizing) an OAuth 2.0 Client.
|
* re-authorizing) an OAuth 2.0 Client.
|
||||||
|
@ -120,7 +120,6 @@ public final class AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
||||||
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
||||||
|
|
||||||
return createAuthorizationContext(authorizeRequest)
|
return createAuthorizationContext(authorizeRequest)
|
||||||
.flatMap((authorizationContext) -> authorize(authorizationContext, authorizeRequest.getPrincipal()));
|
.flatMap((authorizationContext) -> authorize(authorizationContext, authorizeRequest.getPrincipal()));
|
||||||
}
|
}
|
||||||
|
@ -64,39 +64,37 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
|
|||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
ClientRegistration clientRegistration = context.getClientRegistration();
|
ClientRegistration clientRegistration = context.getClientRegistration();
|
||||||
if (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
// If client is already authorized but access token is NOT expired than no
|
// If client is already authorized but access token is NOT expired than no
|
||||||
// need for re-authorization
|
// need for re-authorization
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// As per spec, in section 4.4.3 Access Token Response
|
// As per spec, in section 4.4.3 Access Token Response
|
||||||
// https://tools.ietf.org/html/rfc6749#section-4.4.3
|
// https://tools.ietf.org/html/rfc6749#section-4.4.3
|
||||||
// A refresh token SHOULD NOT be included.
|
// A refresh token SHOULD NOT be included.
|
||||||
//
|
//
|
||||||
// Therefore, renewing an expired access token (re-authorization)
|
// Therefore, renewing an expired access token (re-authorization)
|
||||||
// is the same as acquiring a new access token (authorization).
|
// is the same as acquiring a new access token (authorization).
|
||||||
|
|
||||||
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(
|
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(
|
||||||
clientRegistration);
|
clientRegistration);
|
||||||
|
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, clientCredentialsGrantRequest);
|
||||||
|
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
||||||
|
tokenResponse.getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse tokenResponse;
|
private OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,
|
||||||
|
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
||||||
try {
|
try {
|
||||||
tokenResponse = this.accessTokenResponseClient.getTokenResponse(clientCredentialsGrantRequest);
|
return this.accessTokenResponseClient.getTokenResponse(clientCredentialsGrantRequest);
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthorizationException ex) {
|
catch (OAuth2AuthorizationException ex) {
|
||||||
throw new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);
|
throw new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
|
||||||
tokenResponse.getAccessToken());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
||||||
|
@ -64,31 +64,27 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
ClientRegistration clientRegistration = context.getClientRegistration();
|
ClientRegistration clientRegistration = context.getClientRegistration();
|
||||||
if (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
// If client is already authorized but access token is NOT expired than no
|
// If client is already authorized but access token is NOT expired than no
|
||||||
// need for re-authorization
|
// need for re-authorization
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// As per spec, in section 4.4.3 Access Token Response
|
// As per spec, in section 4.4.3 Access Token Response
|
||||||
// https://tools.ietf.org/html/rfc6749#section-4.4.3
|
// https://tools.ietf.org/html/rfc6749#section-4.4.3
|
||||||
// A refresh token SHOULD NOT be included.
|
// A refresh token SHOULD NOT be included.
|
||||||
//
|
//
|
||||||
// Therefore, renewing an expired access token (re-authorization)
|
// Therefore, renewing an expired access token (re-authorization)
|
||||||
// is the same as acquiring a new access token (authorization).
|
// is the same as acquiring a new access token (authorization).
|
||||||
|
|
||||||
return Mono.just(new OAuth2ClientCredentialsGrantRequest(clientRegistration))
|
return Mono.just(new OAuth2ClientCredentialsGrantRequest(clientRegistration))
|
||||||
.flatMap(this.accessTokenResponseClient::getTokenResponse)
|
.flatMap(this.accessTokenResponseClient::getTokenResponse)
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(),
|
(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(),
|
||||||
e))
|
ex))
|
||||||
.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
||||||
tokenResponse.getAccessToken()));
|
tokenResponse.getAccessToken()));
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
*/
|
*/
|
||||||
public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
|
public JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
|
||||||
ClientRegistrationRepository clientRegistrationRepository) {
|
ClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
|
||||||
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
|
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
|
||||||
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||||
this.jdbcOperations = jdbcOperations;
|
this.jdbcOperations = jdbcOperations;
|
||||||
@ -113,15 +112,12 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
String principalName) {
|
String principalName) {
|
||||||
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
||||||
Assert.hasText(principalName, "principalName cannot be empty");
|
Assert.hasText(principalName, "principalName cannot be empty");
|
||||||
|
|
||||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||||
new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
||||||
new SqlParameterValue(Types.VARCHAR, principalName) };
|
new SqlParameterValue(Types.VARCHAR, principalName) };
|
||||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||||
|
|
||||||
List<OAuth2AuthorizedClient> result = this.jdbcOperations.query(LOAD_AUTHORIZED_CLIENT_SQL, pss,
|
List<OAuth2AuthorizedClient> result = this.jdbcOperations.query(LOAD_AUTHORIZED_CLIENT_SQL, pss,
|
||||||
this.authorizedClientRowMapper);
|
this.authorizedClientRowMapper);
|
||||||
|
|
||||||
return !result.isEmpty() ? (T) result.get(0) : null;
|
return !result.isEmpty() ? (T) result.get(0) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +125,8 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
||||||
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
|
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
|
||||||
Assert.notNull(principal, "principal cannot be null");
|
Assert.notNull(principal, "principal cannot be null");
|
||||||
|
|
||||||
boolean existsAuthorizedClient = null != this.loadAuthorizedClient(
|
boolean existsAuthorizedClient = null != this.loadAuthorizedClient(
|
||||||
authorizedClient.getClientRegistration().getRegistrationId(), principal.getName());
|
authorizedClient.getClientRegistration().getRegistrationId(), principal.getName());
|
||||||
|
|
||||||
if (existsAuthorizedClient) {
|
if (existsAuthorizedClient) {
|
||||||
updateAuthorizedClient(authorizedClient, principal);
|
updateAuthorizedClient(authorizedClient, principal);
|
||||||
}
|
}
|
||||||
@ -149,14 +143,11 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
private void updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
private void updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
||||||
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
|
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
|
||||||
.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
|
.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
|
||||||
|
|
||||||
SqlParameterValue clientRegistrationIdParameter = parameters.remove(0);
|
SqlParameterValue clientRegistrationIdParameter = parameters.remove(0);
|
||||||
SqlParameterValue principalNameParameter = parameters.remove(0);
|
SqlParameterValue principalNameParameter = parameters.remove(0);
|
||||||
parameters.add(clientRegistrationIdParameter);
|
parameters.add(clientRegistrationIdParameter);
|
||||||
parameters.add(principalNameParameter);
|
parameters.add(principalNameParameter);
|
||||||
|
|
||||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
||||||
|
|
||||||
this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
|
this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +155,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
|
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper
|
||||||
.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
|
.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));
|
||||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
||||||
|
|
||||||
this.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);
|
this.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,12 +162,10 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
public void removeAuthorizedClient(String clientRegistrationId, String principalName) {
|
public void removeAuthorizedClient(String clientRegistrationId, String principalName) {
|
||||||
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
||||||
Assert.hasText(principalName, "principalName cannot be empty");
|
Assert.hasText(principalName, "principalName cannot be empty");
|
||||||
|
|
||||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||||
new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
||||||
new SqlParameterValue(Types.VARCHAR, principalName) };
|
new SqlParameterValue(Types.VARCHAR, principalName) };
|
||||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||||
|
|
||||||
this.jdbcOperations.update(REMOVE_AUTHORIZED_CLIENT_SQL, pss);
|
this.jdbcOperations.update(REMOVE_AUTHORIZED_CLIENT_SQL, pss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +217,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
"The ClientRegistration with id '" + clientRegistrationId + "' exists in the data source, "
|
"The ClientRegistration with id '" + clientRegistrationId + "' exists in the data source, "
|
||||||
+ "however, it was not found in the ClientRegistrationRepository.");
|
+ "however, it was not found in the ClientRegistrationRepository.");
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AccessToken.TokenType tokenType = null;
|
OAuth2AccessToken.TokenType tokenType = null;
|
||||||
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) {
|
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) {
|
||||||
tokenType = OAuth2AccessToken.TokenType.BEARER;
|
tokenType = OAuth2AccessToken.TokenType.BEARER;
|
||||||
@ -243,7 +230,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);
|
scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);
|
||||||
}
|
}
|
||||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt, scopes);
|
OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt, scopes);
|
||||||
|
|
||||||
OAuth2RefreshToken refreshToken = null;
|
OAuth2RefreshToken refreshToken = null;
|
||||||
byte[] refreshTokenValue = rs.getBytes("refresh_token_value");
|
byte[] refreshTokenValue = rs.getBytes("refresh_token_value");
|
||||||
if (refreshTokenValue != null) {
|
if (refreshTokenValue != null) {
|
||||||
@ -255,9 +241,7 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
}
|
}
|
||||||
refreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);
|
refreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
String principalName = rs.getString("principal_name");
|
String principalName = rs.getString("principal_name");
|
||||||
|
|
||||||
return new OAuth2AuthorizedClient(clientRegistration, principalName, accessToken, refreshToken);
|
return new OAuth2AuthorizedClient(clientRegistration, principalName, accessToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +261,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
||||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||||
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();
|
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();
|
||||||
|
|
||||||
List<SqlParameterValue> parameters = new ArrayList<>();
|
List<SqlParameterValue> parameters = new ArrayList<>();
|
||||||
parameters.add(new SqlParameterValue(Types.VARCHAR, clientRegistration.getRegistrationId()));
|
parameters.add(new SqlParameterValue(Types.VARCHAR, clientRegistration.getRegistrationId()));
|
||||||
parameters.add(new SqlParameterValue(Types.VARCHAR, principal.getName()));
|
parameters.add(new SqlParameterValue(Types.VARCHAR, principal.getName()));
|
||||||
@ -301,7 +284,6 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
|
|||||||
}
|
}
|
||||||
parameters.add(new SqlParameterValue(Types.BLOB, refreshTokenValue));
|
parameters.add(new SqlParameterValue(Types.BLOB, refreshTokenValue));
|
||||||
parameters.add(new SqlParameterValue(Types.TIMESTAMP, refreshTokenIssuedAt));
|
parameters.add(new SqlParameterValue(Types.TIMESTAMP, refreshTokenIssuedAt));
|
||||||
|
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +157,8 @@ public final class OAuth2AuthorizeRequest {
|
|||||||
|
|
||||||
private static Authentication createAuthentication(final String principalName) {
|
private static Authentication createAuthentication(final String principalName) {
|
||||||
Assert.hasText(principalName, "principalName cannot be empty");
|
Assert.hasText(principalName, "principalName cannot be empty");
|
||||||
|
|
||||||
return new AbstractAuthenticationToken(null) {
|
return new AbstractAuthenticationToken(null) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public Object getCredentials() {
|
||||||
return "";
|
return "";
|
||||||
@ -168,6 +168,7 @@ public final class OAuth2AuthorizeRequest {
|
|||||||
public Object getPrincipal() {
|
public Object getPrincipal() {
|
||||||
return principalName;
|
return principalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,48 +77,43 @@ public final class PasswordOAuth2AuthorizedClientProvider implements OAuth2Autho
|
|||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
ClientRegistration clientRegistration = context.getClientRegistration();
|
ClientRegistration clientRegistration = context.getClientRegistration();
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
|
|
||||||
if (!AuthorizationGrantType.PASSWORD.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (!AuthorizationGrantType.PASSWORD.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String username = context.getAttribute(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME);
|
String username = context.getAttribute(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME);
|
||||||
String password = context.getAttribute(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME);
|
String password = context.getAttribute(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME);
|
||||||
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
|
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
// If client is already authorized and access token is NOT expired than no
|
// If client is already authorized and access token is NOT expired than no
|
||||||
// need for re-authorization
|
// need for re-authorization
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null && hasTokenExpired(authorizedClient.getAccessToken())
|
if (authorizedClient != null && hasTokenExpired(authorizedClient.getAccessToken())
|
||||||
&& authorizedClient.getRefreshToken() != null) {
|
&& authorizedClient.getRefreshToken() != null) {
|
||||||
// If client is already authorized and access token is expired and a refresh
|
// If client is already authorized and access token is expired and a refresh
|
||||||
// token is available,
|
// token is available, than return and allow
|
||||||
// than return and allow RefreshTokenOAuth2AuthorizedClientProvider to handle
|
// RefreshTokenOAuth2AuthorizedClientProvider to handle the refresh
|
||||||
// the refresh
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, username,
|
OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, username,
|
||||||
password);
|
password);
|
||||||
|
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, passwordGrantRequest);
|
||||||
|
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
||||||
|
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse tokenResponse;
|
private OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,
|
||||||
|
OAuth2PasswordGrantRequest passwordGrantRequest) {
|
||||||
try {
|
try {
|
||||||
tokenResponse = this.accessTokenResponseClient.getTokenResponse(passwordGrantRequest);
|
return this.accessTokenResponseClient.getTokenResponse(passwordGrantRequest);
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthorizationException ex) {
|
catch (OAuth2AuthorizationException ex) {
|
||||||
throw new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);
|
throw new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
|
||||||
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
||||||
|
@ -77,26 +77,21 @@ public final class PasswordReactiveOAuth2AuthorizedClientProvider implements Rea
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
ClientRegistration clientRegistration = context.getClientRegistration();
|
ClientRegistration clientRegistration = context.getClientRegistration();
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
|
|
||||||
if (!AuthorizationGrantType.PASSWORD.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (!AuthorizationGrantType.PASSWORD.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String username = context.getAttribute(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME);
|
String username = context.getAttribute(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME);
|
||||||
String password = context.getAttribute(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME);
|
String password = context.getAttribute(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME);
|
||||||
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
|
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
// If client is already authorized and access token is NOT expired than no
|
// If client is already authorized and access token is NOT expired than no
|
||||||
// need for re-authorization
|
// need for re-authorization
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null && hasTokenExpired(authorizedClient.getAccessToken())
|
if (authorizedClient != null && hasTokenExpired(authorizedClient.getAccessToken())
|
||||||
&& authorizedClient.getRefreshToken() != null) {
|
&& authorizedClient.getRefreshToken() != null) {
|
||||||
// If client is already authorized and access token is expired and a refresh
|
// If client is already authorized and access token is expired and a refresh
|
||||||
@ -105,10 +100,8 @@ public final class PasswordReactiveOAuth2AuthorizedClientProvider implements Rea
|
|||||||
// handle the refresh
|
// handle the refresh
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, username,
|
OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, username,
|
||||||
password);
|
password);
|
||||||
|
|
||||||
return Mono.just(passwordGrantRequest).flatMap(this.accessTokenResponseClient::getTokenResponse)
|
return Mono.just(passwordGrantRequest).flatMap(this.accessTokenResponseClient::getTokenResponse)
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(),
|
(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(),
|
||||||
|
@ -75,13 +75,11 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
|
|||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
if (authorizedClient == null || authorizedClient.getRefreshToken() == null
|
if (authorizedClient == null || authorizedClient.getRefreshToken() == null
|
||||||
|| !hasTokenExpired(authorizedClient.getAccessToken())) {
|
|| !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);
|
Object requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);
|
||||||
Set<String> scopes = Collections.emptySet();
|
Set<String> scopes = Collections.emptySet();
|
||||||
if (requestScope != null) {
|
if (requestScope != null) {
|
||||||
@ -89,22 +87,23 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
|
|||||||
+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + "'");
|
+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + "'");
|
||||||
scopes = new HashSet<>(Arrays.asList((String[]) requestScope));
|
scopes = new HashSet<>(Arrays.asList((String[]) requestScope));
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(
|
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(
|
||||||
authorizedClient.getClientRegistration(), authorizedClient.getAccessToken(),
|
authorizedClient.getClientRegistration(), authorizedClient.getAccessToken(),
|
||||||
authorizedClient.getRefreshToken(), scopes);
|
authorizedClient.getRefreshToken(), scopes);
|
||||||
|
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(authorizedClient, refreshTokenGrantRequest);
|
||||||
|
return new OAuth2AuthorizedClient(context.getAuthorizedClient().getClientRegistration(),
|
||||||
|
context.getPrincipal().getName(), tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse tokenResponse;
|
private OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizedClient authorizedClient,
|
||||||
|
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
||||||
try {
|
try {
|
||||||
tokenResponse = this.accessTokenResponseClient.getTokenResponse(refreshTokenGrantRequest);
|
return this.accessTokenResponseClient.getTokenResponse(refreshTokenGrantRequest);
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthorizationException ex) {
|
catch (OAuth2AuthorizationException ex) {
|
||||||
throw new ClientAuthorizationException(ex.getError(),
|
throw new ClientAuthorizationException(ex.getError(),
|
||||||
authorizedClient.getClientRegistration().getRegistrationId(), ex);
|
authorizedClient.getClientRegistration().getRegistrationId(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OAuth2AuthorizedClient(context.getAuthorizedClient().getClientRegistration(),
|
|
||||||
context.getPrincipal().getName(), tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
private boolean hasTokenExpired(AbstractOAuth2Token token) {
|
||||||
|
@ -77,13 +77,11 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
|
||||||
Assert.notNull(context, "context cannot be null");
|
Assert.notNull(context, "context cannot be null");
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
|
||||||
if (authorizedClient == null || authorizedClient.getRefreshToken() == null
|
if (authorizedClient == null || authorizedClient.getRefreshToken() == null
|
||||||
|| !hasTokenExpired(authorizedClient.getAccessToken())) {
|
|| !hasTokenExpired(authorizedClient.getAccessToken())) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Object requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);
|
Object requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);
|
||||||
Set<String> scopes = Collections.emptySet();
|
Set<String> scopes = Collections.emptySet();
|
||||||
if (requestScope != null) {
|
if (requestScope != null) {
|
||||||
@ -92,10 +90,8 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider
|
|||||||
scopes = new HashSet<>(Arrays.asList((String[]) requestScope));
|
scopes = new HashSet<>(Arrays.asList((String[]) requestScope));
|
||||||
}
|
}
|
||||||
ClientRegistration clientRegistration = context.getClientRegistration();
|
ClientRegistration clientRegistration = context.getClientRegistration();
|
||||||
|
|
||||||
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,
|
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,
|
||||||
authorizedClient.getAccessToken(), authorizedClient.getRefreshToken(), scopes);
|
authorizedClient.getAccessToken(), authorizedClient.getRefreshToken(), scopes);
|
||||||
|
|
||||||
return Mono.just(refreshTokenGrantRequest).flatMap(this.accessTokenResponseClient::getTokenResponse)
|
return Mono.just(refreshTokenGrantRequest).flatMap(this.accessTokenResponseClient::getTokenResponse)
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(),
|
(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(),
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.oauth2.client;
|
package org.springframework.security.oauth2.client;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -47,26 +47,20 @@ public class RemoveAuthorizedClientOAuth2AuthorizationFailureHandler implements
|
|||||||
* {@link OAuth2AuthorizedClient}.
|
* {@link OAuth2AuthorizedClient}.
|
||||||
* @see OAuth2ErrorCodes
|
* @see OAuth2ErrorCodes
|
||||||
*/
|
*/
|
||||||
public static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections
|
public static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES;
|
||||||
.unmodifiableSet(new HashSet<>(Arrays.asList(
|
static {
|
||||||
/*
|
Set<String> codes = new LinkedHashSet<>();
|
||||||
* Returned from Resource Servers when an access token provided is
|
// Returned from Resource Servers when an access token provided is expired,
|
||||||
* expired, revoked, malformed, or invalid for other reasons.
|
// revoked, malformed, or invalid for other reasons. Note that this is needed
|
||||||
*
|
// because ServletOAuth2AuthorizedClientExchangeFilterFunction delegates this type
|
||||||
* Note that this is needed because
|
// of failure received from a Resource Server to this failure handler.
|
||||||
* ServletOAuth2AuthorizedClientExchangeFilterFunction delegates this
|
codes.add(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||||
* type of failure received from a Resource Server to this failure
|
// Returned from Authorization Servers when the authorization grant or refresh
|
||||||
* handler.
|
// token is invalid, expired, revoked, does not match the redirection URI used in
|
||||||
*/
|
// the authorization request, or was issued to another client.
|
||||||
OAuth2ErrorCodes.INVALID_TOKEN,
|
codes.add(OAuth2ErrorCodes.INVALID_GRANT);
|
||||||
|
DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections.unmodifiableSet(codes);
|
||||||
/*
|
}
|
||||||
* Returned from Authorization Servers when the authorization grant or
|
|
||||||
* refresh token is invalid, expired, revoked, does not match the
|
|
||||||
* redirection URI used in the authorization request, or was issued to
|
|
||||||
* another client.
|
|
||||||
*/
|
|
||||||
OAuth2ErrorCodes.INVALID_GRANT)));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OAuth 2.0 error codes which will trigger removal of an
|
* The OAuth 2.0 error codes which will trigger removal of an
|
||||||
@ -116,10 +110,8 @@ public class RemoveAuthorizedClientOAuth2AuthorizationFailureHandler implements
|
|||||||
@Override
|
@Override
|
||||||
public void onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal,
|
public void onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal,
|
||||||
Map<String, Object> attributes) {
|
Map<String, Object> attributes) {
|
||||||
|
|
||||||
if (authorizationException instanceof ClientAuthorizationException
|
if (authorizationException instanceof ClientAuthorizationException
|
||||||
&& hasRemovalErrorCode(authorizationException)) {
|
&& hasRemovalErrorCode(authorizationException)) {
|
||||||
|
|
||||||
ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException;
|
ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException;
|
||||||
this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(), principal,
|
this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(), principal,
|
||||||
attributes);
|
attributes);
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.oauth2.client;
|
package org.springframework.security.oauth2.client;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -47,24 +47,20 @@ public class RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler
|
|||||||
* client.
|
* client.
|
||||||
* @see OAuth2ErrorCodes
|
* @see OAuth2ErrorCodes
|
||||||
*/
|
*/
|
||||||
public static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections
|
public static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES;
|
||||||
.unmodifiableSet(new HashSet<>(Arrays.asList(
|
static {
|
||||||
/*
|
Set<String> codes = new LinkedHashSet<>();
|
||||||
* Returned from resource servers when an access token provided is
|
// Returned from resource servers when an access token provided is expired,
|
||||||
* expired, revoked, malformed, or invalid for other reasons.
|
// revoked, malformed, or invalid for other reasons. Note that this is needed
|
||||||
*
|
// because the ServerOAuth2AuthorizedClientExchangeFilterFunction delegates this
|
||||||
* Note that this is needed because the
|
// type of failure received from a resource server to this failure handler.
|
||||||
* ServerOAuth2AuthorizedClientExchangeFilterFunction delegates this
|
codes.add(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||||
* type of failure received from a resource server to this failure
|
// Returned from authorization servers when a refresh token is invalid, expired,
|
||||||
* handler.
|
// revoked, does not match the redirection URI used in the authorization request,
|
||||||
*/
|
// or was issued to another client.
|
||||||
OAuth2ErrorCodes.INVALID_TOKEN,
|
codes.add(OAuth2ErrorCodes.INVALID_GRANT);
|
||||||
/*
|
DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections.unmodifiableSet(codes);
|
||||||
* Returned from authorization servers when a refresh token is
|
}
|
||||||
* invalid, expired, revoked, does not match the redirection URI used
|
|
||||||
* in the authorization request, or was issued to another client.
|
|
||||||
*/
|
|
||||||
OAuth2ErrorCodes.INVALID_GRANT)));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A delegate that removes an {@link OAuth2AuthorizedClient} from a
|
* A delegate that removes an {@link OAuth2AuthorizedClient} from a
|
||||||
@ -116,17 +112,13 @@ public class RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> onAuthorizationFailure(OAuth2AuthorizationException authorizationException,
|
public Mono<Void> onAuthorizationFailure(OAuth2AuthorizationException authorizationException,
|
||||||
Authentication principal, Map<String, Object> attributes) {
|
Authentication principal, Map<String, Object> attributes) {
|
||||||
|
|
||||||
if (authorizationException instanceof ClientAuthorizationException
|
if (authorizationException instanceof ClientAuthorizationException
|
||||||
&& hasRemovalErrorCode(authorizationException)) {
|
&& hasRemovalErrorCode(authorizationException)) {
|
||||||
|
|
||||||
ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException;
|
ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException;
|
||||||
return this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(),
|
return this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(),
|
||||||
principal, attributes);
|
principal, attributes);
|
||||||
}
|
}
|
||||||
else {
|
return Mono.empty();
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,6 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
|
|||||||
*/
|
*/
|
||||||
public OAuth2AuthorizationCodeAuthenticationProvider(
|
public OAuth2AuthorizationCodeAuthenticationProvider(
|
||||||
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {
|
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {
|
||||||
|
|
||||||
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
|
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
|
||||||
this.accessTokenResponseClient = accessTokenResponseClient;
|
this.accessTokenResponseClient = accessTokenResponseClient;
|
||||||
}
|
}
|
||||||
@ -72,30 +71,25 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
|
|||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||||
|
|
||||||
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()
|
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()
|
||||||
.getAuthorizationResponse();
|
.getAuthorizationResponse();
|
||||||
if (authorizationResponse.statusError()) {
|
if (authorizationResponse.statusError()) {
|
||||||
throw new OAuth2AuthorizationException(authorizationResponse.getError());
|
throw new OAuth2AuthorizationException(authorizationResponse.getError());
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
||||||
.getAuthorizationRequest();
|
.getAuthorizationRequest();
|
||||||
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
||||||
throw new OAuth2AuthorizationException(oauth2Error);
|
throw new OAuth2AuthorizationException(oauth2Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(
|
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(
|
||||||
new OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),
|
new OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange()));
|
authorizationCodeAuthentication.getAuthorizationExchange()));
|
||||||
|
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authenticationResult = new OAuth2AuthorizationCodeAuthenticationToken(
|
OAuth2AuthorizationCodeAuthenticationToken authenticationResult = new OAuth2AuthorizationCodeAuthenticationToken(
|
||||||
authorizationCodeAuthentication.getClientRegistration(),
|
authorizationCodeAuthentication.getClientRegistration(),
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange(), accessTokenResponse.getAccessToken(),
|
authorizationCodeAuthentication.getAuthorizationExchange(), accessTokenResponse.getAccessToken(),
|
||||||
accessTokenResponse.getRefreshToken(), accessTokenResponse.getAdditionalParameters());
|
accessTokenResponse.getRefreshToken(), accessTokenResponse.getAdditionalParameters());
|
||||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||||
|
|
||||||
return authenticationResult;
|
return authenticationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,23 +84,19 @@ public class OAuth2AuthorizationCodeReactiveAuthenticationManager implements Rea
|
|||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
OAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
OAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||||
|
|
||||||
OAuth2AuthorizationResponse authorizationResponse = token.getAuthorizationExchange()
|
OAuth2AuthorizationResponse authorizationResponse = token.getAuthorizationExchange()
|
||||||
.getAuthorizationResponse();
|
.getAuthorizationResponse();
|
||||||
if (authorizationResponse.statusError()) {
|
if (authorizationResponse.statusError()) {
|
||||||
return Mono.error(new OAuth2AuthorizationException(authorizationResponse.getError()));
|
return Mono.error(new OAuth2AuthorizationException(authorizationResponse.getError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = token.getAuthorizationExchange()
|
OAuth2AuthorizationRequest authorizationRequest = token.getAuthorizationExchange()
|
||||||
.getAuthorizationRequest();
|
.getAuthorizationRequest();
|
||||||
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
||||||
return Mono.error(new OAuth2AuthorizationException(oauth2Error));
|
return Mono.error(new OAuth2AuthorizationException(oauth2Error));
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(
|
OAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(
|
||||||
token.getClientRegistration(), token.getAuthorizationExchange());
|
token.getClientRegistration(), token.getAuthorizationExchange());
|
||||||
|
|
||||||
return this.accessTokenResponseClient.getTokenResponse(authzRequest).map(onSuccess(token));
|
return this.accessTokenResponseClient.getTokenResponse(authzRequest).map(onSuccess(token));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||||||
public OAuth2LoginAuthenticationProvider(
|
public OAuth2LoginAuthenticationProvider(
|
||||||
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
|
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
|
||||||
OAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {
|
OAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {
|
||||||
|
|
||||||
Assert.notNull(userService, "userService cannot be null");
|
Assert.notNull(userService, "userService cannot be null");
|
||||||
this.authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
|
this.authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
|
||||||
accessTokenResponseClient);
|
accessTokenResponseClient);
|
||||||
@ -93,10 +92,8 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
OAuth2LoginAuthenticationToken loginAuthenticationToken = (OAuth2LoginAuthenticationToken) authentication;
|
OAuth2LoginAuthenticationToken loginAuthenticationToken = (OAuth2LoginAuthenticationToken) authentication;
|
||||||
|
|
||||||
// Section 3.1.2.1 Authentication Request -
|
// Section 3.1.2.1 Authentication Request -
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope
|
||||||
// scope
|
|
||||||
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
|
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
|
||||||
if (loginAuthenticationToken.getAuthorizationExchange().getAuthorizationRequest().getScopes()
|
if (loginAuthenticationToken.getAuthorizationExchange().getAuthorizationRequest().getScopes()
|
||||||
.contains("openid")) {
|
.contains("openid")) {
|
||||||
@ -104,7 +101,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||||||
// and let OidcAuthorizationCodeAuthenticationProvider handle it instead
|
// and let OidcAuthorizationCodeAuthenticationProvider handle it instead
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;
|
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;
|
||||||
try {
|
try {
|
||||||
authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider
|
authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider
|
||||||
@ -116,21 +112,16 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||||||
OAuth2Error oauth2Error = ex.getError();
|
OAuth2Error oauth2Error = ex.getError();
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();
|
OAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();
|
||||||
Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
|
Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
|
||||||
|
|
||||||
OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
|
OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
|
||||||
loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
|
loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
|
||||||
|
|
||||||
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
||||||
.mapAuthorities(oauth2User.getAuthorities());
|
.mapAuthorities(oauth2User.getAuthorities());
|
||||||
|
|
||||||
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
||||||
loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
|
loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
|
||||||
oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
|
oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
|
||||||
authenticationResult.setDetails(loginAuthenticationToken.getDetails());
|
authenticationResult.setDetails(loginAuthenticationToken.getDetails());
|
||||||
|
|
||||||
return authenticationResult;
|
return authenticationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,6 @@ public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
*/
|
*/
|
||||||
public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
|
public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
|
||||||
OAuth2AuthorizationExchange authorizationExchange) {
|
OAuth2AuthorizationExchange authorizationExchange) {
|
||||||
|
|
||||||
super(Collections.emptyList());
|
super(Collections.emptyList());
|
||||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||||
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
||||||
|
@ -88,18 +88,15 @@ public class OAuth2LoginReactiveAuthenticationManager implements ReactiveAuthent
|
|||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
OAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
OAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||||
|
|
||||||
// Section 3.1.2.1 Authentication Request -
|
// Section 3.1.2.1 Authentication Request -
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope
|
||||||
// scope REQUIRED. OpenID Connect requests MUST contain the "openid" scope
|
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
|
||||||
// value.
|
|
||||||
if (token.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) {
|
if (token.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) {
|
||||||
// This is an OpenID Connect Authentication Request so return null
|
// This is an OpenID Connect Authentication Request so return null
|
||||||
// and let OidcAuthorizationCodeReactiveAuthenticationManager handle it
|
// and let OidcAuthorizationCodeReactiveAuthenticationManager handle it
|
||||||
// instead once one is created
|
// instead once one is created
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.authorizationCodeManager.authenticate(token)
|
return this.authorizationCodeManager.authenticate(token)
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString()))
|
(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString()))
|
||||||
@ -128,7 +125,6 @@ public class OAuth2LoginReactiveAuthenticationManager implements ReactiveAuthent
|
|||||||
return this.userService.loadUser(userRequest).map((oauth2User) -> {
|
return this.userService.loadUser(userRequest).map((oauth2User) -> {
|
||||||
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
||||||
.mapAuthorities(oauth2User.getAuthorities());
|
.mapAuthorities(oauth2User.getAuthorities());
|
||||||
|
|
||||||
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
||||||
authentication.getClientRegistration(), authentication.getAuthorizationExchange(), oauth2User,
|
authentication.getClientRegistration(), authentication.getAuthorizationExchange(), oauth2User,
|
||||||
mappedAuthorities, accessToken, authentication.getRefreshToken());
|
mappedAuthorities, accessToken, authentication.getRefreshToken());
|
||||||
|
@ -74,23 +74,9 @@ public final class DefaultAuthorizationCodeTokenResponseClient
|
|||||||
public OAuth2AccessTokenResponse getTokenResponse(
|
public OAuth2AccessTokenResponse getTokenResponse(
|
||||||
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
||||||
Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null");
|
Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null");
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest);
|
||||||
|
ResponseEntity<OAuth2AccessTokenResponse> response = getResponse(request);
|
||||||
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();
|
OAuth2AccessTokenResponse tokenResponse = response.getBody();
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
||||||
// As per spec, in Section 5.1 Successful Access Token Response
|
// As per spec, in Section 5.1 Successful Access Token Response
|
||||||
// https://tools.ietf.org/html/rfc6749#section-5.1
|
// https://tools.ietf.org/html/rfc6749#section-5.1
|
||||||
@ -99,10 +85,22 @@ public final class DefaultAuthorizationCodeTokenResponseClient
|
|||||||
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
||||||
.scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes()).build();
|
.scopes(authorizationCodeGrantRequest.getClientRegistration().getScopes()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<OAuth2AccessTokenResponse> getResponse(RequestEntity<?> request) {
|
||||||
|
try {
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link Converter} used for converting the
|
* Sets the {@link Converter} used for converting the
|
||||||
* {@link OAuth2AuthorizationCodeGrantRequest} to a {@link RequestEntity}
|
* {@link OAuth2AuthorizationCodeGrantRequest} to a {@link RequestEntity}
|
||||||
|
@ -74,23 +74,9 @@ public final class DefaultClientCredentialsTokenResponseClient
|
|||||||
public OAuth2AccessTokenResponse getTokenResponse(
|
public OAuth2AccessTokenResponse getTokenResponse(
|
||||||
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
||||||
Assert.notNull(clientCredentialsGrantRequest, "clientCredentialsGrantRequest cannot be null");
|
Assert.notNull(clientCredentialsGrantRequest, "clientCredentialsGrantRequest cannot be null");
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(clientCredentialsGrantRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(clientCredentialsGrantRequest);
|
||||||
|
ResponseEntity<OAuth2AccessTokenResponse> response = getResponse(request);
|
||||||
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();
|
OAuth2AccessTokenResponse tokenResponse = response.getBody();
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
||||||
// As per spec, in Section 5.1 Successful Access Token Response
|
// As per spec, in Section 5.1 Successful Access Token Response
|
||||||
// https://tools.ietf.org/html/rfc6749#section-5.1
|
// https://tools.ietf.org/html/rfc6749#section-5.1
|
||||||
@ -99,10 +85,22 @@ public final class DefaultClientCredentialsTokenResponseClient
|
|||||||
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
||||||
.scopes(clientCredentialsGrantRequest.getClientRegistration().getScopes()).build();
|
.scopes(clientCredentialsGrantRequest.getClientRegistration().getScopes()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<OAuth2AccessTokenResponse> getResponse(RequestEntity<?> request) {
|
||||||
|
try {
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link Converter} used for converting the
|
* Sets the {@link Converter} used for converting the
|
||||||
* {@link OAuth2ClientCredentialsGrantRequest} to a {@link RequestEntity}
|
* {@link OAuth2ClientCredentialsGrantRequest} to a {@link RequestEntity}
|
||||||
|
@ -73,23 +73,9 @@ public final class DefaultPasswordTokenResponseClient
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenResponse getTokenResponse(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
public OAuth2AccessTokenResponse getTokenResponse(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
||||||
Assert.notNull(passwordGrantRequest, "passwordGrantRequest cannot be null");
|
Assert.notNull(passwordGrantRequest, "passwordGrantRequest cannot be null");
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(passwordGrantRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(passwordGrantRequest);
|
||||||
|
ResponseEntity<OAuth2AccessTokenResponse> response = getResponse(request);
|
||||||
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();
|
OAuth2AccessTokenResponse tokenResponse = response.getBody();
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
|
||||||
// As per spec, in Section 5.1 Successful Access Token Response
|
// As per spec, in Section 5.1 Successful Access Token Response
|
||||||
// https://tools.ietf.org/html/rfc6749#section-5.1
|
// https://tools.ietf.org/html/rfc6749#section-5.1
|
||||||
@ -98,10 +84,22 @@ public final class DefaultPasswordTokenResponseClient
|
|||||||
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)
|
||||||
.scopes(passwordGrantRequest.getClientRegistration().getScopes()).build();
|
.scopes(passwordGrantRequest.getClientRegistration().getScopes()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<OAuth2AccessTokenResponse> getResponse(RequestEntity<?> request) {
|
||||||
|
try {
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link Converter} used for converting the
|
* Sets the {@link Converter} used for converting the
|
||||||
* {@link OAuth2PasswordGrantRequest} to a {@link RequestEntity} representation of the
|
* {@link OAuth2PasswordGrantRequest} to a {@link RequestEntity} representation of the
|
||||||
|
@ -69,12 +69,32 @@ public final class DefaultRefreshTokenTokenResponseClient
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenResponse getTokenResponse(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
public OAuth2AccessTokenResponse getTokenResponse(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
||||||
Assert.notNull(refreshTokenGrantRequest, "refreshTokenGrantRequest cannot be null");
|
Assert.notNull(refreshTokenGrantRequest, "refreshTokenGrantRequest cannot be null");
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(refreshTokenGrantRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(refreshTokenGrantRequest);
|
||||||
|
ResponseEntity<OAuth2AccessTokenResponse> response = getResponse(request);
|
||||||
|
OAuth2AccessTokenResponse tokenResponse = response.getBody();
|
||||||
|
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())
|
||||||
|
|| tokenResponse.getRefreshToken() == null) {
|
||||||
|
OAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse
|
||||||
|
.withResponse(tokenResponse);
|
||||||
|
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
|
||||||
|
tokenResponseBuilder.scopes(refreshTokenGrantRequest.getAccessToken().getScopes());
|
||||||
|
}
|
||||||
|
if (tokenResponse.getRefreshToken() == null) {
|
||||||
|
// Reuse existing refresh token
|
||||||
|
tokenResponseBuilder.refreshToken(refreshTokenGrantRequest.getRefreshToken().getTokenValue());
|
||||||
|
}
|
||||||
|
tokenResponse = tokenResponseBuilder.build();
|
||||||
|
}
|
||||||
|
return tokenResponse;
|
||||||
|
}
|
||||||
|
|
||||||
ResponseEntity<OAuth2AccessTokenResponse> response;
|
private ResponseEntity<OAuth2AccessTokenResponse> getResponse(RequestEntity<?> request) {
|
||||||
try {
|
try {
|
||||||
response = this.restOperations.exchange(request, OAuth2AccessTokenResponse.class);
|
return this.restOperations.exchange(request, OAuth2AccessTokenResponse.class);
|
||||||
}
|
}
|
||||||
catch (RestClientException ex) {
|
catch (RestClientException ex) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
|
||||||
@ -83,31 +103,6 @@ public final class DefaultRefreshTokenTokenResponseClient
|
|||||||
null);
|
null);
|
||||||
throw new OAuth2AuthorizationException(oauth2Error, ex);
|
throw new OAuth2AuthorizationException(oauth2Error, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse tokenResponse = response.getBody();
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())
|
|
||||||
|| tokenResponse.getRefreshToken() == null) {
|
|
||||||
OAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse
|
|
||||||
.withResponse(tokenResponse);
|
|
||||||
|
|
||||||
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
|
|
||||||
tokenResponseBuilder.scopes(refreshTokenGrantRequest.getAccessToken().getScopes());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenResponse.getRefreshToken() == null) {
|
|
||||||
// Reuse existing refresh token
|
|
||||||
tokenResponseBuilder.refreshToken(refreshTokenGrantRequest.getRefreshToken().getTokenValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenResponse = tokenResponseBuilder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenResponse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +81,6 @@ public class NimbusAuthorizationCodeTokenResponseClient
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) {
|
public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) {
|
||||||
ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
// Build the authorization code grant request for the token endpoint
|
// Build the authorization code grant request for the token endpoint
|
||||||
AuthorizationCode authorizationCode = new AuthorizationCode(
|
AuthorizationCode authorizationCode = new AuthorizationCode(
|
||||||
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationResponse().getCode());
|
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationResponse().getCode());
|
||||||
@ -89,19 +88,43 @@ public class NimbusAuthorizationCodeTokenResponseClient
|
|||||||
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationRequest().getRedirectUri());
|
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationRequest().getRedirectUri());
|
||||||
AuthorizationGrant authorizationCodeGrant = new AuthorizationCodeGrant(authorizationCode, redirectUri);
|
AuthorizationGrant authorizationCodeGrant = new AuthorizationCodeGrant(authorizationCode, redirectUri);
|
||||||
URI tokenUri = toURI(clientRegistration.getProviderDetails().getTokenUri());
|
URI tokenUri = toURI(clientRegistration.getProviderDetails().getTokenUri());
|
||||||
|
|
||||||
// Set the credentials to authenticate the client at the token endpoint
|
// Set the credentials to authenticate the client at the token endpoint
|
||||||
ClientID clientId = new ClientID(clientRegistration.getClientId());
|
ClientID clientId = new ClientID(clientRegistration.getClientId());
|
||||||
Secret clientSecret = new Secret(clientRegistration.getClientSecret());
|
Secret clientSecret = new Secret(clientRegistration.getClientSecret());
|
||||||
ClientAuthentication clientAuthentication;
|
boolean isPost = ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod());
|
||||||
if (ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) {
|
ClientAuthentication clientAuthentication = isPost ? new ClientSecretPost(clientId, clientSecret)
|
||||||
clientAuthentication = new ClientSecretPost(clientId, clientSecret);
|
: new ClientSecretBasic(clientId, clientSecret);
|
||||||
|
com.nimbusds.oauth2.sdk.TokenResponse tokenResponse = getTokenResponse(authorizationCodeGrant, tokenUri,
|
||||||
|
clientAuthentication);
|
||||||
|
if (!tokenResponse.indicatesSuccess()) {
|
||||||
|
TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) tokenResponse;
|
||||||
|
ErrorObject errorObject = tokenErrorResponse.getErrorObject();
|
||||||
|
throw new OAuth2AuthorizationException(getOAuthError(errorObject));
|
||||||
}
|
}
|
||||||
else {
|
AccessTokenResponse accessTokenResponse = (AccessTokenResponse) tokenResponse;
|
||||||
clientAuthentication = new ClientSecretBasic(clientId, clientSecret);
|
String accessToken = accessTokenResponse.getTokens().getAccessToken().getValue();
|
||||||
|
OAuth2AccessToken.TokenType accessTokenType = null;
|
||||||
|
if (OAuth2AccessToken.TokenType.BEARER.getValue()
|
||||||
|
.equalsIgnoreCase(accessTokenResponse.getTokens().getAccessToken().getType().getValue())) {
|
||||||
|
accessTokenType = OAuth2AccessToken.TokenType.BEARER;
|
||||||
}
|
}
|
||||||
|
long expiresIn = accessTokenResponse.getTokens().getAccessToken().getLifetime();
|
||||||
|
// 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 Authorization Request
|
||||||
|
Set<String> scopes = getScopes(authorizationGrantRequest, accessTokenResponse);
|
||||||
|
String refreshToken = null;
|
||||||
|
if (accessTokenResponse.getTokens().getRefreshToken() != null) {
|
||||||
|
refreshToken = accessTokenResponse.getTokens().getRefreshToken().getValue();
|
||||||
|
}
|
||||||
|
Map<String, Object> additionalParameters = new LinkedHashMap<>(accessTokenResponse.getCustomParameters());
|
||||||
|
return OAuth2AccessTokenResponse.withToken(accessToken).tokenType(accessTokenType).expiresIn(expiresIn)
|
||||||
|
.scopes(scopes).refreshToken(refreshToken).additionalParameters(additionalParameters).build();
|
||||||
|
}
|
||||||
|
|
||||||
com.nimbusds.oauth2.sdk.TokenResponse tokenResponse;
|
private com.nimbusds.oauth2.sdk.TokenResponse getTokenResponse(AuthorizationGrant authorizationCodeGrant,
|
||||||
|
URI tokenUri, ClientAuthentication clientAuthentication) {
|
||||||
try {
|
try {
|
||||||
// Send the Access Token request
|
// Send the Access Token request
|
||||||
TokenRequest tokenRequest = new TokenRequest(tokenUri, clientAuthentication, authorizationCodeGrant);
|
TokenRequest tokenRequest = new TokenRequest(tokenUri, clientAuthentication, authorizationCodeGrant);
|
||||||
@ -109,7 +132,7 @@ public class NimbusAuthorizationCodeTokenResponseClient
|
|||||||
httpRequest.setAccept(MediaType.APPLICATION_JSON_VALUE);
|
httpRequest.setAccept(MediaType.APPLICATION_JSON_VALUE);
|
||||||
httpRequest.setConnectTimeout(30000);
|
httpRequest.setConnectTimeout(30000);
|
||||||
httpRequest.setReadTimeout(30000);
|
httpRequest.setReadTimeout(30000);
|
||||||
tokenResponse = com.nimbusds.oauth2.sdk.TokenResponse.parse(httpRequest.send());
|
return com.nimbusds.oauth2.sdk.TokenResponse.parse(httpRequest.send());
|
||||||
}
|
}
|
||||||
catch (ParseException | IOException ex) {
|
catch (ParseException | IOException ex) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
|
||||||
@ -118,55 +141,25 @@ public class NimbusAuthorizationCodeTokenResponseClient
|
|||||||
null);
|
null);
|
||||||
throw new OAuth2AuthorizationException(oauth2Error, ex);
|
throw new OAuth2AuthorizationException(oauth2Error, ex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!tokenResponse.indicatesSuccess()) {
|
private Set<String> getScopes(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest,
|
||||||
TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) tokenResponse;
|
AccessTokenResponse accessTokenResponse) {
|
||||||
ErrorObject errorObject = tokenErrorResponse.getErrorObject();
|
|
||||||
OAuth2Error oauth2Error;
|
|
||||||
if (errorObject == null) {
|
|
||||||
oauth2Error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
oauth2Error = new OAuth2Error(
|
|
||||||
(errorObject.getCode() != null) ? errorObject.getCode() : OAuth2ErrorCodes.SERVER_ERROR,
|
|
||||||
errorObject.getDescription(),
|
|
||||||
(errorObject.getURI() != null) ? errorObject.getURI().toString() : null);
|
|
||||||
}
|
|
||||||
throw new OAuth2AuthorizationException(oauth2Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
AccessTokenResponse accessTokenResponse = (AccessTokenResponse) tokenResponse;
|
|
||||||
|
|
||||||
String accessToken = accessTokenResponse.getTokens().getAccessToken().getValue();
|
|
||||||
OAuth2AccessToken.TokenType accessTokenType = null;
|
|
||||||
if (OAuth2AccessToken.TokenType.BEARER.getValue()
|
|
||||||
.equalsIgnoreCase(accessTokenResponse.getTokens().getAccessToken().getType().getValue())) {
|
|
||||||
accessTokenType = OAuth2AccessToken.TokenType.BEARER;
|
|
||||||
}
|
|
||||||
long expiresIn = accessTokenResponse.getTokens().getAccessToken().getLifetime();
|
|
||||||
|
|
||||||
// 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 Authorization Request
|
|
||||||
Set<String> scopes;
|
|
||||||
if (CollectionUtils.isEmpty(accessTokenResponse.getTokens().getAccessToken().getScope())) {
|
if (CollectionUtils.isEmpty(accessTokenResponse.getTokens().getAccessToken().getScope())) {
|
||||||
scopes = new LinkedHashSet<>(
|
return new LinkedHashSet<>(
|
||||||
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationRequest().getScopes());
|
authorizationGrantRequest.getAuthorizationExchange().getAuthorizationRequest().getScopes());
|
||||||
}
|
}
|
||||||
else {
|
return new LinkedHashSet<>(accessTokenResponse.getTokens().getAccessToken().getScope().toStringList());
|
||||||
scopes = new LinkedHashSet<>(accessTokenResponse.getTokens().getAccessToken().getScope().toStringList());
|
}
|
||||||
|
|
||||||
|
private OAuth2Error getOAuthError(ErrorObject errorObject) {
|
||||||
|
if (errorObject == null) {
|
||||||
|
return new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
String errorCode = (errorObject.getCode() != null) ? errorObject.getCode() : OAuth2ErrorCodes.SERVER_ERROR;
|
||||||
String refreshToken = null;
|
String description = errorObject.getDescription();
|
||||||
if (accessTokenResponse.getTokens().getRefreshToken() != null) {
|
String uri = (errorObject.getURI() != null) ? errorObject.getURI().toString() : null;
|
||||||
refreshToken = accessTokenResponse.getTokens().getRefreshToken().getValue();
|
return new OAuth2Error(errorCode, description, uri);
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> additionalParameters = new LinkedHashMap<>(accessTokenResponse.getCustomParameters());
|
|
||||||
|
|
||||||
return OAuth2AccessTokenResponse.withToken(accessToken).tokenType(accessTokenType).expiresIn(expiresIn)
|
|
||||||
.scopes(scopes).refreshToken(refreshToken).additionalParameters(additionalParameters).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static URI toURI(String uriStr) {
|
private static URI toURI(String uriStr) {
|
||||||
|
@ -53,12 +53,10 @@ public class OAuth2AuthorizationCodeGrantRequestEntityConverter
|
|||||||
@Override
|
@Override
|
||||||
public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
||||||
ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
||||||
MultiValueMap<String, String> formParameters = this.buildFormParameters(authorizationCodeGrantRequest);
|
MultiValueMap<String, String> formParameters = this.buildFormParameters(authorizationCodeGrantRequest);
|
||||||
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
||||||
.toUri();
|
.toUri();
|
||||||
|
|
||||||
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +71,6 @@ public class OAuth2AuthorizationCodeGrantRequestEntityConverter
|
|||||||
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
|
||||||
ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
|
||||||
OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange();
|
OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange();
|
||||||
|
|
||||||
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
||||||
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue());
|
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue());
|
||||||
formParameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode());
|
formParameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode());
|
||||||
@ -92,7 +89,6 @@ public class OAuth2AuthorizationCodeGrantRequestEntityConverter
|
|||||||
if (codeVerifier != null) {
|
if (codeVerifier != null) {
|
||||||
formParameters.add(PkceParameterNames.CODE_VERIFIER, codeVerifier);
|
formParameters.add(PkceParameterNames.CODE_VERIFIER, codeVerifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
return formParameters;
|
return formParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +53,10 @@ public class OAuth2ClientCredentialsGrantRequestEntityConverter
|
|||||||
@Override
|
@Override
|
||||||
public RequestEntity<?> convert(OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
public RequestEntity<?> convert(OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
||||||
ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
||||||
MultiValueMap<String, String> formParameters = this.buildFormParameters(clientCredentialsGrantRequest);
|
MultiValueMap<String, String> formParameters = this.buildFormParameters(clientCredentialsGrantRequest);
|
||||||
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
||||||
.toUri();
|
.toUri();
|
||||||
|
|
||||||
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +70,6 @@ public class OAuth2ClientCredentialsGrantRequestEntityConverter
|
|||||||
private MultiValueMap<String, String> buildFormParameters(
|
private MultiValueMap<String, String> buildFormParameters(
|
||||||
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {
|
||||||
ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
||||||
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, clientCredentialsGrantRequest.getGrantType().getValue());
|
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, clientCredentialsGrantRequest.getGrantType().getValue());
|
||||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
||||||
@ -83,7 +80,6 @@ public class OAuth2ClientCredentialsGrantRequestEntityConverter
|
|||||||
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
||||||
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
||||||
}
|
}
|
||||||
|
|
||||||
return formParameters;
|
return formParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +53,10 @@ public class OAuth2PasswordGrantRequestEntityConverter
|
|||||||
@Override
|
@Override
|
||||||
public RequestEntity<?> convert(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
public RequestEntity<?> convert(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
||||||
ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
||||||
MultiValueMap<String, String> formParameters = buildFormParameters(passwordGrantRequest);
|
MultiValueMap<String, String> formParameters = buildFormParameters(passwordGrantRequest);
|
||||||
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
||||||
.toUri();
|
.toUri();
|
||||||
|
|
||||||
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +69,6 @@ public class OAuth2PasswordGrantRequestEntityConverter
|
|||||||
*/
|
*/
|
||||||
private MultiValueMap<String, String> buildFormParameters(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
private MultiValueMap<String, String> buildFormParameters(OAuth2PasswordGrantRequest passwordGrantRequest) {
|
||||||
ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
||||||
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, passwordGrantRequest.getGrantType().getValue());
|
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, passwordGrantRequest.getGrantType().getValue());
|
||||||
formParameters.add(OAuth2ParameterNames.USERNAME, passwordGrantRequest.getUsername());
|
formParameters.add(OAuth2ParameterNames.USERNAME, passwordGrantRequest.getUsername());
|
||||||
@ -84,7 +81,6 @@ public class OAuth2PasswordGrantRequestEntityConverter
|
|||||||
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
||||||
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
||||||
}
|
}
|
||||||
|
|
||||||
return formParameters;
|
return formParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +53,10 @@ public class OAuth2RefreshTokenGrantRequestEntityConverter
|
|||||||
@Override
|
@Override
|
||||||
public RequestEntity<?> convert(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
public RequestEntity<?> convert(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
||||||
ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration);
|
||||||
MultiValueMap<String, String> formParameters = buildFormParameters(refreshTokenGrantRequest);
|
MultiValueMap<String, String> formParameters = buildFormParameters(refreshTokenGrantRequest);
|
||||||
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build()
|
||||||
.toUri();
|
.toUri();
|
||||||
|
|
||||||
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +69,6 @@ public class OAuth2RefreshTokenGrantRequestEntityConverter
|
|||||||
*/
|
*/
|
||||||
private MultiValueMap<String, String> buildFormParameters(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
private MultiValueMap<String, String> buildFormParameters(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {
|
||||||
ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration();
|
ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration();
|
||||||
|
|
||||||
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
|
||||||
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, refreshTokenGrantRequest.getGrantType().getValue());
|
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, refreshTokenGrantRequest.getGrantType().getValue());
|
||||||
formParameters.add(OAuth2ParameterNames.REFRESH_TOKEN,
|
formParameters.add(OAuth2ParameterNames.REFRESH_TOKEN,
|
||||||
@ -84,7 +81,6 @@ public class OAuth2RefreshTokenGrantRequestEntityConverter
|
|||||||
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
||||||
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
||||||
}
|
}
|
||||||
|
|
||||||
return formParameters;
|
return formParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,12 +68,10 @@ public final class WebClientReactiveRefreshTokenTokenResponseClient
|
|||||||
@Override
|
@Override
|
||||||
OAuth2AccessTokenResponse populateTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest,
|
OAuth2AccessTokenResponse populateTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest,
|
||||||
OAuth2AccessTokenResponse accessTokenResponse) {
|
OAuth2AccessTokenResponse accessTokenResponse) {
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())
|
if (!CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())
|
||||||
&& accessTokenResponse.getRefreshToken() != null) {
|
&& accessTokenResponse.getRefreshToken() != null) {
|
||||||
return accessTokenResponse;
|
return accessTokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse
|
OAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse
|
||||||
.withResponse(accessTokenResponse);
|
.withResponse(accessTokenResponse);
|
||||||
if (CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())) {
|
if (CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())) {
|
||||||
|
@ -55,14 +55,12 @@ public class OAuth2ErrorResponseErrorHandler implements ResponseErrorHandler {
|
|||||||
if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) {
|
if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) {
|
||||||
this.defaultErrorHandler.handleError(response);
|
this.defaultErrorHandler.handleError(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Bearer Token Error may be in the WWW-Authenticate response header
|
// A Bearer Token Error may be in the WWW-Authenticate response header
|
||||||
// See https://tools.ietf.org/html/rfc6750#section-3
|
// See https://tools.ietf.org/html/rfc6750#section-3
|
||||||
OAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());
|
OAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());
|
||||||
if (oauth2Error == null) {
|
if (oauth2Error == null) {
|
||||||
oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);
|
oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new OAuth2AuthorizationException(oauth2Error);
|
throw new OAuth2AuthorizationException(oauth2Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,21 +69,21 @@ public class OAuth2ErrorResponseErrorHandler implements ResponseErrorHandler {
|
|||||||
if (!StringUtils.hasText(wwwAuthenticateHeader)) {
|
if (!StringUtils.hasText(wwwAuthenticateHeader)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
BearerTokenError bearerTokenError = getBearerToken(wwwAuthenticateHeader);
|
||||||
BearerTokenError bearerTokenError;
|
|
||||||
try {
|
|
||||||
bearerTokenError = BearerTokenError.parse(wwwAuthenticateHeader);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String errorCode = (bearerTokenError.getCode() != null) ? bearerTokenError.getCode()
|
String errorCode = (bearerTokenError.getCode() != null) ? bearerTokenError.getCode()
|
||||||
: OAuth2ErrorCodes.SERVER_ERROR;
|
: OAuth2ErrorCodes.SERVER_ERROR;
|
||||||
String errorDescription = bearerTokenError.getDescription();
|
String errorDescription = bearerTokenError.getDescription();
|
||||||
String errorUri = (bearerTokenError.getURI() != null) ? bearerTokenError.getURI().toString() : null;
|
String errorUri = (bearerTokenError.getURI() != null) ? bearerTokenError.getURI().toString() : null;
|
||||||
|
|
||||||
return new OAuth2Error(errorCode, errorDescription, errorUri);
|
return new OAuth2Error(errorCode, errorDescription, errorUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BearerTokenError getBearerToken(String wwwAuthenticateHeader) {
|
||||||
|
try {
|
||||||
|
return BearerTokenError.parse(wwwAuthenticateHeader);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,6 @@ final class ClientRegistrationDeserializer extends JsonDeserializer<ClientRegist
|
|||||||
JsonNode clientRegistrationNode = mapper.readTree(parser);
|
JsonNode clientRegistrationNode = mapper.readTree(parser);
|
||||||
JsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, "providerDetails");
|
JsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, "providerDetails");
|
||||||
JsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, "userInfoEndpoint");
|
JsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, "userInfoEndpoint");
|
||||||
|
|
||||||
return ClientRegistration
|
return ClientRegistration
|
||||||
.withRegistrationId(JsonNodeUtils.findStringValue(clientRegistrationNode, "registrationId"))
|
.withRegistrationId(JsonNodeUtils.findStringValue(clientRegistrationNode, "registrationId"))
|
||||||
.clientId(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientId"))
|
.clientId(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientId"))
|
||||||
@ -62,8 +61,7 @@ final class ClientRegistrationDeserializer extends JsonDeserializer<ClientRegist
|
|||||||
.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER
|
.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||||
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "authorizationGrantType")))
|
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "authorizationGrantType")))
|
||||||
.redirectUri(JsonNodeUtils.findStringValue(clientRegistrationNode, "redirectUri"))
|
.redirectUri(JsonNodeUtils.findStringValue(clientRegistrationNode, "redirectUri"))
|
||||||
.scope(JsonNodeUtils.findValue(clientRegistrationNode, "scopes", JsonNodeUtils.SET_TYPE_REFERENCE,
|
.scope(JsonNodeUtils.findValue(clientRegistrationNode, "scopes", JsonNodeUtils.STRING_SET, mapper))
|
||||||
mapper))
|
|
||||||
.clientName(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientName"))
|
.clientName(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientName"))
|
||||||
.authorizationUri(JsonNodeUtils.findStringValue(providerDetailsNode, "authorizationUri"))
|
.authorizationUri(JsonNodeUtils.findStringValue(providerDetailsNode, "authorizationUri"))
|
||||||
.tokenUri(JsonNodeUtils.findStringValue(providerDetailsNode, "tokenUri"))
|
.tokenUri(JsonNodeUtils.findStringValue(providerDetailsNode, "tokenUri"))
|
||||||
@ -74,7 +72,7 @@ final class ClientRegistrationDeserializer extends JsonDeserializer<ClientRegist
|
|||||||
.jwkSetUri(JsonNodeUtils.findStringValue(providerDetailsNode, "jwkSetUri"))
|
.jwkSetUri(JsonNodeUtils.findStringValue(providerDetailsNode, "jwkSetUri"))
|
||||||
.issuerUri(JsonNodeUtils.findStringValue(providerDetailsNode, "issuerUri"))
|
.issuerUri(JsonNodeUtils.findStringValue(providerDetailsNode, "issuerUri"))
|
||||||
.providerConfigurationMetadata(JsonNodeUtils.findValue(providerDetailsNode, "configurationMetadata",
|
.providerConfigurationMetadata(JsonNodeUtils.findValue(providerDetailsNode, "configurationMetadata",
|
||||||
JsonNodeUtils.MAP_TYPE_REFERENCE, mapper))
|
JsonNodeUtils.STRING_OBJECT_MAP, mapper))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,20 +31,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
*/
|
*/
|
||||||
abstract class JsonNodeUtils {
|
abstract class JsonNodeUtils {
|
||||||
|
|
||||||
static final TypeReference<Set<String>> SET_TYPE_REFERENCE = new TypeReference<Set<String>>() {
|
static final TypeReference<Set<String>> STRING_SET = new TypeReference<Set<String>>() {
|
||||||
};
|
};
|
||||||
static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() {
|
|
||||||
|
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<Map<String, Object>>() {
|
||||||
};
|
};
|
||||||
|
|
||||||
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
||||||
if (jsonNode == null) {
|
if (jsonNode == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
JsonNode nodeValue = jsonNode.findValue(fieldName);
|
JsonNode value = jsonNode.findValue(fieldName);
|
||||||
if (nodeValue != null && nodeValue.isTextual()) {
|
return (value != null && value.isTextual()) ? value.asText() : null;
|
||||||
return nodeValue.asText();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
||||||
@ -52,22 +50,16 @@ abstract class JsonNodeUtils {
|
|||||||
if (jsonNode == null) {
|
if (jsonNode == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
JsonNode nodeValue = jsonNode.findValue(fieldName);
|
JsonNode value = jsonNode.findValue(fieldName);
|
||||||
if (nodeValue != null && nodeValue.isContainerNode()) {
|
return (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null;
|
||||||
return mapper.convertValue(nodeValue, valueTypeReference);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
||||||
if (jsonNode == null) {
|
if (jsonNode == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
JsonNode nodeValue = jsonNode.findValue(fieldName);
|
JsonNode value = jsonNode.findValue(fieldName);
|
||||||
if (nodeValue != null && nodeValue.isObject()) {
|
return (value != null && value.isObject()) ? value : null;
|
||||||
return nodeValue;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.util.StdConverter;
|
|||||||
|
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||||
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
||||||
@ -45,35 +46,36 @@ final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAut
|
|||||||
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context)
|
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
|
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
|
||||||
JsonNode authorizationRequestNode = mapper.readTree(parser);
|
JsonNode root = mapper.readTree(parser);
|
||||||
|
return deserialize(parser, mapper, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizationRequest deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root)
|
||||||
|
throws JsonParseException {
|
||||||
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER
|
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||||
.convert(JsonNodeUtils.findObjectNode(authorizationRequestNode, "authorizationGrantType"));
|
.convert(JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
|
||||||
|
Builder builder = getBuilder(parser, authorizationGrantType);
|
||||||
|
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
|
||||||
|
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
|
||||||
|
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
|
||||||
|
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, mapper));
|
||||||
|
builder.state(JsonNodeUtils.findStringValue(root, "state"));
|
||||||
|
builder.additionalParameters(
|
||||||
|
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, mapper));
|
||||||
|
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
|
||||||
|
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, mapper));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest.Builder builder;
|
private OAuth2AuthorizationRequest.Builder getBuilder(JsonParser parser,
|
||||||
|
AuthorizationGrantType authorizationGrantType) throws JsonParseException {
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||||
builder = OAuth2AuthorizationRequest.authorizationCode();
|
return OAuth2AuthorizationRequest.authorizationCode();
|
||||||
}
|
}
|
||||||
else if (AuthorizationGrantType.IMPLICIT.equals(authorizationGrantType)) {
|
if (AuthorizationGrantType.IMPLICIT.equals(authorizationGrantType)) {
|
||||||
builder = OAuth2AuthorizationRequest.implicit();
|
return OAuth2AuthorizationRequest.implicit();
|
||||||
}
|
}
|
||||||
else {
|
throw new JsonParseException(parser, "Invalid authorizationGrantType");
|
||||||
throw new JsonParseException(parser, "Invalid authorizationGrantType");
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.authorizationUri(JsonNodeUtils.findStringValue(authorizationRequestNode, "authorizationUri"))
|
|
||||||
.clientId(JsonNodeUtils.findStringValue(authorizationRequestNode, "clientId"))
|
|
||||||
.redirectUri(JsonNodeUtils.findStringValue(authorizationRequestNode, "redirectUri"))
|
|
||||||
.scopes(JsonNodeUtils
|
|
||||||
.findValue(authorizationRequestNode, "scopes", JsonNodeUtils.SET_TYPE_REFERENCE, mapper))
|
|
||||||
.state(JsonNodeUtils.findStringValue(authorizationRequestNode, "state"))
|
|
||||||
.additionalParameters(JsonNodeUtils.findValue(authorizationRequestNode, "additionalParameters",
|
|
||||||
JsonNodeUtils.MAP_TYPE_REFERENCE, mapper))
|
|
||||||
.authorizationRequestUri(
|
|
||||||
JsonNodeUtils.findStringValue(authorizationRequestNode, "authorizationRequestUri"))
|
|
||||||
.attributes(JsonNodeUtils.findValue(authorizationRequestNode, "attributes",
|
|
||||||
JsonNodeUtils.MAP_TYPE_REFERENCE, mapper))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,10 @@ abstract class StdConverters {
|
|||||||
if (ClientAuthenticationMethod.BASIC.getValue().equalsIgnoreCase(value)) {
|
if (ClientAuthenticationMethod.BASIC.getValue().equalsIgnoreCase(value)) {
|
||||||
return ClientAuthenticationMethod.BASIC;
|
return ClientAuthenticationMethod.BASIC;
|
||||||
}
|
}
|
||||||
else if (ClientAuthenticationMethod.POST.getValue().equalsIgnoreCase(value)) {
|
if (ClientAuthenticationMethod.POST.getValue().equalsIgnoreCase(value)) {
|
||||||
return ClientAuthenticationMethod.POST;
|
return ClientAuthenticationMethod.POST;
|
||||||
}
|
}
|
||||||
else if (ClientAuthenticationMethod.NONE.getValue().equalsIgnoreCase(value)) {
|
if (ClientAuthenticationMethod.NONE.getValue().equalsIgnoreCase(value)) {
|
||||||
return ClientAuthenticationMethod.NONE;
|
return ClientAuthenticationMethod.NONE;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -72,13 +72,13 @@ abstract class StdConverters {
|
|||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||||
}
|
}
|
||||||
else if (AuthorizationGrantType.IMPLICIT.getValue().equalsIgnoreCase(value)) {
|
if (AuthorizationGrantType.IMPLICIT.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthorizationGrantType.IMPLICIT;
|
return AuthorizationGrantType.IMPLICIT;
|
||||||
}
|
}
|
||||||
else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {
|
if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthorizationGrantType.CLIENT_CREDENTIALS;
|
return AuthorizationGrantType.CLIENT_CREDENTIALS;
|
||||||
}
|
}
|
||||||
else if (AuthorizationGrantType.PASSWORD.getValue().equalsIgnoreCase(value)) {
|
if (AuthorizationGrantType.PASSWORD.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthorizationGrantType.PASSWORD;
|
return AuthorizationGrantType.PASSWORD;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -94,10 +94,10 @@ abstract class StdConverters {
|
|||||||
if (AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {
|
if (AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthenticationMethod.HEADER;
|
return AuthenticationMethod.HEADER;
|
||||||
}
|
}
|
||||||
else if (AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {
|
if (AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthenticationMethod.FORM;
|
return AuthenticationMethod.FORM;
|
||||||
}
|
}
|
||||||
else if (AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {
|
if (AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {
|
||||||
return AuthenticationMethod.QUERY;
|
return AuthenticationMethod.QUERY;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -110,7 +110,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||||||
public OidcAuthorizationCodeAuthenticationProvider(
|
public OidcAuthorizationCodeAuthenticationProvider(
|
||||||
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
|
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
|
||||||
OAuth2UserService<OidcUserRequest, OidcUser> userService) {
|
OAuth2UserService<OidcUserRequest, OidcUser> userService) {
|
||||||
|
|
||||||
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
|
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
|
||||||
Assert.notNull(userService, "userService cannot be null");
|
Assert.notNull(userService, "userService cannot be null");
|
||||||
this.accessTokenResponseClient = accessTokenResponseClient;
|
this.accessTokenResponseClient = accessTokenResponseClient;
|
||||||
@ -120,7 +119,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;
|
OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;
|
||||||
|
|
||||||
// Section 3.1.2.1 Authentication Request -
|
// Section 3.1.2.1 Authentication Request -
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
// scope
|
// scope
|
||||||
@ -131,35 +129,20 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||||||
// and let OAuth2LoginAuthenticationProvider handle it instead
|
// and let OAuth2LoginAuthenticationProvider handle it instead
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
||||||
.getAuthorizationRequest();
|
.getAuthorizationRequest();
|
||||||
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()
|
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()
|
||||||
.getAuthorizationResponse();
|
.getAuthorizationResponse();
|
||||||
|
|
||||||
if (authorizationResponse.statusError()) {
|
if (authorizationResponse.statusError()) {
|
||||||
throw new OAuth2AuthenticationException(authorizationResponse.getError(),
|
throw new OAuth2AuthenticationException(authorizationResponse.getError(),
|
||||||
authorizationResponse.getError().toString());
|
authorizationResponse.getError().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
OAuth2AccessTokenResponse accessTokenResponse = getResponse(authorizationCodeAuthentication);
|
||||||
OAuth2AccessTokenResponse accessTokenResponse;
|
|
||||||
try {
|
|
||||||
accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(
|
|
||||||
new OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),
|
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange()));
|
|
||||||
}
|
|
||||||
catch (OAuth2AuthorizationException ex) {
|
|
||||||
OAuth2Error oauth2Error = ex.getError();
|
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
||||||
|
|
||||||
Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
|
Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
|
||||||
if (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {
|
if (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {
|
||||||
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,
|
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,
|
||||||
@ -169,39 +152,54 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||||||
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString());
|
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString());
|
||||||
}
|
}
|
||||||
OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
|
OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
|
||||||
|
validateNonce(authorizationRequest, idToken);
|
||||||
// Validate nonce
|
|
||||||
String requestNonce = authorizationRequest.getAttribute(OidcParameterNames.NONCE);
|
|
||||||
if (requestNonce != null) {
|
|
||||||
String nonceHash;
|
|
||||||
try {
|
|
||||||
nonceHash = createHash(requestNonce);
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException ex) {
|
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
|
||||||
}
|
|
||||||
String nonceHashClaim = idToken.getNonce();
|
|
||||||
if (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {
|
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
|
OidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
|
||||||
accessTokenResponse.getAccessToken(), idToken, additionalParameters));
|
accessTokenResponse.getAccessToken(), idToken, additionalParameters));
|
||||||
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
||||||
.mapAuthorities(oidcUser.getAuthorities());
|
.mapAuthorities(oidcUser.getAuthorities());
|
||||||
|
|
||||||
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
||||||
authorizationCodeAuthentication.getClientRegistration(),
|
authorizationCodeAuthentication.getClientRegistration(),
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange(), oidcUser, mappedAuthorities,
|
authorizationCodeAuthentication.getAuthorizationExchange(), oidcUser, mappedAuthorities,
|
||||||
accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken());
|
accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken());
|
||||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||||
|
|
||||||
return authenticationResult;
|
return authenticationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OAuth2AccessTokenResponse getResponse(OAuth2LoginAuthenticationToken authorizationCodeAuthentication) {
|
||||||
|
try {
|
||||||
|
return this.accessTokenResponseClient.getTokenResponse(
|
||||||
|
new OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),
|
||||||
|
authorizationCodeAuthentication.getAuthorizationExchange()));
|
||||||
|
}
|
||||||
|
catch (OAuth2AuthorizationException ex) {
|
||||||
|
OAuth2Error oauth2Error = ex.getError();
|
||||||
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateNonce(OAuth2AuthorizationRequest authorizationRequest, OidcIdToken idToken) {
|
||||||
|
String requestNonce = authorizationRequest.getAttribute(OidcParameterNames.NONCE);
|
||||||
|
if (requestNonce == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String nonceHash = getNonceHash(requestNonce);
|
||||||
|
String nonceHashClaim = idToken.getNonce();
|
||||||
|
if (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {
|
||||||
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
||||||
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNonceHash(String requestNonce) {
|
||||||
|
try {
|
||||||
|
return createHash(requestNonce);
|
||||||
|
}
|
||||||
|
catch (NoSuchAlgorithmException ex) {
|
||||||
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
||||||
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link JwtDecoderFactory} used for {@link OidcIdToken} signature
|
* Sets the {@link JwtDecoderFactory} used for {@link OidcIdToken} signature
|
||||||
* verification. The factory returns a {@link JwtDecoder} associated to the provided
|
* verification. The factory returns a {@link JwtDecoder} associated to the provided
|
||||||
@ -235,18 +233,21 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||||||
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
|
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
|
||||||
OAuth2AccessTokenResponse accessTokenResponse) {
|
OAuth2AccessTokenResponse accessTokenResponse) {
|
||||||
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
|
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
|
||||||
Jwt jwt;
|
Jwt jwt = getJwt(accessTokenResponse, jwtDecoder);
|
||||||
|
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),
|
||||||
|
jwt.getClaims());
|
||||||
|
return idToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jwt getJwt(OAuth2AccessTokenResponse accessTokenResponse, JwtDecoder jwtDecoder) {
|
||||||
try {
|
try {
|
||||||
jwt = jwtDecoder
|
Map<String, Object> parameters = accessTokenResponse.getAdditionalParameters();
|
||||||
.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));
|
return jwtDecoder.decode((String) parameters.get(OidcParameterNames.ID_TOKEN));
|
||||||
}
|
}
|
||||||
catch (JwtException ex) {
|
catch (JwtException ex) {
|
||||||
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);
|
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);
|
||||||
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);
|
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);
|
||||||
}
|
}
|
||||||
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),
|
|
||||||
jwt.getClaims());
|
|
||||||
return idToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String createHash(String nonce) throws NoSuchAlgorithmException {
|
static String createHash(String nonce) throws NoSuchAlgorithmException {
|
||||||
|
@ -114,7 +114,6 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||||
|
|
||||||
// Section 3.1.2.1 Authentication Request -
|
// Section 3.1.2.1 Authentication Request -
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
// scope REQUIRED. OpenID Connect requests MUST contain the "openid" scope
|
// scope REQUIRED. OpenID Connect requests MUST contain the "openid" scope
|
||||||
@ -125,26 +124,21 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
// and let OAuth2LoginReactiveAuthenticationManager handle it instead
|
// and let OAuth2LoginReactiveAuthenticationManager handle it instead
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()
|
||||||
.getAuthorizationRequest();
|
.getAuthorizationRequest();
|
||||||
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication
|
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication
|
||||||
.getAuthorizationExchange().getAuthorizationResponse();
|
.getAuthorizationExchange().getAuthorizationResponse();
|
||||||
|
|
||||||
if (authorizationResponse.statusError()) {
|
if (authorizationResponse.statusError()) {
|
||||||
return Mono.error(new OAuth2AuthenticationException(authorizationResponse.getError(),
|
return Mono.error(new OAuth2AuthenticationException(authorizationResponse.getError(),
|
||||||
authorizationResponse.getError().toString()));
|
authorizationResponse.getError().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
|
||||||
return Mono.error(new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()));
|
return Mono.error(new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(
|
OAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(
|
||||||
authorizationCodeAuthentication.getClientRegistration(),
|
authorizationCodeAuthentication.getClientRegistration(),
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange());
|
authorizationCodeAuthentication.getAuthorizationExchange());
|
||||||
|
|
||||||
return this.accessTokenResponseClient.getTokenResponse(authzRequest).flatMap(
|
return this.accessTokenResponseClient.getTokenResponse(authzRequest).flatMap(
|
||||||
(accessTokenResponse) -> authenticationResult(authorizationCodeAuthentication, accessTokenResponse))
|
(accessTokenResponse) -> authenticationResult(authorizationCodeAuthentication, accessTokenResponse))
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
@ -190,7 +184,6 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();
|
OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();
|
||||||
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
||||||
Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
|
Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
|
||||||
|
|
||||||
if (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {
|
if (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {
|
||||||
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,
|
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,
|
||||||
"Missing (required) ID Token in Token Response for Client Registration: "
|
"Missing (required) ID Token in Token Response for Client Registration: "
|
||||||
@ -198,14 +191,12 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
null);
|
null);
|
||||||
return Mono.error(new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString()));
|
return Mono.error(new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return createOidcToken(clientRegistration, accessTokenResponse)
|
return createOidcToken(clientRegistration, accessTokenResponse)
|
||||||
.doOnNext((idToken) -> validateNonce(authorizationCodeAuthentication, idToken))
|
.doOnNext((idToken) -> validateNonce(authorizationCodeAuthentication, idToken))
|
||||||
.map((idToken) -> new OidcUserRequest(clientRegistration, accessToken, idToken, additionalParameters))
|
.map((idToken) -> new OidcUserRequest(clientRegistration, accessToken, idToken, additionalParameters))
|
||||||
.flatMap(this.userService::loadUser).map((oauth2User) -> {
|
.flatMap(this.userService::loadUser).map((oauth2User) -> {
|
||||||
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
||||||
.mapAuthorities(oauth2User.getAuthorities());
|
.mapAuthorities(oauth2User.getAuthorities());
|
||||||
|
|
||||||
return new OAuth2LoginAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(),
|
return new OAuth2LoginAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(),
|
||||||
authorizationCodeAuthentication.getAuthorizationExchange(), oauth2User, mappedAuthorities,
|
authorizationCodeAuthentication.getAuthorizationExchange(), oauth2User, mappedAuthorities,
|
||||||
accessToken, accessTokenResponse.getRefreshToken());
|
accessToken, accessTokenResponse.getRefreshToken());
|
||||||
@ -225,14 +216,7 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
String requestNonce = authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationRequest()
|
String requestNonce = authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationRequest()
|
||||||
.getAttribute(OidcParameterNames.NONCE);
|
.getAttribute(OidcParameterNames.NONCE);
|
||||||
if (requestNonce != null) {
|
if (requestNonce != null) {
|
||||||
String nonceHash;
|
String nonceHash = getNonceHash(requestNonce);
|
||||||
try {
|
|
||||||
nonceHash = createHash(requestNonce);
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException ex) {
|
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
|
||||||
}
|
|
||||||
String nonceHashClaim = idToken.getNonce();
|
String nonceHashClaim = idToken.getNonce();
|
||||||
if (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {
|
if (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
||||||
@ -243,6 +227,16 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements React
|
|||||||
return Mono.just(idToken);
|
return Mono.just(idToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getNonceHash(String requestNonce) {
|
||||||
|
try {
|
||||||
|
return createHash(requestNonce);
|
||||||
|
}
|
||||||
|
catch (NoSuchAlgorithmException ex) {
|
||||||
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);
|
||||||
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static String createHash(String nonce) throws NoSuchAlgorithmException {
|
static String createHash(String nonce) throws NoSuchAlgorithmException {
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
byte[] digest = md.digest(nonce.getBytes(StandardCharsets.US_ASCII));
|
byte[] digest = md.digest(nonce.getBytes(StandardCharsets.US_ASCII));
|
||||||
|
@ -20,6 +20,7 @@ import java.net.URL;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -66,15 +67,16 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
|
|||||||
|
|
||||||
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
|
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
|
||||||
|
|
||||||
private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
|
private static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;
|
||||||
{
|
static {
|
||||||
put(MacAlgorithm.HS256, "HmacSHA256");
|
Map<JwsAlgorithm, String> mappings = new HashMap<>();
|
||||||
put(MacAlgorithm.HS384, "HmacSHA384");
|
mappings.put(MacAlgorithm.HS256, "HmacSHA256");
|
||||||
put(MacAlgorithm.HS512, "HmacSHA512");
|
mappings.put(MacAlgorithm.HS384, "HmacSHA384");
|
||||||
}
|
mappings.put(MacAlgorithm.HS512, "HmacSHA512");
|
||||||
|
JCA_ALGORITHM_MAPPINGS = Collections.unmodifiableMap(mappings);
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
|
private static final ClaimTypeConverter DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
|
||||||
createDefaultClaimTypeConverters());
|
createDefaultClaimTypeConverters());
|
||||||
|
|
||||||
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
|
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
|
||||||
@ -100,23 +102,22 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
|
|||||||
Converter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));
|
Converter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));
|
||||||
Converter<Object, ?> collectionStringConverter = getConverter(
|
Converter<Object, ?> collectionStringConverter = getConverter(
|
||||||
TypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));
|
TypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));
|
||||||
|
Map<String, Converter<Object, ?>> converters = new HashMap<>();
|
||||||
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
converters.put(IdTokenClaimNames.ISS, urlConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.ISS, urlConverter);
|
converters.put(IdTokenClaimNames.AUD, collectionStringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AUD, collectionStringConverter);
|
converters.put(IdTokenClaimNames.NONCE, stringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.NONCE, stringConverter);
|
converters.put(IdTokenClaimNames.EXP, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.EXP, instantConverter);
|
converters.put(IdTokenClaimNames.IAT, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.IAT, instantConverter);
|
converters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);
|
converters.put(IdTokenClaimNames.AMR, collectionStringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AMR, collectionStringConverter);
|
converters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
converters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
converters.put(StandardClaimNames.UPDATED_AT, instantConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.UPDATED_AT, instantConverter);
|
return converters;
|
||||||
return claimTypeConverters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
||||||
final TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);
|
TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);
|
||||||
return (source) -> ClaimConversionService.getSharedInstance().convert(source, sourceDescriptor,
|
return (source) -> ClaimConversionService.getSharedInstance().convert(source, sourceDescriptor,
|
||||||
targetDescriptor);
|
targetDescriptor);
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
|
|||||||
}
|
}
|
||||||
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm).build();
|
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm).build();
|
||||||
}
|
}
|
||||||
else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
|
if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||||
//
|
//
|
||||||
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as
|
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as
|
||||||
@ -176,7 +177,6 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
|
|||||||
// For MAC based algorithms, the behavior is unspecified if the aud is
|
// For MAC based algorithms, the behavior is unspecified if the aud is
|
||||||
// multi-valued or
|
// multi-valued or
|
||||||
// if an azp value is present that is different than the aud value.
|
// if an azp value is present that is different than the aud value.
|
||||||
|
|
||||||
String clientSecret = clientRegistration.getClientSecret();
|
String clientSecret = clientRegistration.getClientSecret();
|
||||||
if (!StringUtils.hasText(clientSecret)) {
|
if (!StringUtils.hasText(clientSecret)) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
||||||
@ -187,10 +187,9 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
|
|||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),
|
SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),
|
||||||
jcaAlgorithmMappings.get(jwsAlgorithm));
|
JCA_ALGORITHM_MAPPINGS.get(jwsAlgorithm));
|
||||||
return NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
|
return NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
||||||
"Failed to find a Signature Verifier for Client Registration: '"
|
"Failed to find a Signature Verifier for Client Registration: '"
|
||||||
+ clientRegistration.getRegistrationId()
|
+ clientRegistration.getRegistrationId()
|
||||||
|
@ -68,21 +68,17 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
public OAuth2TokenValidatorResult validate(Jwt idToken) {
|
public OAuth2TokenValidatorResult validate(Jwt idToken) {
|
||||||
// 3.1.3.7 ID Token Validation
|
// 3.1.3.7 ID Token Validation
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||||
|
|
||||||
Map<String, Object> invalidClaims = validateRequiredClaims(idToken);
|
Map<String, Object> invalidClaims = validateRequiredClaims(idToken);
|
||||||
if (!invalidClaims.isEmpty()) {
|
if (!invalidClaims.isEmpty()) {
|
||||||
return OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));
|
return OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. The Issuer Identifier for the OpenID Provider (which is typically obtained
|
// 2. The Issuer Identifier for the OpenID Provider (which is typically obtained
|
||||||
// during Discovery)
|
// during Discovery)
|
||||||
// MUST exactly match the value of the iss (issuer) Claim.
|
// MUST exactly match the value of the iss (issuer) Claim.
|
||||||
String metadataIssuer = this.clientRegistration.getProviderDetails().getIssuerUri();
|
String metadataIssuer = this.clientRegistration.getProviderDetails().getIssuerUri();
|
||||||
|
|
||||||
if (metadataIssuer != null && !Objects.equals(metadataIssuer, idToken.getIssuer().toExternalForm())) {
|
if (metadataIssuer != null && !Objects.equals(metadataIssuer, idToken.getIssuer().toExternalForm())) {
|
||||||
invalidClaims.put(IdTokenClaimNames.ISS, idToken.getIssuer());
|
invalidClaims.put(IdTokenClaimNames.ISS, idToken.getIssuer());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. The Client MUST validate that the aud (audience) Claim contains its
|
// 3. The Client MUST validate that the aud (audience) Claim contains its
|
||||||
// client_id value
|
// client_id value
|
||||||
// registered at the Issuer identified by the iss (issuer) Claim as an audience.
|
// registered at the Issuer identified by the iss (issuer) Claim as an audience.
|
||||||
@ -93,31 +89,26 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
if (!idToken.getAudience().contains(this.clientRegistration.getClientId())) {
|
if (!idToken.getAudience().contains(this.clientRegistration.getClientId())) {
|
||||||
invalidClaims.put(IdTokenClaimNames.AUD, idToken.getAudience());
|
invalidClaims.put(IdTokenClaimNames.AUD, idToken.getAudience());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If the ID Token contains multiple audiences,
|
// 4. If the ID Token contains multiple audiences,
|
||||||
// the Client SHOULD verify that an azp Claim is present.
|
// the Client SHOULD verify that an azp Claim is present.
|
||||||
String authorizedParty = idToken.getClaimAsString(IdTokenClaimNames.AZP);
|
String authorizedParty = idToken.getClaimAsString(IdTokenClaimNames.AZP);
|
||||||
if (idToken.getAudience().size() > 1 && authorizedParty == null) {
|
if (idToken.getAudience().size() > 1 && authorizedParty == null) {
|
||||||
invalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);
|
invalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. If an azp (authorized party) Claim is present,
|
// 5. If an azp (authorized party) Claim is present,
|
||||||
// the Client SHOULD verify that its client_id is the Claim Value.
|
// the Client SHOULD verify that its client_id is the Claim Value.
|
||||||
if (authorizedParty != null && !authorizedParty.equals(this.clientRegistration.getClientId())) {
|
if (authorizedParty != null && !authorizedParty.equals(this.clientRegistration.getClientId())) {
|
||||||
invalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);
|
invalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the
|
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the
|
||||||
// Client
|
// Client
|
||||||
// in the id_token_signed_response_alg parameter during Registration.
|
// in the id_token_signed_response_alg parameter during Registration.
|
||||||
// TODO Depends on gh-4413
|
// TODO Depends on gh-4413
|
||||||
|
|
||||||
// 9. The current time MUST be before the time represented by the exp Claim.
|
// 9. The current time MUST be before the time represented by the exp Claim.
|
||||||
Instant now = Instant.now(this.clock);
|
Instant now = Instant.now(this.clock);
|
||||||
if (now.minus(this.clockSkew).isAfter(idToken.getExpiresAt())) {
|
if (now.minus(this.clockSkew).isAfter(idToken.getExpiresAt())) {
|
||||||
invalidClaims.put(IdTokenClaimNames.EXP, idToken.getExpiresAt());
|
invalidClaims.put(IdTokenClaimNames.EXP, idToken.getExpiresAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. The iat Claim can be used to reject tokens that were issued too far away
|
// 10. The iat Claim can be used to reject tokens that were issued too far away
|
||||||
// from the current time,
|
// from the current time,
|
||||||
// limiting the amount of time that nonces need to be stored to prevent attacks.
|
// limiting the amount of time that nonces need to be stored to prevent attacks.
|
||||||
@ -125,11 +116,9 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
if (now.plus(this.clockSkew).isBefore(idToken.getIssuedAt())) {
|
if (now.plus(this.clockSkew).isBefore(idToken.getIssuedAt())) {
|
||||||
invalidClaims.put(IdTokenClaimNames.IAT, idToken.getIssuedAt());
|
invalidClaims.put(IdTokenClaimNames.IAT, idToken.getIssuedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!invalidClaims.isEmpty()) {
|
if (!invalidClaims.isEmpty()) {
|
||||||
return OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));
|
return OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));
|
||||||
}
|
}
|
||||||
|
|
||||||
return OAuth2TokenValidatorResult.success();
|
return OAuth2TokenValidatorResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +153,6 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
|
|
||||||
private static Map<String, Object> validateRequiredClaims(Jwt idToken) {
|
private static Map<String, Object> validateRequiredClaims(Jwt idToken) {
|
||||||
Map<String, Object> requiredClaims = new HashMap<>();
|
Map<String, Object> requiredClaims = new HashMap<>();
|
||||||
|
|
||||||
URL issuer = idToken.getIssuer();
|
URL issuer = idToken.getIssuer();
|
||||||
if (issuer == null) {
|
if (issuer == null) {
|
||||||
requiredClaims.put(IdTokenClaimNames.ISS, issuer);
|
requiredClaims.put(IdTokenClaimNames.ISS, issuer);
|
||||||
@ -185,7 +173,6 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
if (issuedAt == null) {
|
if (issuedAt == null) {
|
||||||
requiredClaims.put(IdTokenClaimNames.IAT, issuedAt);
|
requiredClaims.put(IdTokenClaimNames.IAT, issuedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return requiredClaims;
|
return requiredClaims;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import java.net.URL;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -66,15 +67,16 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
|
|||||||
|
|
||||||
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
|
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
|
||||||
|
|
||||||
private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
|
private static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;
|
||||||
{
|
static {
|
||||||
put(MacAlgorithm.HS256, "HmacSHA256");
|
Map<JwsAlgorithm, String> mappings = new HashMap<JwsAlgorithm, String>();
|
||||||
put(MacAlgorithm.HS384, "HmacSHA384");
|
mappings.put(MacAlgorithm.HS256, "HmacSHA256");
|
||||||
put(MacAlgorithm.HS512, "HmacSHA512");
|
mappings.put(MacAlgorithm.HS384, "HmacSHA384");
|
||||||
}
|
mappings.put(MacAlgorithm.HS512, "HmacSHA512");
|
||||||
};
|
JCA_ALGORITHM_MAPPINGS = Collections.unmodifiableMap(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
private static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
|
private static final ClaimTypeConverter DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
|
||||||
createDefaultClaimTypeConverters());
|
createDefaultClaimTypeConverters());
|
||||||
|
|
||||||
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
|
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
|
||||||
@ -100,19 +102,18 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
|
|||||||
Converter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));
|
Converter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));
|
||||||
Converter<Object, ?> collectionStringConverter = getConverter(
|
Converter<Object, ?> collectionStringConverter = getConverter(
|
||||||
TypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));
|
TypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));
|
||||||
|
Map<String, Converter<Object, ?>> converters = new HashMap<>();
|
||||||
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
converters.put(IdTokenClaimNames.ISS, urlConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.ISS, urlConverter);
|
converters.put(IdTokenClaimNames.AUD, collectionStringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AUD, collectionStringConverter);
|
converters.put(IdTokenClaimNames.NONCE, stringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.NONCE, stringConverter);
|
converters.put(IdTokenClaimNames.EXP, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.EXP, instantConverter);
|
converters.put(IdTokenClaimNames.IAT, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.IAT, instantConverter);
|
converters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);
|
converters.put(IdTokenClaimNames.AMR, collectionStringConverter);
|
||||||
claimTypeConverters.put(IdTokenClaimNames.AMR, collectionStringConverter);
|
converters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
converters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
converters.put(StandardClaimNames.UPDATED_AT, instantConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.UPDATED_AT, instantConverter);
|
return converters;
|
||||||
return claimTypeConverters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
||||||
@ -153,7 +154,6 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
|
|||||||
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by
|
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by
|
||||||
// the Client
|
// the Client
|
||||||
// in the id_token_signed_response_alg parameter during Registration.
|
// in the id_token_signed_response_alg parameter during Registration.
|
||||||
|
|
||||||
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
|
||||||
if (!StringUtils.hasText(jwkSetUri)) {
|
if (!StringUtils.hasText(jwkSetUri)) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
||||||
@ -166,7 +166,7 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
|
|||||||
return NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)
|
return NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
|
if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||||
//
|
//
|
||||||
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as
|
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as
|
||||||
@ -188,11 +188,10 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
|
|||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),
|
SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),
|
||||||
jcaAlgorithmMappings.get(jwsAlgorithm));
|
JCA_ALGORITHM_MAPPINGS.get(jwsAlgorithm));
|
||||||
return NimbusReactiveJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm)
|
return NimbusReactiveJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
|
||||||
"Failed to find a Signature Verifier for Client Registration: '"
|
"Failed to find a Signature Verifier for Client Registration: '"
|
||||||
+ clientRegistration.getRegistrationId()
|
+ clientRegistration.getRegistrationId()
|
||||||
|
@ -81,7 +81,6 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
|
|||||||
public static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {
|
public static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {
|
||||||
Converter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));
|
Converter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));
|
||||||
Converter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));
|
Converter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));
|
||||||
|
|
||||||
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
||||||
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
||||||
@ -113,9 +112,7 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
|
|||||||
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo,
|
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo,
|
||||||
userNameAttributeName);
|
userNameAttributeName);
|
||||||
}
|
}
|
||||||
else {
|
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo);
|
||||||
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +120,6 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
|
|||||||
if (!OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest)) {
|
if (!OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest)) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.oauth2UserService.loadUser(userRequest).map(OAuth2User::getAttributes)
|
return this.oauth2UserService.loadUser(userRequest).map(OAuth2User::getAttributes)
|
||||||
.map((claims) -> convertClaims(claims, userRequest.getClientRegistration())).map(OidcUserInfo::new)
|
.map((claims) -> convertClaims(claims, userRequest.getClientRegistration())).map(OidcUserInfo::new)
|
||||||
.doOnNext((userInfo) -> {
|
.doOnNext((userInfo) -> {
|
||||||
|
@ -47,7 +47,6 @@ public class OidcUserRequest extends OAuth2UserRequest {
|
|||||||
* @param idToken the ID Token
|
* @param idToken the ID Token
|
||||||
*/
|
*/
|
||||||
public OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken) {
|
public OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken) {
|
||||||
|
|
||||||
this(clientRegistration, accessToken, idToken, Collections.emptyMap());
|
this(clientRegistration, accessToken, idToken, Collections.emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,6 @@ public class OidcUserRequest extends OAuth2UserRequest {
|
|||||||
*/
|
*/
|
||||||
public OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken,
|
public OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken,
|
||||||
Map<String, Object> additionalParameters) {
|
Map<String, Object> additionalParameters) {
|
||||||
|
|
||||||
super(clientRegistration, accessToken, additionalParameters);
|
super(clientRegistration, accessToken, additionalParameters);
|
||||||
Assert.notNull(idToken, "idToken cannot be null");
|
Assert.notNull(idToken, "idToken cannot be null");
|
||||||
this.idToken = idToken;
|
this.idToken = idToken;
|
||||||
|
@ -46,10 +46,8 @@ final class OidcUserRequestUtils {
|
|||||||
// Auto-disabled if UserInfo Endpoint URI is not provided
|
// Auto-disabled if UserInfo Endpoint URI is not provided
|
||||||
ClientRegistration clientRegistration = userRequest.getClientRegistration();
|
ClientRegistration clientRegistration = userRequest.getClientRegistration();
|
||||||
if (StringUtils.isEmpty(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())) {
|
if (StringUtils.isEmpty(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Claims requested by the profile, email, address, and phone scope values
|
// 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),
|
// 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
|
// when a response_type value is used that results in an Access Token being
|
||||||
@ -60,13 +58,11 @@ final class OidcUserRequestUtils {
|
|||||||
// The Authorization Code Grant Flow, which is response_type=code, results in an
|
// The Authorization Code Grant Flow, which is response_type=code, results in an
|
||||||
// Access Token being issued.
|
// Access Token being issued.
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
|
|
||||||
// Return true if there is at least one match between the authorized scope(s)
|
// Return true if there is at least one match between the authorized scope(s)
|
||||||
// and UserInfo scope(s)
|
// and UserInfo scope(s)
|
||||||
return CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(),
|
return CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(),
|
||||||
userRequest.getClientRegistration().getScopes());
|
userRequest.getClientRegistration().getScopes());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import org.springframework.core.convert.converter.Converter;
|
|||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||||
@ -87,7 +88,6 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
|
|||||||
public static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {
|
public static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {
|
||||||
Converter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));
|
Converter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));
|
||||||
Converter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));
|
Converter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));
|
||||||
|
|
||||||
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
Map<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();
|
||||||
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);
|
||||||
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);
|
||||||
@ -96,7 +96,7 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
||||||
final TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);
|
TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);
|
||||||
return (source) -> ClaimConversionService.getSharedInstance().convert(source, sourceDescriptor,
|
return (source) -> ClaimConversionService.getSharedInstance().convert(source, sourceDescriptor,
|
||||||
targetDescriptor);
|
targetDescriptor);
|
||||||
}
|
}
|
||||||
@ -107,26 +107,14 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
|
|||||||
OidcUserInfo userInfo = null;
|
OidcUserInfo userInfo = null;
|
||||||
if (this.shouldRetrieveUserInfo(userRequest)) {
|
if (this.shouldRetrieveUserInfo(userRequest)) {
|
||||||
OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest);
|
OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest);
|
||||||
|
Map<String, Object> claims = getClaims(userRequest, oauth2User);
|
||||||
Map<String, Object> claims;
|
|
||||||
Converter<Map<String, Object>, Map<String, Object>> claimTypeConverter = this.claimTypeConverterFactory
|
|
||||||
.apply(userRequest.getClientRegistration());
|
|
||||||
if (claimTypeConverter != null) {
|
|
||||||
claims = claimTypeConverter.convert(oauth2User.getAttributes());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
claims = DEFAULT_CLAIM_TYPE_CONVERTER.convert(oauth2User.getAttributes());
|
|
||||||
}
|
|
||||||
userInfo = new OidcUserInfo(claims);
|
userInfo = new OidcUserInfo(claims);
|
||||||
|
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
|
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
|
||||||
|
|
||||||
// 1) The sub (subject) Claim MUST always be returned in the UserInfo Response
|
// 1) The sub (subject) Claim MUST always be returned in the UserInfo Response
|
||||||
if (userInfo.getSubject() == null) {
|
if (userInfo.getSubject() == null) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Due to the possibility of token substitution attacks (see Section
|
// 2) Due to the possibility of token substitution attacks (see Section
|
||||||
// 16.11),
|
// 16.11),
|
||||||
// the UserInfo Response is not guaranteed to be about the End-User
|
// the UserInfo Response is not guaranteed to be about the End-User
|
||||||
@ -139,36 +127,39 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
|
|||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
|
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
|
||||||
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
|
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
|
||||||
OAuth2AccessToken token = userRequest.getAccessToken();
|
OAuth2AccessToken token = userRequest.getAccessToken();
|
||||||
for (String authority : token.getScopes()) {
|
for (String authority : token.getScopes()) {
|
||||||
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
|
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
|
||||||
}
|
}
|
||||||
|
return getUser(userRequest, userInfo, authorities);
|
||||||
|
}
|
||||||
|
|
||||||
OidcUser user;
|
private Map<String, Object> getClaims(OidcUserRequest userRequest, OAuth2User oauth2User) {
|
||||||
|
Converter<Map<String, Object>, Map<String, Object>> converter = this.claimTypeConverterFactory
|
||||||
|
.apply(userRequest.getClientRegistration());
|
||||||
|
if (converter != null) {
|
||||||
|
return converter.convert(oauth2User.getAttributes());
|
||||||
|
}
|
||||||
|
return DEFAULT_CLAIM_TYPE_CONVERTER.convert(oauth2User.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
|
private OidcUser getUser(OidcUserRequest userRequest, OidcUserInfo userInfo, Set<GrantedAuthority> authorities) {
|
||||||
.getUserNameAttributeName();
|
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
|
||||||
|
String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
|
||||||
if (StringUtils.hasText(userNameAttributeName)) {
|
if (StringUtils.hasText(userNameAttributeName)) {
|
||||||
user = new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName);
|
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName);
|
||||||
}
|
}
|
||||||
else {
|
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo);
|
||||||
user = new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
|
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
|
||||||
// Auto-disabled if UserInfo Endpoint URI is not provided
|
// Auto-disabled if UserInfo Endpoint URI is not provided
|
||||||
if (StringUtils
|
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
|
||||||
.isEmpty(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {
|
if (StringUtils.isEmpty(providerDetails.getUserInfoEndpoint().getUri())) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Claims requested by the profile, email, address, and phone scope values
|
// 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),
|
// 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
|
// when a response_type value is used that results in an Access Token being
|
||||||
@ -180,13 +171,11 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
|
|||||||
// Access Token being issued.
|
// Access Token being issued.
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE
|
if (AuthorizationGrantType.AUTHORIZATION_CODE
|
||||||
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
|
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
|
||||||
|
|
||||||
// Return true if there is at least one match between the authorized scope(s)
|
// Return true if there is at least one match between the authorized scope(s)
|
||||||
// and accessible scope(s)
|
// and accessible scope(s)
|
||||||
return this.accessibleScopes.isEmpty()
|
return this.accessibleScopes.isEmpty()
|
||||||
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
|
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
@ -59,36 +60,29 @@ public final class OidcClientInitiatedLogoutSuccessHandler extends SimpleUrlLogo
|
|||||||
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,
|
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,
|
||||||
Authentication authentication) {
|
Authentication authentication) {
|
||||||
String targetUrl = null;
|
String targetUrl = null;
|
||||||
URI endSessionEndpoint;
|
|
||||||
if (authentication instanceof OAuth2AuthenticationToken && authentication.getPrincipal() instanceof OidcUser) {
|
if (authentication instanceof OAuth2AuthenticationToken && authentication.getPrincipal() instanceof OidcUser) {
|
||||||
String registrationId = ((OAuth2AuthenticationToken) authentication).getAuthorizedClientRegistrationId();
|
String registrationId = ((OAuth2AuthenticationToken) authentication).getAuthorizedClientRegistrationId();
|
||||||
ClientRegistration clientRegistration = this.clientRegistrationRepository
|
ClientRegistration clientRegistration = this.clientRegistrationRepository
|
||||||
.findByRegistrationId(registrationId);
|
.findByRegistrationId(registrationId);
|
||||||
endSessionEndpoint = this.endSessionEndpoint(clientRegistration);
|
URI endSessionEndpoint = this.endSessionEndpoint(clientRegistration);
|
||||||
if (endSessionEndpoint != null) {
|
if (endSessionEndpoint != null) {
|
||||||
String idToken = idToken(authentication);
|
String idToken = idToken(authentication);
|
||||||
URI postLogoutRedirectUri = postLogoutRedirectUri(request);
|
URI postLogoutRedirectUri = postLogoutRedirectUri(request);
|
||||||
targetUrl = endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri);
|
targetUrl = endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetUrl == null) {
|
return (targetUrl != null) ? targetUrl : super.determineTargetUrl(request, response);
|
||||||
targetUrl = super.determineTargetUrl(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private URI endSessionEndpoint(ClientRegistration clientRegistration) {
|
private URI endSessionEndpoint(ClientRegistration clientRegistration) {
|
||||||
URI result = null;
|
|
||||||
if (clientRegistration != null) {
|
if (clientRegistration != null) {
|
||||||
Object endSessionEndpoint = clientRegistration.getProviderDetails().getConfigurationMetadata()
|
ProviderDetails providerDetails = clientRegistration.getProviderDetails();
|
||||||
.get("end_session_endpoint");
|
Object endSessionEndpoint = providerDetails.getConfigurationMetadata().get("end_session_endpoint");
|
||||||
if (endSessionEndpoint != null) {
|
if (endSessionEndpoint != null) {
|
||||||
result = URI.create(endSessionEndpoint.toString());
|
return URI.create(endSessionEndpoint.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String idToken(Authentication authentication) {
|
private String idToken(Authentication authentication) {
|
||||||
|
@ -66,14 +66,10 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
|
|||||||
*/
|
*/
|
||||||
public OidcClientInitiatedServerLogoutSuccessHandler(
|
public OidcClientInitiatedServerLogoutSuccessHandler(
|
||||||
ReactiveClientRegistrationRepository clientRegistrationRepository) {
|
ReactiveClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
|
||||||
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||||
this.clientRegistrationRepository = clientRegistrationRepository;
|
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {
|
public Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {
|
||||||
return Mono.just(authentication).filter(OAuth2AuthenticationToken.class::isInstance)
|
return Mono.just(authentication).filter(OAuth2AuthenticationToken.class::isInstance)
|
||||||
@ -95,16 +91,14 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private URI endSessionEndpoint(ClientRegistration clientRegistration) {
|
private URI endSessionEndpoint(ClientRegistration clientRegistration) {
|
||||||
URI result = null;
|
|
||||||
if (clientRegistration != null) {
|
if (clientRegistration != null) {
|
||||||
Object endSessionEndpoint = clientRegistration.getProviderDetails().getConfigurationMetadata()
|
Object endSessionEndpoint = clientRegistration.getProviderDetails().getConfigurationMetadata()
|
||||||
.get("end_session_endpoint");
|
.get("end_session_endpoint");
|
||||||
if (endSessionEndpoint != null) {
|
if (endSessionEndpoint != null) {
|
||||||
result = URI.create(endSessionEndpoint.toString());
|
return URI.create(endSessionEndpoint.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private URI endpointUri(URI endSessionEndpoint, String idToken, URI postLogoutRedirectUri) {
|
private URI endpointUri(URI endSessionEndpoint, String idToken, URI postLogoutRedirectUri) {
|
||||||
|
@ -620,26 +620,29 @@ public final class ClientRegistration implements Serializable {
|
|||||||
|
|
||||||
private ClientRegistration create() {
|
private ClientRegistration create() {
|
||||||
ClientRegistration clientRegistration = new ClientRegistration();
|
ClientRegistration clientRegistration = new ClientRegistration();
|
||||||
|
|
||||||
clientRegistration.registrationId = this.registrationId;
|
clientRegistration.registrationId = this.registrationId;
|
||||||
clientRegistration.clientId = this.clientId;
|
clientRegistration.clientId = this.clientId;
|
||||||
clientRegistration.clientSecret = StringUtils.hasText(this.clientSecret) ? this.clientSecret : "";
|
clientRegistration.clientSecret = StringUtils.hasText(this.clientSecret) ? this.clientSecret : "";
|
||||||
if (this.clientAuthenticationMethod != null) {
|
clientRegistration.clientAuthenticationMethod = (this.clientAuthenticationMethod != null)
|
||||||
clientRegistration.clientAuthenticationMethod = this.clientAuthenticationMethod;
|
? this.clientAuthenticationMethod : deduceClientAuthenticationMethod(clientRegistration);
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)
|
|
||||||
&& !StringUtils.hasText(this.clientSecret)) {
|
|
||||||
clientRegistration.clientAuthenticationMethod = ClientAuthenticationMethod.NONE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
clientRegistration.clientAuthenticationMethod = ClientAuthenticationMethod.BASIC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientRegistration.authorizationGrantType = this.authorizationGrantType;
|
clientRegistration.authorizationGrantType = this.authorizationGrantType;
|
||||||
clientRegistration.redirectUri = this.redirectUri;
|
clientRegistration.redirectUri = this.redirectUri;
|
||||||
clientRegistration.scopes = this.scopes;
|
clientRegistration.scopes = this.scopes;
|
||||||
|
clientRegistration.providerDetails = createProviderDetails(clientRegistration);
|
||||||
|
clientRegistration.clientName = StringUtils.hasText(this.clientName) ? this.clientName
|
||||||
|
: this.registrationId;
|
||||||
|
return clientRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientAuthenticationMethod deduceClientAuthenticationMethod(ClientRegistration clientRegistration) {
|
||||||
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)
|
||||||
|
&& !StringUtils.hasText(this.clientSecret)) {
|
||||||
|
return ClientAuthenticationMethod.NONE;
|
||||||
|
}
|
||||||
|
return ClientAuthenticationMethod.BASIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProviderDetails createProviderDetails(ClientRegistration clientRegistration) {
|
||||||
ProviderDetails providerDetails = clientRegistration.new ProviderDetails();
|
ProviderDetails providerDetails = clientRegistration.new ProviderDetails();
|
||||||
providerDetails.authorizationUri = this.authorizationUri;
|
providerDetails.authorizationUri = this.authorizationUri;
|
||||||
providerDetails.tokenUri = this.tokenUri;
|
providerDetails.tokenUri = this.tokenUri;
|
||||||
@ -649,12 +652,7 @@ public final class ClientRegistration implements Serializable {
|
|||||||
providerDetails.jwkSetUri = this.jwkSetUri;
|
providerDetails.jwkSetUri = this.jwkSetUri;
|
||||||
providerDetails.issuerUri = this.issuerUri;
|
providerDetails.issuerUri = this.issuerUri;
|
||||||
providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);
|
providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);
|
||||||
clientRegistration.providerDetails = providerDetails;
|
return providerDetails;
|
||||||
|
|
||||||
clientRegistration.clientName = StringUtils.hasText(this.clientName) ? this.clientName
|
|
||||||
: this.registrationId;
|
|
||||||
|
|
||||||
return clientRegistration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateAuthorizationCodeGrantType() {
|
private void validateAuthorizationCodeGrantType() {
|
||||||
@ -696,7 +694,6 @@ public final class ClientRegistration implements Serializable {
|
|||||||
if (this.scopes == null) {
|
if (this.scopes == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String scope : this.scopes) {
|
for (String scope : this.scopes) {
|
||||||
Assert.isTrue(validateScope(scope), "scope \"" + scope + "\" contains invalid characters");
|
Assert.isTrue(validateScope(scope), "scope \"" + scope + "\" contains invalid characters");
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,6 @@ public final class ClientRegistrations {
|
|||||||
private static Supplier<ClientRegistration.Builder> oidc(URI issuer) {
|
private static Supplier<ClientRegistration.Builder> oidc(URI issuer) {
|
||||||
URI uri = UriComponentsBuilder.fromUri(issuer).replacePath(issuer.getPath() + OIDC_METADATA_PATH)
|
URI uri = UriComponentsBuilder.fromUri(issuer).replacePath(issuer.getPath() + OIDC_METADATA_PATH)
|
||||||
.build(Collections.emptyMap());
|
.build(Collections.emptyMap());
|
||||||
|
|
||||||
return () -> {
|
return () -> {
|
||||||
RequestEntity<Void> request = RequestEntity.get(uri).build();
|
RequestEntity<Void> request = RequestEntity.get(uri).build();
|
||||||
Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
|
Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
|
||||||
@ -182,12 +181,10 @@ public final class ClientRegistrations {
|
|||||||
Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
|
Map<String, Object> configuration = rest.exchange(request, typeReference).getBody();
|
||||||
AuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse);
|
AuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse);
|
||||||
ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString());
|
ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString());
|
||||||
|
|
||||||
URI jwkSetUri = metadata.getJWKSetURI();
|
URI jwkSetUri = metadata.getJWKSetURI();
|
||||||
if (jwkSetUri != null) {
|
if (jwkSetUri != null) {
|
||||||
builder.jwkSetUri(jwkSetUri.toASCIIString());
|
builder.jwkSetUri(jwkSetUri.toASCIIString());
|
||||||
}
|
}
|
||||||
|
|
||||||
String userinfoEndpoint = (String) configuration.get("userinfo_endpoint");
|
String userinfoEndpoint = (String) configuration.get("userinfo_endpoint");
|
||||||
if (userinfoEndpoint != null) {
|
if (userinfoEndpoint != null) {
|
||||||
builder.userInfoUri(userinfoEndpoint);
|
builder.userInfoUri(userinfoEndpoint);
|
||||||
@ -221,7 +218,6 @@ public final class ClientRegistrations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T parse(Map<String, Object> body, ThrowingFunction<JSONObject, T, ParseException> parser) {
|
private static <T> T parse(Map<String, Object> body, ThrowingFunction<JSONObject, T, ParseException> parser) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return parser.apply(new JSONObject(body));
|
return parser.apply(new JSONObject(body));
|
||||||
}
|
}
|
||||||
@ -233,25 +229,19 @@ public final class ClientRegistrations {
|
|||||||
private static ClientRegistration.Builder withProviderConfiguration(AuthorizationServerMetadata metadata,
|
private static ClientRegistration.Builder withProviderConfiguration(AuthorizationServerMetadata metadata,
|
||||||
String issuer) {
|
String issuer) {
|
||||||
String metadataIssuer = metadata.getIssuer().getValue();
|
String metadataIssuer = metadata.getIssuer().getValue();
|
||||||
if (!issuer.equals(metadataIssuer)) {
|
Assert.state(issuer.equals(metadataIssuer),
|
||||||
throw new IllegalStateException(
|
() -> "The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did "
|
||||||
"The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did "
|
+ "not match the requested issuer \"" + issuer + "\"");
|
||||||
+ "not match the requested issuer \"" + issuer + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = URI.create(issuer).getHost();
|
String name = URI.create(issuer).getHost();
|
||||||
ClientAuthenticationMethod method = getClientAuthenticationMethod(issuer,
|
ClientAuthenticationMethod method = getClientAuthenticationMethod(issuer,
|
||||||
metadata.getTokenEndpointAuthMethods());
|
metadata.getTokenEndpointAuthMethods());
|
||||||
List<GrantType> grantTypes = metadata.getGrantTypes();
|
List<GrantType> grantTypes = metadata.getGrantTypes();
|
||||||
// If null, the default includes authorization_code
|
// If null, the default includes authorization_code
|
||||||
if (grantTypes != null && !grantTypes.contains(GrantType.AUTHORIZATION_CODE)) {
|
Assert.isTrue(grantTypes == null || grantTypes.contains(GrantType.AUTHORIZATION_CODE),
|
||||||
throw new IllegalArgumentException(
|
"Only AuthorizationGrantType.AUTHORIZATION_CODE is supported. The issuer \"" + issuer
|
||||||
"Only AuthorizationGrantType.AUTHORIZATION_CODE is supported. The issuer \"" + issuer
|
+ "\" returned a configuration of " + grantTypes);
|
||||||
+ "\" returned a configuration of " + grantTypes);
|
|
||||||
}
|
|
||||||
List<String> scopes = getScopes(metadata);
|
List<String> scopes = getScopes(metadata);
|
||||||
Map<String, Object> configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject());
|
Map<String, Object> configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject());
|
||||||
|
|
||||||
return ClientRegistration.withRegistrationId(name).userNameAttributeName(IdTokenClaimNames.SUB).scope(scopes)
|
return ClientRegistration.withRegistrationId(name).userNameAttributeName(IdTokenClaimNames.SUB).scope(scopes)
|
||||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).clientAuthenticationMethod(method)
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).clientAuthenticationMethod(method)
|
||||||
.redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}")
|
.redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}")
|
||||||
@ -284,9 +274,7 @@ public final class ClientRegistrations {
|
|||||||
// If null, default to "openid" which must be supported
|
// If null, default to "openid" which must be supported
|
||||||
return Collections.singletonList(OidcScopes.OPENID);
|
return Collections.singletonList(OidcScopes.OPENID);
|
||||||
}
|
}
|
||||||
else {
|
return scope.toStringList();
|
||||||
return scope.toStringList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ThrowingFunction<S, T, E extends Throwable> {
|
private interface ThrowingFunction<S, T, E extends Throwable> {
|
||||||
|
@ -67,9 +67,8 @@ public final class InMemoryClientRegistrationRepository
|
|||||||
private static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {
|
private static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {
|
||||||
ConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap<>();
|
ConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap<>();
|
||||||
for (ClientRegistration registration : registrations) {
|
for (ClientRegistration registration : registrations) {
|
||||||
if (result.containsKey(registration.getRegistrationId())) {
|
Assert.state(!result.containsKey(registration.getRegistrationId()),
|
||||||
throw new IllegalStateException(String.format("Duplicate key %s", registration.getRegistrationId()));
|
() -> String.format("Duplicate key %s", registration.getRegistrationId()));
|
||||||
}
|
|
||||||
result.put(registration.getRegistrationId(), registration);
|
result.put(registration.getRegistrationId(), registration);
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableMap(result);
|
return Collections.unmodifiableMap(result);
|
||||||
|
@ -88,22 +88,22 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService<OAuth
|
|||||||
if (customUserType == null) {
|
if (customUserType == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
|
||||||
|
ResponseEntity<? extends OAuth2User> response = getResponse(customUserType, request);
|
||||||
|
OAuth2User oauth2User = response.getBody();
|
||||||
|
return oauth2User;
|
||||||
|
}
|
||||||
|
|
||||||
ResponseEntity<? extends OAuth2User> response;
|
private ResponseEntity<? extends OAuth2User> getResponse(Class<? extends OAuth2User> customUserType,
|
||||||
|
RequestEntity<?> request) {
|
||||||
try {
|
try {
|
||||||
response = this.restOperations.exchange(request, customUserType);
|
return this.restOperations.exchange(request, customUserType);
|
||||||
}
|
}
|
||||||
catch (RestClientException ex) {
|
catch (RestClientException ex) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
|
||||||
"An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null);
|
"An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2User oauth2User = response.getBody();
|
|
||||||
|
|
||||||
return oauth2User;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,7 +87,6 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
||||||
Assert.notNull(userRequest, "userRequest cannot be null");
|
Assert.notNull(userRequest, "userRequest cannot be null");
|
||||||
|
|
||||||
if (!StringUtils
|
if (!StringUtils
|
||||||
.hasText(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {
|
.hasText(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,
|
||||||
@ -105,12 +104,21 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
|
|||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
|
RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
|
||||||
|
ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);
|
||||||
|
Map<String, Object> userAttributes = response.getBody();
|
||||||
|
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
|
||||||
|
authorities.add(new OAuth2UserAuthority(userAttributes));
|
||||||
|
OAuth2AccessToken token = userRequest.getAccessToken();
|
||||||
|
for (String authority : token.getScopes()) {
|
||||||
|
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
|
||||||
|
}
|
||||||
|
return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
|
||||||
|
}
|
||||||
|
|
||||||
ResponseEntity<Map<String, Object>> response;
|
private ResponseEntity<Map<String, Object>> getResponse(OAuth2UserRequest userRequest, RequestEntity<?> request) {
|
||||||
try {
|
try {
|
||||||
response = this.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE);
|
return this.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE);
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthorizationException ex) {
|
catch (OAuth2AuthorizationException ex) {
|
||||||
OAuth2Error oauth2Error = ex.getError();
|
OAuth2Error oauth2Error = ex.getError();
|
||||||
@ -145,16 +153,6 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
|
|||||||
"An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null);
|
"An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> userAttributes = response.getBody();
|
|
||||||
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
|
|
||||||
authorities.add(new OAuth2UserAuthority(userAttributes));
|
|
||||||
OAuth2AccessToken token = userRequest.getAccessToken();
|
|
||||||
for (String authority : token.getScopes()) {
|
|
||||||
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,13 +74,18 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
|
|
||||||
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute";
|
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute";
|
||||||
|
|
||||||
|
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final ParameterizedTypeReference<Map<String, String>> STRING_STRING_MAP = new ParameterizedTypeReference<Map<String, String>>() {
|
||||||
|
};
|
||||||
|
|
||||||
private WebClient webClient = WebClient.create();
|
private WebClient webClient = WebClient.create();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
Assert.notNull(userRequest, "userRequest cannot be null");
|
Assert.notNull(userRequest, "userRequest cannot be null");
|
||||||
|
|
||||||
String userInfoUri = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
|
String userInfoUri = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
|
||||||
.getUri();
|
.getUri();
|
||||||
if (!StringUtils.hasText(userInfoUri)) {
|
if (!StringUtils.hasText(userInfoUri)) {
|
||||||
@ -99,32 +104,17 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<Map<String, Object>>() {
|
|
||||||
};
|
|
||||||
|
|
||||||
AuthenticationMethod authenticationMethod = userRequest.getClientRegistration().getProviderDetails()
|
AuthenticationMethod authenticationMethod = userRequest.getClientRegistration().getProviderDetails()
|
||||||
.getUserInfoEndpoint().getAuthenticationMethod();
|
.getUserInfoEndpoint().getAuthenticationMethod();
|
||||||
WebClient.RequestHeadersSpec<?> requestHeadersSpec;
|
WebClient.RequestHeadersSpec<?> requestHeadersSpec = getRequestHeaderSpec(userRequest, userInfoUri,
|
||||||
if (AuthenticationMethod.FORM.equals(authenticationMethod)) {
|
authenticationMethod);
|
||||||
requestHeadersSpec = this.webClient.post().uri(userInfoUri)
|
|
||||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
|
||||||
.syncBody("access_token=" + userRequest.getAccessToken().getTokenValue());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
requestHeadersSpec = this.webClient.get().uri(userInfoUri)
|
|
||||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
.headers((headers) -> headers.setBearerAuth(userRequest.getAccessToken().getTokenValue()));
|
|
||||||
}
|
|
||||||
Mono<Map<String, Object>> userAttributes = requestHeadersSpec.retrieve()
|
Mono<Map<String, Object>> userAttributes = requestHeadersSpec.retrieve()
|
||||||
.onStatus((s) -> s != HttpStatus.OK, (response) -> parse(response).map((userInfoErrorResponse) -> {
|
.onStatus((s) -> s != HttpStatus.OK, (response) -> parse(response).map((userInfoErrorResponse) -> {
|
||||||
String description = userInfoErrorResponse.getErrorObject().getDescription();
|
String description = userInfoErrorResponse.getErrorObject().getDescription();
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, description,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, description,
|
||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
})).bodyToMono(typeReference);
|
})).bodyToMono(DefaultReactiveOAuth2UserService.STRING_OBJECT_MAP);
|
||||||
|
|
||||||
return userAttributes.map((attrs) -> {
|
return userAttributes.map((attrs) -> {
|
||||||
GrantedAuthority authority = new OAuth2UserAuthority(attrs);
|
GrantedAuthority authority = new OAuth2UserAuthority(attrs);
|
||||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||||
@ -136,13 +126,13 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
|
|
||||||
return new DefaultOAuth2User(authorities, attrs, userNameAttributeName);
|
return new DefaultOAuth2User(authorities, attrs, userNameAttributeName);
|
||||||
}).onErrorMap(IOException.class,
|
}).onErrorMap(IOException.class,
|
||||||
(e) -> new AuthenticationServiceException("Unable to access the userInfoEndpoint " + userInfoUri,
|
(ex) -> new AuthenticationServiceException("Unable to access the userInfoEndpoint " + userInfoUri,
|
||||||
e))
|
ex))
|
||||||
.onErrorMap(UnsupportedMediaTypeException.class, (e) -> {
|
.onErrorMap(UnsupportedMediaTypeException.class, (ex) -> {
|
||||||
String errorMessage = "An error occurred while attempting to retrieve the UserInfo Resource from '"
|
String errorMessage = "An error occurred while attempting to retrieve the UserInfo Resource from '"
|
||||||
+ userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
|
+ userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
|
||||||
.getUri()
|
.getUri()
|
||||||
+ "': response contains invalid content type '" + e.getContentType().toString() + "'. "
|
+ "': response contains invalid content type '" + ex.getContentType().toString() + "'. "
|
||||||
+ "The UserInfo Response should return a JSON object (content type 'application/json') "
|
+ "The UserInfo Response should return a JSON object (content type 'application/json') "
|
||||||
+ "that contains a collection of name and value pairs of the claims about the authenticated End-User. "
|
+ "that contains a collection of name and value pairs of the claims about the authenticated End-User. "
|
||||||
+ "Please ensure the UserInfo Uri in UserInfoEndpoint for Client Registration '"
|
+ "Please ensure the UserInfo Uri in UserInfoEndpoint for Client Registration '"
|
||||||
@ -151,7 +141,7 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
+ "as defined in OpenID Connect 1.0: 'https://openid.net/specs/openid-connect-core-1_0.html#UserInfo'";
|
+ "as defined in OpenID Connect 1.0: 'https://openid.net/specs/openid-connect-core-1_0.html#UserInfo'";
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorMessage,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorMessage,
|
||||||
null);
|
null);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), e);
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
|
||||||
}).onErrorMap((t) -> !(t instanceof AuthenticationServiceException), (t) -> {
|
}).onErrorMap((t) -> !(t instanceof AuthenticationServiceException), (t) -> {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
|
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
|
||||||
"An error occurred reading the UserInfo Success response: " + t.getMessage(), null);
|
"An error occurred reading the UserInfo Success response: " + t.getMessage(), null);
|
||||||
@ -160,6 +150,17 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WebClient.RequestHeadersSpec<?> getRequestHeaderSpec(OAuth2UserRequest userRequest, String userInfoUri,
|
||||||
|
AuthenticationMethod authenticationMethod) {
|
||||||
|
if (AuthenticationMethod.FORM.equals(authenticationMethod)) {
|
||||||
|
return this.webClient.post().uri(userInfoUri).header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
|
.syncBody("access_token=" + userRequest.getAccessToken().getTokenValue());
|
||||||
|
}
|
||||||
|
return this.webClient.get().uri(userInfoUri).header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.headers((headers) -> headers.setBearerAuth(userRequest.getAccessToken().getTokenValue()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link WebClient} used for retrieving the user endpoint
|
* Sets the {@link WebClient} used for retrieving the user endpoint
|
||||||
* @param webClient the client to use
|
* @param webClient the client to use
|
||||||
@ -170,18 +171,13 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Mono<UserInfoErrorResponse> parse(ClientResponse httpResponse) {
|
private static Mono<UserInfoErrorResponse> parse(ClientResponse httpResponse) {
|
||||||
|
|
||||||
String wwwAuth = httpResponse.headers().asHttpHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);
|
String wwwAuth = httpResponse.headers().asHttpHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(wwwAuth)) {
|
if (!StringUtils.isEmpty(wwwAuth)) {
|
||||||
// Bearer token error?
|
// Bearer token error?
|
||||||
return Mono.fromCallable(() -> UserInfoErrorResponse.parse(wwwAuth));
|
return Mono.fromCallable(() -> UserInfoErrorResponse.parse(wwwAuth));
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterizedTypeReference<Map<String, String>> typeReference = new ParameterizedTypeReference<Map<String, String>>() {
|
|
||||||
};
|
|
||||||
// Other error?
|
// Other error?
|
||||||
return httpResponse.bodyToMono(typeReference)
|
return httpResponse.bodyToMono(STRING_STRING_MAP)
|
||||||
.map((body) -> new UserInfoErrorResponse(ErrorObject.parse(new JSONObject(body))));
|
.map((body) -> new UserInfoErrorResponse(ErrorObject.parse(new JSONObject(body))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +54,7 @@ public class OAuth2UserRequestEntityConverter implements Converter<OAuth2UserReq
|
|||||||
@Override
|
@Override
|
||||||
public RequestEntity<?> convert(OAuth2UserRequest userRequest) {
|
public RequestEntity<?> convert(OAuth2UserRequest userRequest) {
|
||||||
ClientRegistration clientRegistration = userRequest.getClientRegistration();
|
ClientRegistration clientRegistration = userRequest.getClientRegistration();
|
||||||
|
HttpMethod httpMethod = getHttpMethod(clientRegistration);
|
||||||
HttpMethod httpMethod = HttpMethod.GET;
|
|
||||||
if (AuthenticationMethod.FORM
|
|
||||||
.equals(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())) {
|
|
||||||
httpMethod = HttpMethod.POST;
|
|
||||||
}
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||||
URI uri = UriComponentsBuilder
|
URI uri = UriComponentsBuilder
|
||||||
@ -80,4 +75,12 @@ public class OAuth2UserRequestEntityConverter implements Converter<OAuth2UserReq
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpMethod getHttpMethod(ClientRegistration clientRegistration) {
|
||||||
|
if (AuthenticationMethod.FORM
|
||||||
|
.equals(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())) {
|
||||||
|
return HttpMethod.POST;
|
||||||
|
}
|
||||||
|
return HttpMethod.GET;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,7 @@ public final class AuthenticatedPrincipalOAuth2AuthorizedClientRepository implem
|
|||||||
if (this.isPrincipalAuthenticated(principal)) {
|
if (this.isPrincipalAuthenticated(principal)) {
|
||||||
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());
|
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());
|
||||||
}
|
}
|
||||||
else {
|
return this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, request);
|
||||||
return this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal,
|
|
||||||
request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -143,41 +143,13 @@ public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2Au
|
|||||||
if (registrationId == null) {
|
if (registrationId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
||||||
if (clientRegistration == null) {
|
if (clientRegistration == null) {
|
||||||
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
|
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> attributes = new HashMap<>();
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||||
|
OAuth2AuthorizationRequest.Builder builder = getBuilder(clientRegistration, attributes);
|
||||||
OAuth2AuthorizationRequest.Builder builder;
|
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
|
||||||
builder = OAuth2AuthorizationRequest.authorizationCode();
|
|
||||||
Map<String, Object> additionalParameters = new HashMap<>();
|
|
||||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())
|
|
||||||
&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
|
|
||||||
// Section 3.1.2.1 Authentication Request -
|
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
|
||||||
// scope
|
|
||||||
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope
|
|
||||||
// value.
|
|
||||||
addNonceParameters(attributes, additionalParameters);
|
|
||||||
}
|
|
||||||
if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
|
|
||||||
addPkceParameters(attributes, additionalParameters);
|
|
||||||
}
|
|
||||||
builder.additionalParameters(additionalParameters);
|
|
||||||
}
|
|
||||||
else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
|
|
||||||
builder = OAuth2AuthorizationRequest.implicit();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
|
|
||||||
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
|
|
||||||
}
|
|
||||||
|
|
||||||
String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
|
String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
|
||||||
|
|
||||||
@ -191,6 +163,33 @@ public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2Au
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizationRequest.Builder getBuilder(ClientRegistration clientRegistration,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
|
OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode();
|
||||||
|
Map<String, Object> additionalParameters = new HashMap<>();
|
||||||
|
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())
|
||||||
|
&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
|
||||||
|
// Section 3.1.2.1 Authentication Request -
|
||||||
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope
|
||||||
|
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope
|
||||||
|
// value.
|
||||||
|
addNonceParameters(attributes, additionalParameters);
|
||||||
|
}
|
||||||
|
if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
|
||||||
|
addPkceParameters(attributes, additionalParameters);
|
||||||
|
}
|
||||||
|
builder.additionalParameters(additionalParameters);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
|
return OAuth2AuthorizationRequest.implicit();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
|
||||||
|
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
|
||||||
|
}
|
||||||
|
|
||||||
private String resolveRegistrationId(HttpServletRequest request) {
|
private String resolveRegistrationId(HttpServletRequest request) {
|
||||||
if (this.authorizationRequestMatcher.matches(request)) {
|
if (this.authorizationRequestMatcher.matches(request)) {
|
||||||
return this.authorizationRequestMatcher.matcher(request).getVariables()
|
return this.authorizationRequestMatcher.matcher(request).getVariables()
|
||||||
@ -220,7 +219,6 @@ public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2Au
|
|||||||
String action) {
|
String action) {
|
||||||
Map<String, String> uriVariables = new HashMap<>();
|
Map<String, String> uriVariables = new HashMap<>();
|
||||||
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
|
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
|
||||||
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
|
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
|
||||||
.replacePath(request.getContextPath()).replaceQuery(null).fragment(null).build();
|
.replacePath(request.getContextPath()).replaceQuery(null).fragment(null).build();
|
||||||
String scheme = uriComponents.getScheme();
|
String scheme = uriComponents.getScheme();
|
||||||
@ -238,9 +236,7 @@ public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2Au
|
|||||||
}
|
}
|
||||||
uriVariables.put("basePath", (path != null) ? path : "");
|
uriVariables.put("basePath", (path != null) ? path : "");
|
||||||
uriVariables.put("baseUrl", uriComponents.toUriString());
|
uriVariables.put("baseUrl", uriComponents.toUriString());
|
||||||
|
|
||||||
uriVariables.put("action", (action != null) ? action : "");
|
uriVariables.put("action", (action != null) ? action : "");
|
||||||
|
|
||||||
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri()).buildAndExpand(uriVariables)
|
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri()).buildAndExpand(uriVariables)
|
||||||
.toUriString();
|
.toUriString();
|
||||||
}
|
}
|
||||||
|
@ -131,16 +131,13 @@ public final class DefaultOAuth2AuthorizedClientManager implements OAuth2Authori
|
|||||||
@Override
|
@Override
|
||||||
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
||||||
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
||||||
|
|
||||||
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
||||||
OAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();
|
OAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();
|
||||||
Authentication principal = authorizeRequest.getPrincipal();
|
Authentication principal = authorizeRequest.getPrincipal();
|
||||||
|
|
||||||
HttpServletRequest servletRequest = getHttpServletRequestOrDefault(authorizeRequest.getAttributes());
|
HttpServletRequest servletRequest = getHttpServletRequestOrDefault(authorizeRequest.getAttributes());
|
||||||
Assert.notNull(servletRequest, "servletRequest cannot be null");
|
Assert.notNull(servletRequest, "servletRequest cannot be null");
|
||||||
HttpServletResponse servletResponse = getHttpServletResponseOrDefault(authorizeRequest.getAttributes());
|
HttpServletResponse servletResponse = getHttpServletResponseOrDefault(authorizeRequest.getAttributes());
|
||||||
Assert.notNull(servletResponse, "servletResponse cannot be null");
|
Assert.notNull(servletResponse, "servletResponse cannot be null");
|
||||||
|
|
||||||
OAuth2AuthorizationContext.Builder contextBuilder;
|
OAuth2AuthorizationContext.Builder contextBuilder;
|
||||||
if (authorizedClient != null) {
|
if (authorizedClient != null) {
|
||||||
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
|
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
|
||||||
@ -166,7 +163,6 @@ public final class DefaultOAuth2AuthorizedClientManager implements OAuth2Authori
|
|||||||
attributes.putAll(contextAttributes);
|
attributes.putAll(contextAttributes);
|
||||||
}
|
}
|
||||||
}).build();
|
}).build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);
|
authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);
|
||||||
}
|
}
|
||||||
@ -175,7 +171,6 @@ public final class DefaultOAuth2AuthorizedClientManager implements OAuth2Authori
|
|||||||
createAttributes(servletRequest, servletResponse));
|
createAttributes(servletRequest, servletResponse));
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizedClient != null) {
|
if (authorizedClient != null) {
|
||||||
this.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,
|
this.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,
|
||||||
createAttributes(servletRequest, servletResponse));
|
createAttributes(servletRequest, servletResponse));
|
||||||
@ -189,7 +184,6 @@ public final class DefaultOAuth2AuthorizedClientManager implements OAuth2Authori
|
|||||||
return authorizationContext.getAuthorizedClient();
|
return authorizationContext.getAuthorizedClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authorizedClient;
|
return authorizedClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,10 +136,8 @@ public final class DefaultReactiveOAuth2AuthorizedClientManager implements React
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
||||||
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
||||||
|
|
||||||
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
||||||
Authentication principal = authorizeRequest.getPrincipal();
|
Authentication principal = authorizeRequest.getPrincipal();
|
||||||
|
|
||||||
return Mono.justOrEmpty(authorizeRequest.<ServerWebExchange>getAttribute(ServerWebExchange.class.getName()))
|
return Mono.justOrEmpty(authorizeRequest.<ServerWebExchange>getAttribute(ServerWebExchange.class.getName()))
|
||||||
.switchIfEmpty(currentServerWebExchangeMono)
|
.switchIfEmpty(currentServerWebExchangeMono)
|
||||||
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException("serverWebExchange cannot be null")))
|
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException("serverWebExchange cannot be null")))
|
||||||
@ -183,7 +181,6 @@ public final class DefaultReactiveOAuth2AuthorizedClientManager implements React
|
|||||||
*/
|
*/
|
||||||
private Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,
|
private Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,
|
||||||
Authentication principal, ServerWebExchange serverWebExchange) {
|
Authentication principal, ServerWebExchange serverWebExchange) {
|
||||||
|
|
||||||
return this.authorizedClientProvider.authorize(authorizationContext)
|
return this.authorizedClientProvider.authorize(authorizationContext)
|
||||||
// Delegate to the authorizationSuccessHandler of the successful
|
// Delegate to the authorizationSuccessHandler of the successful
|
||||||
// authorization
|
// authorization
|
||||||
|
@ -161,12 +161,10 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
if (matchesAuthorizationResponse(request)) {
|
if (matchesAuthorizationResponse(request)) {
|
||||||
processAuthorizationResponse(request, response);
|
processAuthorizationResponse(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +178,6 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||||||
if (authorizationRequest == null) {
|
if (authorizationRequest == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare redirect_uri
|
// Compare redirect_uri
|
||||||
UriComponents requestUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).build();
|
UriComponents requestUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).build();
|
||||||
UriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).build();
|
UriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).build();
|
||||||
@ -193,7 +190,6 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||||||
// before doing an exact comparison with the authorizationRequest.getRedirectUri()
|
// before doing an exact comparison with the authorizationRequest.getRedirectUri()
|
||||||
// parameters (if any)
|
// parameters (if any)
|
||||||
requestUriParameters.retainAll(redirectUriParameters);
|
requestUriParameters.retainAll(redirectUriParameters);
|
||||||
|
|
||||||
if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())
|
if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())
|
||||||
&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())
|
&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())
|
||||||
&& Objects.equals(requestUri.getHost(), redirectUri.getHost())
|
&& Objects.equals(requestUri.getHost(), redirectUri.getHost())
|
||||||
@ -207,24 +203,18 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
private void processAuthorizationResponse(HttpServletRequest request, HttpServletResponse response)
|
private void processAuthorizationResponse(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository
|
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository
|
||||||
.removeAuthorizationRequest(request, response);
|
.removeAuthorizationRequest(request, response);
|
||||||
|
|
||||||
String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
|
String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
|
||||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
||||||
|
|
||||||
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
|
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
|
||||||
String redirectUri = UrlUtils.buildFullRequestUrl(request);
|
String redirectUri = UrlUtils.buildFullRequestUrl(request);
|
||||||
OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,
|
OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,
|
||||||
redirectUri);
|
redirectUri);
|
||||||
|
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(
|
OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(
|
||||||
clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
|
clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
|
||||||
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||||
|
|
||||||
OAuth2AuthorizationCodeAuthenticationToken authenticationResult;
|
OAuth2AuthorizationCodeAuthenticationToken authenticationResult;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationManager
|
authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationManager
|
||||||
.authenticate(authenticationRequest);
|
.authenticate(authenticationRequest);
|
||||||
@ -242,24 +232,19 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||||||
this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString());
|
this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
String principalName = (currentAuthentication != null) ? currentAuthentication.getName() : "anonymousUser";
|
String principalName = (currentAuthentication != null) ? currentAuthentication.getName() : "anonymousUser";
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||||
authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),
|
authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),
|
||||||
authenticationResult.getRefreshToken());
|
authenticationResult.getRefreshToken());
|
||||||
|
|
||||||
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request,
|
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request,
|
||||||
response);
|
response);
|
||||||
|
|
||||||
String redirectUrl = authorizationRequest.getRedirectUri();
|
String redirectUrl = authorizationRequest.getRedirectUri();
|
||||||
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
|
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
|
||||||
if (savedRequest != null) {
|
if (savedRequest != null) {
|
||||||
redirectUrl = savedRequest.getRedirectUrl();
|
redirectUrl = savedRequest.getRedirectUrl();
|
||||||
this.requestCache.removeRequest(request, response);
|
this.requestCache.removeRequest(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.redirectStrategy.sendRedirect(request, response, redirectUrl);
|
this.redirectStrategy.sendRedirect(request, response, redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.core.log.LogMessage;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
@ -162,7 +163,6 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
|
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
|
||||||
if (authorizationRequest != null) {
|
if (authorizationRequest != null) {
|
||||||
@ -170,11 +170,10 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception failed) {
|
catch (Exception ex) {
|
||||||
this.unsuccessfulRedirectForAuthorization(request, response, failed);
|
this.unsuccessfulRedirectForAuthorization(request, response, ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
@ -201,22 +200,18 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ex instanceof ServletException) {
|
if (ex instanceof ServletException) {
|
||||||
throw (ServletException) ex;
|
throw (ServletException) ex;
|
||||||
}
|
}
|
||||||
else if (ex instanceof RuntimeException) {
|
if (ex instanceof RuntimeException) {
|
||||||
throw (RuntimeException) ex;
|
throw (RuntimeException) ex;
|
||||||
}
|
}
|
||||||
else {
|
throw new RuntimeException(ex);
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
|
private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
|
||||||
OAuth2AuthorizationRequest authorizationRequest) throws IOException {
|
OAuth2AuthorizationRequest authorizationRequest) throws IOException {
|
||||||
|
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
|
||||||
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
|
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
|
||||||
}
|
}
|
||||||
@ -225,11 +220,8 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void unsuccessfulRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
|
private void unsuccessfulRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
|
||||||
Exception failed) throws IOException {
|
Exception ex) throws IOException {
|
||||||
|
this.logger.error(LogMessage.format("Authorization Request failed: %s", ex, ex));
|
||||||
if (this.logger.isErrorEnabled()) {
|
|
||||||
this.logger.error("Authorization Request failed: " + failed.toString(), failed);
|
|
||||||
}
|
|
||||||
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),
|
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
|
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
|
||||||
}
|
}
|
||||||
|
@ -66,16 +66,13 @@ final class OAuth2AuthorizationResponseUtils {
|
|||||||
String code = request.getFirst(OAuth2ParameterNames.CODE);
|
String code = request.getFirst(OAuth2ParameterNames.CODE);
|
||||||
String errorCode = request.getFirst(OAuth2ParameterNames.ERROR);
|
String errorCode = request.getFirst(OAuth2ParameterNames.ERROR);
|
||||||
String state = request.getFirst(OAuth2ParameterNames.STATE);
|
String state = request.getFirst(OAuth2ParameterNames.STATE);
|
||||||
|
|
||||||
if (StringUtils.hasText(code)) {
|
if (StringUtils.hasText(code)) {
|
||||||
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
|
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
|
||||||
}
|
}
|
||||||
else {
|
String errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);
|
||||||
String errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);
|
String errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);
|
||||||
String errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);
|
return OAuth2AuthorizationResponse.error(errorCode).redirectUri(redirectUri).errorDescription(errorDescription)
|
||||||
return OAuth2AuthorizationResponse.error(errorCode).redirectUri(redirectUri)
|
.errorUri(errorUri).state(state).build();
|
||||||
.errorDescription(errorDescription).errorUri(errorUri).state(state).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -158,20 +158,17 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
|||||||
@Override
|
@Override
|
||||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws AuthenticationException {
|
throws AuthenticationException {
|
||||||
|
|
||||||
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
|
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
|
||||||
if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
|
if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
|
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository
|
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository
|
||||||
.removeAuthorizationRequest(request, response);
|
.removeAuthorizationRequest(request, response);
|
||||||
if (authorizationRequest == null) {
|
if (authorizationRequest == null) {
|
||||||
OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);
|
OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
|
String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
|
||||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
|
||||||
if (clientRegistration == null) {
|
if (clientRegistration == null) {
|
||||||
@ -183,26 +180,21 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
|||||||
.build().toUriString();
|
.build().toUriString();
|
||||||
OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,
|
OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,
|
||||||
redirectUri);
|
redirectUri);
|
||||||
|
|
||||||
Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
|
Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
|
||||||
OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration,
|
OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration,
|
||||||
new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
|
new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
|
||||||
authenticationRequest.setDetails(authenticationDetails);
|
authenticationRequest.setDetails(authenticationDetails);
|
||||||
|
|
||||||
OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this
|
OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this
|
||||||
.getAuthenticationManager().authenticate(authenticationRequest);
|
.getAuthenticationManager().authenticate(authenticationRequest);
|
||||||
|
|
||||||
OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
|
OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
|
||||||
authenticationResult.getPrincipal(), authenticationResult.getAuthorities(),
|
authenticationResult.getPrincipal(), authenticationResult.getAuthorities(),
|
||||||
authenticationResult.getClientRegistration().getRegistrationId());
|
authenticationResult.getClientRegistration().getRegistrationId());
|
||||||
oauth2Authentication.setDetails(authenticationDetails);
|
oauth2Authentication.setDetails(authenticationDetails);
|
||||||
|
|
||||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||||
authenticationResult.getClientRegistration(), oauth2Authentication.getName(),
|
authenticationResult.getClientRegistration(), oauth2Authentication.getName(),
|
||||||
authenticationResult.getAccessToken(), authenticationResult.getRefreshToken());
|
authenticationResult.getAccessToken(), authenticationResult.getRefreshToken());
|
||||||
|
|
||||||
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
|
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
|
||||||
|
|
||||||
return oauth2Authentication;
|
return oauth2Authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,46 +114,38 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
|
|||||||
@Override
|
@Override
|
||||||
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
|
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
|
||||||
|
|
||||||
String clientRegistrationId = this.resolveClientRegistrationId(parameter);
|
String clientRegistrationId = this.resolveClientRegistrationId(parameter);
|
||||||
if (StringUtils.isEmpty(clientRegistrationId)) {
|
if (StringUtils.isEmpty(clientRegistrationId)) {
|
||||||
throw new IllegalArgumentException("Unable to resolve the Client Registration Identifier. "
|
throw new IllegalArgumentException("Unable to resolve the Client Registration Identifier. "
|
||||||
+ "It must be provided via @RegisteredOAuth2AuthorizedClient(\"client1\") or "
|
+ "It must be provided via @RegisteredOAuth2AuthorizedClient(\"client1\") or "
|
||||||
+ "@RegisteredOAuth2AuthorizedClient(registrationId = \"client1\").");
|
+ "@RegisteredOAuth2AuthorizedClient(registrationId = \"client1\").");
|
||||||
}
|
}
|
||||||
|
|
||||||
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
|
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
principal = ANONYMOUS_AUTHENTICATION;
|
principal = ANONYMOUS_AUTHENTICATION;
|
||||||
}
|
}
|
||||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||||
HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
|
HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
|
||||||
|
|
||||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
||||||
.principal(principal).attribute(HttpServletRequest.class.getName(), servletRequest)
|
.principal(principal).attribute(HttpServletRequest.class.getName(), servletRequest)
|
||||||
.attribute(HttpServletResponse.class.getName(), servletResponse).build();
|
.attribute(HttpServletResponse.class.getName(), servletResponse).build();
|
||||||
|
|
||||||
return this.authorizedClientManager.authorize(authorizeRequest);
|
return this.authorizedClientManager.authorize(authorizeRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveClientRegistrationId(MethodParameter parameter) {
|
private String resolveClientRegistrationId(MethodParameter parameter) {
|
||||||
RegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils
|
RegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils
|
||||||
.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);
|
.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);
|
||||||
|
|
||||||
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
|
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
String clientRegistrationId = null;
|
|
||||||
if (!StringUtils.isEmpty(authorizedClientAnnotation.registrationId())) {
|
if (!StringUtils.isEmpty(authorizedClientAnnotation.registrationId())) {
|
||||||
clientRegistrationId = authorizedClientAnnotation.registrationId();
|
return authorizedClientAnnotation.registrationId();
|
||||||
}
|
}
|
||||||
else if (!StringUtils.isEmpty(authorizedClientAnnotation.value())) {
|
if (!StringUtils.isEmpty(authorizedClientAnnotation.value())) {
|
||||||
clientRegistrationId = authorizedClientAnnotation.value();
|
return authorizedClientAnnotation.value();
|
||||||
}
|
}
|
||||||
else if (principal != null && OAuth2AuthenticationToken.class.isAssignableFrom(principal.getClass())) {
|
if (principal != null && OAuth2AuthenticationToken.class.isAssignableFrom(principal.getClass())) {
|
||||||
clientRegistrationId = ((OAuth2AuthenticationToken) principal).getAuthorizedClientRegistrationId();
|
return ((OAuth2AuthenticationToken) principal).getAuthorizedClientRegistrationId();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return clientRegistrationId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,7 +176,6 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
|
|||||||
|
|
||||||
private void updateDefaultAuthorizedClientManager(
|
private void updateDefaultAuthorizedClientManager(
|
||||||
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient) {
|
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient) {
|
||||||
|
|
||||||
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
||||||
.authorizationCode().refreshToken()
|
.authorizationCode().refreshToken()
|
||||||
.clientCredentials(
|
.clientCredentials(
|
||||||
|
@ -206,12 +206,10 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
public ServerOAuth2AuthorizedClientExchangeFilterFunction(
|
public ServerOAuth2AuthorizedClientExchangeFilterFunction(
|
||||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
|
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||||
|
|
||||||
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(
|
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(
|
||||||
(clientRegistrationId, principal, attributes) -> authorizedClientRepository.removeAuthorizedClient(
|
(clientRegistrationId, principal, attributes) -> authorizedClientRepository.removeAuthorizedClient(
|
||||||
clientRegistrationId, principal,
|
clientRegistrationId, principal,
|
||||||
(ServerWebExchange) attributes.get(ServerWebExchange.class.getName())));
|
(ServerWebExchange) attributes.get(ServerWebExchange.class.getName())));
|
||||||
|
|
||||||
this.authorizedClientManager = createDefaultAuthorizedClientManager(clientRegistrationRepository,
|
this.authorizedClientManager = createDefaultAuthorizedClientManager(clientRegistrationRepository,
|
||||||
authorizedClientRepository, authorizationFailureHandler);
|
authorizedClientRepository, authorizationFailureHandler);
|
||||||
this.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);
|
this.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);
|
||||||
@ -222,7 +220,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
|
ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||||
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
||||||
|
|
||||||
// gh-7544
|
// gh-7544
|
||||||
if (authorizedClientRepository instanceof UnAuthenticatedServerOAuth2AuthorizedClientRepository) {
|
if (authorizedClientRepository instanceof UnAuthenticatedServerOAuth2AuthorizedClientRepository) {
|
||||||
UnAuthenticatedReactiveOAuth2AuthorizedClientManager unauthenticatedAuthorizedClientManager = new UnAuthenticatedReactiveOAuth2AuthorizedClientManager(
|
UnAuthenticatedReactiveOAuth2AuthorizedClientManager unauthenticatedAuthorizedClientManager = new UnAuthenticatedReactiveOAuth2AuthorizedClientManager(
|
||||||
@ -234,11 +231,9 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
.authorizationCode().refreshToken().clientCredentials().password().build());
|
.authorizationCode().refreshToken().clientCredentials().password().build());
|
||||||
return unauthenticatedAuthorizedClientManager;
|
return unauthenticatedAuthorizedClientManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
||||||
clientRegistrationRepository, authorizedClientRepository);
|
clientRegistrationRepository, authorizedClientRepository);
|
||||||
authorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);
|
authorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);
|
||||||
|
|
||||||
return authorizedClientManager;
|
return authorizedClientManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,9 +439,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
|
|
||||||
private Mono<OAuth2AuthorizeRequest> authorizeRequest(ClientRequest request) {
|
private Mono<OAuth2AuthorizeRequest> authorizeRequest(ClientRequest request) {
|
||||||
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
||||||
|
|
||||||
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
||||||
|
|
||||||
return Mono.zip(clientRegistrationId, this.currentAuthenticationMono, serverWebExchange).map((t3) -> {
|
return Mono.zip(clientRegistrationId, this.currentAuthenticationMono, serverWebExchange).map((t3) -> {
|
||||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(t3.getT1())
|
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(t3.getT1())
|
||||||
.principal(t3.getT2());
|
.principal(t3.getT2());
|
||||||
@ -488,7 +481,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
private Mono<OAuth2AuthorizeRequest> reauthorizeRequest(ClientRequest request,
|
private Mono<OAuth2AuthorizeRequest> reauthorizeRequest(ClientRequest request,
|
||||||
OAuth2AuthorizedClient authorizedClient) {
|
OAuth2AuthorizedClient authorizedClient) {
|
||||||
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
||||||
|
|
||||||
return Mono.zip(this.currentAuthenticationMono, serverWebExchange).map((t2) -> {
|
return Mono.zip(this.currentAuthenticationMono, serverWebExchange).map((t2) -> {
|
||||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)
|
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)
|
||||||
.principal(t2.getT1());
|
.principal(t2.getT1());
|
||||||
@ -561,27 +553,39 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
|
||||||
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
|
||||||
|
|
||||||
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
|
||||||
Authentication principal = authorizeRequest.getPrincipal();
|
Authentication principal = authorizeRequest.getPrincipal();
|
||||||
|
return Mono.justOrEmpty(authorizeRequest.getAuthorizedClient())
|
||||||
|
.switchIfEmpty(loadAuthorizedClient(clientRegistrationId, principal))
|
||||||
|
.flatMap((authorizedClient) -> reauthorize(authorizedClient, authorizeRequest, principal))
|
||||||
|
.switchIfEmpty(findAndAuthorize(clientRegistrationId, principal));
|
||||||
|
}
|
||||||
|
|
||||||
return Mono.justOrEmpty(authorizeRequest.getAuthorizedClient()).switchIfEmpty(Mono.defer(
|
private Mono<OAuth2AuthorizedClient> loadAuthorizedClient(String clientRegistrationId,
|
||||||
() -> this.authorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, null)))
|
Authentication principal) {
|
||||||
.flatMap((authorizedClient) -> // Re-authorize
|
return Mono.defer(
|
||||||
Mono.just(OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient).principal(principal)
|
() -> this.authorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, null));
|
||||||
.build()).flatMap((authorizationContext) -> authorize(authorizationContext, principal))
|
}
|
||||||
// Default to the existing authorizedClient if the client
|
|
||||||
// was not re-authorized
|
private Mono<OAuth2AuthorizedClient> reauthorize(OAuth2AuthorizedClient authorizedClient,
|
||||||
.defaultIfEmpty((authorizeRequest.getAuthorizedClient() != null)
|
OAuth2AuthorizeRequest authorizeRequest, Authentication principal) {
|
||||||
? authorizeRequest.getAuthorizedClient() : authorizedClient))
|
return Mono
|
||||||
.switchIfEmpty(Mono.defer(() ->
|
.just(OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient).principal(principal)
|
||||||
// Authorize
|
.build())
|
||||||
this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)
|
.flatMap((authorizationContext) -> authorize(authorizationContext, principal))
|
||||||
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(
|
// Default to the existing authorizedClient if the client was not
|
||||||
"Could not find ClientRegistration with id '" + clientRegistrationId + "'")))
|
// re-authorized
|
||||||
.flatMap((clientRegistration) -> Mono.just(OAuth2AuthorizationContext
|
.defaultIfEmpty((authorizeRequest.getAuthorizedClient() != null)
|
||||||
.withClientRegistration(clientRegistration).principal(principal).build()))
|
? authorizeRequest.getAuthorizedClient() : authorizedClient);
|
||||||
.flatMap((authorizationContext) -> authorize(authorizationContext, principal))));
|
}
|
||||||
|
|
||||||
|
private Mono<OAuth2AuthorizedClient> findAndAuthorize(String clientRegistrationId, Authentication principal) {
|
||||||
|
return Mono.defer(() -> this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)
|
||||||
|
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(
|
||||||
|
"Could not find ClientRegistration with id '" + clientRegistrationId + "'")))
|
||||||
|
.flatMap((clientRegistration) -> Mono.just(OAuth2AuthorizationContext
|
||||||
|
.withClientRegistration(clientRegistration).principal(principal).build()))
|
||||||
|
.flatMap((authorizationContext) -> authorize(authorizationContext, principal)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -597,7 +601,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
*/
|
*/
|
||||||
private Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,
|
private Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,
|
||||||
Authentication principal) {
|
Authentication principal) {
|
||||||
|
|
||||||
return this.authorizedClientProvider.authorize(authorizationContext)
|
return this.authorizedClientProvider.authorize(authorizationContext)
|
||||||
// Delegates to the authorizationSuccessHandler of the successful
|
// Delegates to the authorizationSuccessHandler of the successful
|
||||||
// authorization
|
// authorization
|
||||||
@ -642,7 +645,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
private AuthorizationFailureForwarder(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
private AuthorizationFailureForwarder(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
||||||
Assert.notNull(authorizationFailureHandler, "authorizationFailureHandler cannot be null");
|
Assert.notNull(authorizationFailureHandler, "authorizationFailureHandler cannot be null");
|
||||||
this.authorizationFailureHandler = authorizationFailureHandler;
|
this.authorizationFailureHandler = authorizationFailureHandler;
|
||||||
|
|
||||||
Map<Integer, String> httpStatusToOAuth2Error = new HashMap<>();
|
Map<Integer, String> httpStatusToOAuth2Error = new HashMap<>();
|
||||||
httpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED.value(), OAuth2ErrorCodes.INVALID_TOKEN);
|
httpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED.value(), OAuth2ErrorCodes.INVALID_TOKEN);
|
||||||
httpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN.value(), OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
httpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN.value(), OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
||||||
@ -661,17 +663,12 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
private Mono<Void> handleResponse(ClientRequest request, ClientResponse response) {
|
private Mono<Void> handleResponse(ClientRequest request, ClientResponse response) {
|
||||||
return Mono.justOrEmpty(resolveErrorIfPossible(response)).flatMap((oauth2Error) -> {
|
return Mono.justOrEmpty(resolveErrorIfPossible(response)).flatMap((oauth2Error) -> {
|
||||||
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
||||||
|
|
||||||
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
||||||
|
|
||||||
return Mono
|
return Mono
|
||||||
.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
||||||
serverWebExchange, clientRegistrationId)
|
serverWebExchange, clientRegistrationId)
|
||||||
.flatMap((tuple3) -> handleAuthorizationFailure(tuple3.getT1(), // Authentication
|
.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(),
|
||||||
// principal
|
new ClientAuthorizationException(oauth2Error, zipped.getT3())));
|
||||||
tuple3.getT2().orElse(null), // ServerWebExchange exchange
|
|
||||||
new ClientAuthorizationException(oauth2Error, tuple3.getT3()))); // String
|
|
||||||
// clientRegistrationId
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,18 +717,12 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
WebClientResponseException exception) {
|
WebClientResponseException exception) {
|
||||||
return Mono.justOrEmpty(resolveErrorIfPossible(exception.getRawStatusCode())).flatMap((oauth2Error) -> {
|
return Mono.justOrEmpty(resolveErrorIfPossible(exception.getRawStatusCode())).flatMap((oauth2Error) -> {
|
||||||
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
||||||
|
|
||||||
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
Mono<String> clientRegistrationId = effectiveClientRegistrationId(request);
|
||||||
|
|
||||||
return Mono
|
return Mono
|
||||||
.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
||||||
serverWebExchange, clientRegistrationId)
|
serverWebExchange, clientRegistrationId)
|
||||||
.flatMap((tuple3) -> handleAuthorizationFailure(tuple3.getT1(), // Authentication
|
.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(),
|
||||||
// principal
|
new ClientAuthorizationException(oauth2Error, zipped.getT3(), exception)));
|
||||||
tuple3.getT2().orElse(null), // ServerWebExchange exchange
|
|
||||||
new ClientAuthorizationException(oauth2Error, tuple3.getT3(), // String
|
|
||||||
// clientRegistrationId
|
|
||||||
exception)));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,14 +736,10 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
*/
|
*/
|
||||||
private Mono<Void> handleAuthorizationException(ClientRequest request, OAuth2AuthorizationException exception) {
|
private Mono<Void> handleAuthorizationException(ClientRequest request, OAuth2AuthorizationException exception) {
|
||||||
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
Mono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);
|
||||||
|
return Mono
|
||||||
return Mono.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,
|
||||||
serverWebExchange).flatMap(
|
serverWebExchange)
|
||||||
(tuple2) -> handleAuthorizationFailure(tuple2.getT1(), // Authentication
|
.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(), exception));
|
||||||
// principal
|
|
||||||
tuple2.getT2().orElse(null), // ServerWebExchange
|
|
||||||
// exchange
|
|
||||||
exception));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -763,11 +750,10 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
|||||||
* @return a {@link Mono} that completes empty after the authorization failure
|
* @return a {@link Mono} that completes empty after the authorization failure
|
||||||
* handler completes.
|
* handler completes.
|
||||||
*/
|
*/
|
||||||
private Mono<Void> handleAuthorizationFailure(Authentication principal, ServerWebExchange exchange,
|
private Mono<Void> handleAuthorizationFailure(Authentication principal, Optional<ServerWebExchange> exchange,
|
||||||
OAuth2AuthorizationException exception) {
|
OAuth2AuthorizationException exception) {
|
||||||
|
|
||||||
return this.authorizationFailureHandler.onAuthorizationFailure(exception, principal,
|
return this.authorizationFailureHandler.onAuthorizationFailure(exception, principal,
|
||||||
createAttributes(exchange));
|
createAttributes(exchange.orElse(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> createAttributes(ServerWebExchange exchange) {
|
private Map<String, Object> createAttributes(ServerWebExchange exchange) {
|
||||||
|
@ -218,12 +218,9 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
public ServletOAuth2AuthorizedClientExchangeFilterFunction(
|
public ServletOAuth2AuthorizedClientExchangeFilterFunction(
|
||||||
ClientRegistrationRepository clientRegistrationRepository,
|
ClientRegistrationRepository clientRegistrationRepository,
|
||||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||||
|
|
||||||
OAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
|
OAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
|
||||||
(clientRegistrationId, principal, attributes) -> authorizedClientRepository.removeAuthorizedClient(
|
(clientRegistrationId, principal, attributes) -> removeAuthorizedClient(authorizedClientRepository,
|
||||||
clientRegistrationId, principal,
|
clientRegistrationId, principal, attributes));
|
||||||
(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),
|
|
||||||
(HttpServletResponse) attributes.get(HttpServletResponse.class.getName())));
|
|
||||||
DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
||||||
clientRegistrationRepository, authorizedClientRepository);
|
clientRegistrationRepository, authorizedClientRepository);
|
||||||
defaultAuthorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);
|
defaultAuthorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);
|
||||||
@ -232,6 +229,13 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
this.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);
|
this.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeAuthorizedClient(OAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||||
|
String clientRegistrationId, Authentication principal, Map<String, Object> attributes) {
|
||||||
|
HttpServletRequest request = getRequest(attributes);
|
||||||
|
HttpServletResponse response = getResponse(attributes);
|
||||||
|
authorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link OAuth2AccessTokenResponseClient} used for getting an
|
* Sets the {@link OAuth2AccessTokenResponseClient} used for getting an
|
||||||
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
|
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
|
||||||
@ -453,9 +457,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
|| !request.attribute(AUTHENTICATION_ATTR_NAME).isPresent()) {
|
|| !request.attribute(AUTHENTICATION_ATTR_NAME).isPresent()) {
|
||||||
return mergeRequestAttributesFromContext(request);
|
return mergeRequestAttributesFromContext(request);
|
||||||
}
|
}
|
||||||
else {
|
return Mono.just(request);
|
||||||
return Mono.just(request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<ClientRequest> mergeRequestAttributesFromContext(ClientRequest request) {
|
private Mono<ClientRequest> mergeRequestAttributesFromContext(ClientRequest request) {
|
||||||
@ -530,23 +532,13 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
}
|
}
|
||||||
HttpServletRequest servletRequest = getRequest(attrs);
|
HttpServletRequest servletRequest = getRequest(attrs);
|
||||||
HttpServletResponse servletResponse = getResponse(attrs);
|
HttpServletResponse servletResponse = getResponse(attrs);
|
||||||
|
|
||||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
||||||
.principal(authentication);
|
.principal(authentication);
|
||||||
builder.attributes((attributes) -> {
|
builder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));
|
||||||
if (servletRequest != null) {
|
|
||||||
attributes.put(HttpServletRequest.class.getName(), servletRequest);
|
|
||||||
}
|
|
||||||
if (servletResponse != null) {
|
|
||||||
attributes.put(HttpServletResponse.class.getName(), servletResponse);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
OAuth2AuthorizeRequest authorizeRequest = builder.build();
|
OAuth2AuthorizeRequest authorizeRequest = builder.build();
|
||||||
|
// NOTE: 'authorizedClientManager.authorize()' needs to be executed on a dedicated
|
||||||
// NOTE:
|
// thread via subscribeOn(Schedulers.boundedElastic()) since it performs a
|
||||||
// 'authorizedClientManager.authorize()' needs to be executed
|
// blocking I/O operation using RestTemplate internally
|
||||||
// on a dedicated thread via subscribeOn(Schedulers.boundedElastic())
|
|
||||||
// since it performs a blocking I/O operation using RestTemplate internally
|
|
||||||
return Mono.fromSupplier(() -> this.authorizedClientManager.authorize(authorizeRequest))
|
return Mono.fromSupplier(() -> this.authorizedClientManager.authorize(authorizeRequest))
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
}
|
||||||
@ -563,27 +555,27 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
}
|
}
|
||||||
HttpServletRequest servletRequest = getRequest(attrs);
|
HttpServletRequest servletRequest = getRequest(attrs);
|
||||||
HttpServletResponse servletResponse = getResponse(attrs);
|
HttpServletResponse servletResponse = getResponse(attrs);
|
||||||
|
|
||||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)
|
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)
|
||||||
.principal(authentication);
|
.principal(authentication);
|
||||||
builder.attributes((attributes) -> {
|
builder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));
|
||||||
if (servletRequest != null) {
|
|
||||||
attributes.put(HttpServletRequest.class.getName(), servletRequest);
|
|
||||||
}
|
|
||||||
if (servletResponse != null) {
|
|
||||||
attributes.put(HttpServletResponse.class.getName(), servletResponse);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
OAuth2AuthorizeRequest reauthorizeRequest = builder.build();
|
OAuth2AuthorizeRequest reauthorizeRequest = builder.build();
|
||||||
|
// NOTE: 'authorizedClientManager.authorize()' needs to be executed on a dedicated
|
||||||
// NOTE:
|
// thread via subscribeOn(Schedulers.boundedElastic()) since it performs a
|
||||||
// 'authorizedClientManager.authorize()' needs to be executed
|
// blocking I/O operation using RestTemplate internally
|
||||||
// on a dedicated thread via subscribeOn(Schedulers.boundedElastic())
|
|
||||||
// since it performs a blocking I/O operation using RestTemplate internally
|
|
||||||
return Mono.fromSupplier(() -> this.authorizedClientManager.authorize(reauthorizeRequest))
|
return Mono.fromSupplier(() -> this.authorizedClientManager.authorize(reauthorizeRequest))
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addToAttributes(Map<String, Object> attributes, HttpServletRequest servletRequest,
|
||||||
|
HttpServletResponse servletResponse) {
|
||||||
|
if (servletRequest != null) {
|
||||||
|
attributes.put(HTTP_SERVLET_REQUEST_ATTR_NAME, servletRequest);
|
||||||
|
}
|
||||||
|
if (servletResponse != null) {
|
||||||
|
attributes.put(HTTP_SERVLET_RESPONSE_ATTR_NAME, servletResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ClientRequest bearer(ClientRequest request, OAuth2AuthorizedClient authorizedClient) {
|
private ClientRequest bearer(ClientRequest request, OAuth2AuthorizedClient authorizedClient) {
|
||||||
return ClientRequest.from(request)
|
return ClientRequest.from(request)
|
||||||
.headers((headers) -> headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue()))
|
.headers((headers) -> headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue()))
|
||||||
@ -612,8 +604,8 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
|
|
||||||
private static Authentication createAuthentication(final String principalName) {
|
private static Authentication createAuthentication(final String principalName) {
|
||||||
Assert.hasText(principalName, "principalName cannot be empty");
|
Assert.hasText(principalName, "principalName cannot be empty");
|
||||||
|
|
||||||
return new AbstractAuthenticationToken(null) {
|
return new AbstractAuthenticationToken(null) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public Object getCredentials() {
|
||||||
return "";
|
return "";
|
||||||
@ -656,7 +648,6 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
private AuthorizationFailureForwarder(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
private AuthorizationFailureForwarder(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {
|
||||||
Assert.notNull(authorizationFailureHandler, "authorizationFailureHandler cannot be null");
|
Assert.notNull(authorizationFailureHandler, "authorizationFailureHandler cannot be null");
|
||||||
this.authorizationFailureHandler = authorizationFailureHandler;
|
this.authorizationFailureHandler = authorizationFailureHandler;
|
||||||
|
|
||||||
Map<Integer, String> httpStatusToOAuth2Error = new HashMap<>();
|
Map<Integer, String> httpStatusToOAuth2Error = new HashMap<>();
|
||||||
httpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED.value(), OAuth2ErrorCodes.INVALID_TOKEN);
|
httpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED.value(), OAuth2ErrorCodes.INVALID_TOKEN);
|
||||||
httpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN.value(), OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
httpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN.value(), OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
||||||
@ -679,14 +670,11 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
if (authorizedClient == null) {
|
if (authorizedClient == null) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,
|
ClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,
|
||||||
authorizedClient.getClientRegistration().getRegistrationId());
|
authorizedClient.getClientRegistration().getRegistrationId());
|
||||||
|
|
||||||
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
||||||
HttpServletRequest servletRequest = getRequest(attrs);
|
HttpServletRequest servletRequest = getRequest(attrs);
|
||||||
HttpServletResponse servletResponse = getResponse(attrs);
|
HttpServletResponse servletResponse = getResponse(attrs);
|
||||||
|
|
||||||
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -740,14 +728,11 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
if (authorizedClient == null) {
|
if (authorizedClient == null) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,
|
ClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,
|
||||||
authorizedClient.getClientRegistration().getRegistrationId(), exception);
|
authorizedClient.getClientRegistration().getRegistrationId(), exception);
|
||||||
|
|
||||||
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
||||||
HttpServletRequest servletRequest = getRequest(attrs);
|
HttpServletRequest servletRequest = getRequest(attrs);
|
||||||
HttpServletResponse servletResponse = getResponse(attrs);
|
HttpServletResponse servletResponse = getResponse(attrs);
|
||||||
|
|
||||||
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -769,11 +754,9 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
|||||||
if (authorizedClient == null) {
|
if (authorizedClient == null) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
Authentication principal = createAuthentication(authorizedClient.getPrincipalName());
|
||||||
HttpServletRequest servletRequest = getRequest(attrs);
|
HttpServletRequest servletRequest = getRequest(attrs);
|
||||||
HttpServletResponse servletResponse = getResponse(attrs);
|
HttpServletResponse servletResponse = getResponse(attrs);
|
||||||
|
|
||||||
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
return handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -105,28 +105,24 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
|
|||||||
return Mono.defer(() -> {
|
return Mono.defer(() -> {
|
||||||
RegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils
|
RegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils
|
||||||
.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);
|
.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);
|
||||||
|
|
||||||
String clientRegistrationId = StringUtils.hasLength(authorizedClientAnnotation.registrationId())
|
String clientRegistrationId = StringUtils.hasLength(authorizedClientAnnotation.registrationId())
|
||||||
? authorizedClientAnnotation.registrationId() : null;
|
? authorizedClientAnnotation.registrationId() : null;
|
||||||
|
|
||||||
return authorizeRequest(clientRegistrationId, exchange).flatMap(this.authorizedClientManager::authorize);
|
return authorizeRequest(clientRegistrationId, exchange).flatMap(this.authorizedClientManager::authorize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<OAuth2AuthorizeRequest> authorizeRequest(String registrationId, ServerWebExchange exchange) {
|
private Mono<OAuth2AuthorizeRequest> authorizeRequest(String registrationId, ServerWebExchange exchange) {
|
||||||
Mono<Authentication> defaultedAuthentication = currentAuthentication();
|
Mono<Authentication> defaultedAuthentication = currentAuthentication();
|
||||||
|
|
||||||
Mono<String> defaultedRegistrationId = Mono.justOrEmpty(registrationId)
|
Mono<String> defaultedRegistrationId = Mono.justOrEmpty(registrationId)
|
||||||
.switchIfEmpty(clientRegistrationId(defaultedAuthentication))
|
.switchIfEmpty(clientRegistrationId(defaultedAuthentication))
|
||||||
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(
|
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(
|
||||||
"The clientRegistrationId could not be resolved. Please provide one")));
|
"The clientRegistrationId could not be resolved. Please provide one")));
|
||||||
|
|
||||||
Mono<ServerWebExchange> defaultedExchange = Mono.justOrEmpty(exchange)
|
Mono<ServerWebExchange> defaultedExchange = Mono.justOrEmpty(exchange)
|
||||||
.switchIfEmpty(currentServerWebExchange());
|
.switchIfEmpty(currentServerWebExchange());
|
||||||
|
|
||||||
return Mono.zip(defaultedRegistrationId, defaultedAuthentication, defaultedExchange)
|
return Mono.zip(defaultedRegistrationId, defaultedAuthentication, defaultedExchange)
|
||||||
.map((t3) -> OAuth2AuthorizeRequest.withClientRegistrationId(t3.getT1()).principal(t3.getT2())
|
.map((zipped) -> OAuth2AuthorizeRequest.withClientRegistrationId(zipped.getT1())
|
||||||
.attribute(ServerWebExchange.class.getName(), t3.getT3()).build());
|
.principal(zipped.getT2()).attribute(ServerWebExchange.class.getName(), zipped.getT3())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Authentication> currentAuthentication() {
|
private Mono<Authentication> currentAuthentication() {
|
||||||
|
@ -83,10 +83,7 @@ public final class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository
|
|||||||
if (this.isPrincipalAuthenticated(principal)) {
|
if (this.isPrincipalAuthenticated(principal)) {
|
||||||
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());
|
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());
|
||||||
}
|
}
|
||||||
else {
|
return this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, exchange);
|
||||||
return this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal,
|
|
||||||
exchange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -95,9 +92,7 @@ public final class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository
|
|||||||
if (this.isPrincipalAuthenticated(principal)) {
|
if (this.isPrincipalAuthenticated(principal)) {
|
||||||
return this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
return this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
||||||
}
|
}
|
||||||
else {
|
return this.anonymousAuthorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, exchange);
|
||||||
return this.anonymousAuthorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, exchange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,10 +101,8 @@ public final class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository
|
|||||||
if (this.isPrincipalAuthenticated(principal)) {
|
if (this.isPrincipalAuthenticated(principal)) {
|
||||||
return this.authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());
|
return this.authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());
|
||||||
}
|
}
|
||||||
else {
|
return this.anonymousAuthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,
|
||||||
return this.anonymousAuthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,
|
exchange);
|
||||||
exchange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPrincipalAuthenticated(Authentication authentication) {
|
private boolean isPrincipalAuthenticated(Authentication authentication) {
|
||||||
|
@ -153,13 +153,23 @@ public class DefaultServerOAuth2AuthorizationRequestResolver implements ServerOA
|
|||||||
private OAuth2AuthorizationRequest authorizationRequest(ServerWebExchange exchange,
|
private OAuth2AuthorizationRequest authorizationRequest(ServerWebExchange exchange,
|
||||||
ClientRegistration clientRegistration) {
|
ClientRegistration clientRegistration) {
|
||||||
String redirectUriStr = expandRedirectUri(exchange.getRequest(), clientRegistration);
|
String redirectUriStr = expandRedirectUri(exchange.getRequest(), clientRegistration);
|
||||||
|
|
||||||
Map<String, Object> attributes = new HashMap<>();
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||||
|
OAuth2AuthorizationRequest.Builder builder = getBuilder(clientRegistration, attributes);
|
||||||
|
builder.clientId(clientRegistration.getClientId())
|
||||||
|
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||||
|
.redirectUri(redirectUriStr).scopes(clientRegistration.getScopes())
|
||||||
|
.state(this.stateGenerator.generateKey()).attributes(attributes);
|
||||||
|
|
||||||
OAuth2AuthorizationRequest.Builder builder;
|
this.authorizationRequestCustomizer.accept(builder);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizationRequest.Builder getBuilder(ClientRegistration clientRegistration,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
builder = OAuth2AuthorizationRequest.authorizationCode();
|
OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode();
|
||||||
Map<String, Object> additionalParameters = new HashMap<>();
|
Map<String, Object> additionalParameters = new HashMap<>();
|
||||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())
|
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())
|
||||||
&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
|
&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
|
||||||
@ -174,23 +184,14 @@ public class DefaultServerOAuth2AuthorizationRequestResolver implements ServerOA
|
|||||||
addPkceParameters(attributes, additionalParameters);
|
addPkceParameters(attributes, additionalParameters);
|
||||||
}
|
}
|
||||||
builder.additionalParameters(additionalParameters);
|
builder.additionalParameters(additionalParameters);
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
builder = OAuth2AuthorizationRequest.implicit();
|
return OAuth2AuthorizationRequest.implicit();
|
||||||
}
|
}
|
||||||
else {
|
throw new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException(
|
"Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
|
||||||
"Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
|
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
|
||||||
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
|
|
||||||
}
|
|
||||||
builder.clientId(clientRegistration.getClientId())
|
|
||||||
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
|
|
||||||
.redirectUri(redirectUriStr).scopes(clientRegistration.getScopes())
|
|
||||||
.state(this.stateGenerator.generateKey()).attributes(attributes);
|
|
||||||
|
|
||||||
this.authorizationRequestCustomizer.accept(builder);
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,7 +214,6 @@ public class DefaultServerOAuth2AuthorizationRequestResolver implements ServerOA
|
|||||||
private static String expandRedirectUri(ServerHttpRequest request, ClientRegistration clientRegistration) {
|
private static String expandRedirectUri(ServerHttpRequest request, ClientRegistration clientRegistration) {
|
||||||
Map<String, String> uriVariables = new HashMap<>();
|
Map<String, String> uriVariables = new HashMap<>();
|
||||||
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
|
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
|
||||||
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI())
|
UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI())
|
||||||
.replacePath(request.getPath().contextPath().value()).replaceQuery(null).fragment(null).build();
|
.replacePath(request.getPath().contextPath().value()).replaceQuery(null).fragment(null).build();
|
||||||
String scheme = uriComponents.getScheme();
|
String scheme = uriComponents.getScheme();
|
||||||
@ -231,13 +231,11 @@ public class DefaultServerOAuth2AuthorizationRequestResolver implements ServerOA
|
|||||||
}
|
}
|
||||||
uriVariables.put("basePath", (path != null) ? path : "");
|
uriVariables.put("basePath", (path != null) ? path : "");
|
||||||
uriVariables.put("baseUrl", uriComponents.toUriString());
|
uriVariables.put("baseUrl", uriComponents.toUriString());
|
||||||
|
|
||||||
String action = "";
|
String action = "";
|
||||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
||||||
action = "login";
|
action = "login";
|
||||||
}
|
}
|
||||||
uriVariables.put("action", action);
|
uriVariables.put("action", action);
|
||||||
|
|
||||||
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri()).buildAndExpand(uriVariables)
|
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri()).buildAndExpand(uriVariables)
|
||||||
.toUriString();
|
.toUriString();
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ public class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter {
|
|||||||
.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
|
.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
|
||||||
.flatMap((matchResult) -> this.authenticationConverter.convert(exchange).onErrorMap(
|
.flatMap((matchResult) -> this.authenticationConverter.convert(exchange).onErrorMap(
|
||||||
OAuth2AuthorizationException.class,
|
OAuth2AuthorizationException.class,
|
||||||
(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString())))
|
(ex) -> new OAuth2AuthenticationException(ex.getError(), ex.getError().toString())))
|
||||||
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
||||||
.flatMap((token) -> authenticate(exchange, chain, token))
|
.flatMap((token) -> authenticate(exchange, chain, token))
|
||||||
.onErrorResume(AuthenticationException.class, (e) -> this.authenticationFailureHandler
|
.onErrorResume(AuthenticationException.class, (e) -> this.authenticationFailureHandler
|
||||||
@ -217,7 +217,7 @@ public class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter {
|
|||||||
WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);
|
WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);
|
||||||
return this.authenticationManager.authenticate(token)
|
return this.authenticationManager.authenticate(token)
|
||||||
.onErrorMap(OAuth2AuthorizationException.class,
|
.onErrorMap(OAuth2AuthorizationException.class,
|
||||||
(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString()))
|
(ex) -> new OAuth2AuthenticationException(ex.getError(), ex.getError().toString()))
|
||||||
.switchIfEmpty(Mono.defer(
|
.switchIfEmpty(Mono.defer(
|
||||||
() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass()))))
|
() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass()))))
|
||||||
.flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))
|
.flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))
|
||||||
@ -258,7 +258,6 @@ public class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter {
|
|||||||
// before doing an exact comparison with the authorizationRequest.getRedirectUri()
|
// before doing an exact comparison with the authorizationRequest.getRedirectUri()
|
||||||
// parameters (if any)
|
// parameters (if any)
|
||||||
requestUriParameters.retainAll(redirectUriParameters);
|
requestUriParameters.retainAll(redirectUriParameters);
|
||||||
|
|
||||||
if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())
|
if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())
|
||||||
&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())
|
&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())
|
||||||
&& Objects.equals(requestUri.getHost(), redirectUri.getHost())
|
&& Objects.equals(requestUri.getHost(), redirectUri.getHost())
|
||||||
|
@ -130,8 +130,8 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|||||||
return this.authorizationRequestResolver.resolve(exchange)
|
return this.authorizationRequestResolver.resolve(exchange)
|
||||||
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
||||||
.onErrorResume(ClientAuthorizationRequiredException.class,
|
.onErrorResume(ClientAuthorizationRequiredException.class,
|
||||||
(e) -> this.requestCache.saveRequest(exchange)
|
(ex) -> this.requestCache.saveRequest(exchange).then(
|
||||||
.then(this.authorizationRequestResolver.resolve(exchange, e.getClientRegistrationId())))
|
this.authorizationRequestResolver.resolve(exchange, ex.getClientRegistrationId())))
|
||||||
.flatMap((clientRegistration) -> sendRedirectForAuthorization(exchange, clientRegistration));
|
.flatMap((clientRegistration) -> sendRedirectForAuthorization(exchange, clientRegistration));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +143,6 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|||||||
saveAuthorizationRequest = this.authorizationRequestRepository
|
saveAuthorizationRequest = this.authorizationRequestRepository
|
||||||
.saveAuthorizationRequest(authorizationRequest, exchange);
|
.saveAuthorizationRequest(authorizationRequest, exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
URI redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getAuthorizationRequestUri())
|
URI redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getAuthorizationRequestUri())
|
||||||
.build(true).toUri();
|
.build(true).toUri();
|
||||||
return saveAuthorizationRequest
|
return saveAuthorizationRequest
|
||||||
|
@ -66,16 +66,13 @@ final class OAuth2AuthorizationResponseUtils {
|
|||||||
String code = request.getFirst(OAuth2ParameterNames.CODE);
|
String code = request.getFirst(OAuth2ParameterNames.CODE);
|
||||||
String errorCode = request.getFirst(OAuth2ParameterNames.ERROR);
|
String errorCode = request.getFirst(OAuth2ParameterNames.ERROR);
|
||||||
String state = request.getFirst(OAuth2ParameterNames.STATE);
|
String state = request.getFirst(OAuth2ParameterNames.STATE);
|
||||||
|
|
||||||
if (StringUtils.hasText(code)) {
|
if (StringUtils.hasText(code)) {
|
||||||
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
|
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
|
||||||
}
|
}
|
||||||
else {
|
String errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);
|
||||||
String errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);
|
String errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);
|
||||||
String errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);
|
return OAuth2AuthorizationResponse.error(errorCode).redirectUri(redirectUri).errorDescription(errorDescription)
|
||||||
return OAuth2AuthorizationResponse.error(errorCode).redirectUri(redirectUri)
|
.errorUri(errorUri).state(state).build();
|
||||||
.errorDescription(errorDescription).errorUri(errorUri).state(state).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ public class UnAuthenticatedServerOAuth2AuthorizedClientRepository implements Se
|
|||||||
Assert.notNull(clientRegistrationId, "clientRegistrationId cannot be null");
|
Assert.notNull(clientRegistrationId, "clientRegistrationId cannot be null");
|
||||||
Assert.isNull(serverWebExchange, "serverWebExchange must be null");
|
Assert.isNull(serverWebExchange, "serverWebExchange must be null");
|
||||||
Assert.isTrue(isUnauthenticated(authentication), "The user " + authentication + " should not be authenticated");
|
Assert.isTrue(isUnauthenticated(authentication), "The user " + authentication + " should not be authenticated");
|
||||||
|
|
||||||
return Mono.fromSupplier(() -> (T) this.clientRegistrationIdToAuthorizedClient.get(clientRegistrationId));
|
return Mono.fromSupplier(() -> (T) this.clientRegistrationIdToAuthorizedClient.get(clientRegistrationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,14 +121,11 @@ public final class WebSessionOAuth2ServerAuthorizationRequestRepository
|
|||||||
|
|
||||||
private Mono<Map<String, OAuth2AuthorizationRequest>> saveStateToAuthorizationRequest(ServerWebExchange exchange) {
|
private Mono<Map<String, OAuth2AuthorizationRequest>> saveStateToAuthorizationRequest(ServerWebExchange exchange) {
|
||||||
Assert.notNull(exchange, "exchange cannot be null");
|
Assert.notNull(exchange, "exchange cannot be null");
|
||||||
|
|
||||||
return getSessionAttributes(exchange).doOnNext((sessionAttrs) -> {
|
return getSessionAttributes(exchange).doOnNext((sessionAttrs) -> {
|
||||||
Object stateToAuthzRequest = sessionAttrs.get(this.sessionAttributeName);
|
Object stateToAuthzRequest = sessionAttrs.get(this.sessionAttributeName);
|
||||||
|
|
||||||
if (stateToAuthzRequest == null) {
|
if (stateToAuthzRequest == null) {
|
||||||
stateToAuthzRequest = new HashMap<String, OAuth2AuthorizationRequest>();
|
stateToAuthzRequest = new HashMap<String, OAuth2AuthorizationRequest>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// No matter stateToAuthzRequest was in session or not, we should always put
|
// No matter stateToAuthzRequest was in session or not, we should always put
|
||||||
// it into session again
|
// it into session again
|
||||||
// in case of redis or hazelcast session. #6215
|
// in case of redis or hazelcast session. #6215
|
||||||
|
Loading…
x
Reference in New Issue
Block a user