Apply Spring formatting to 1.3.x

Issue gh-1616
This commit is contained in:
Joe Grandja
2024-05-16 12:27:18 -04:00
parent 041b94d682
commit 320176a67b
84 changed files with 1446 additions and 1226 deletions

View File

@@ -38,10 +38,16 @@ import org.springframework.util.Assert;
* @author Joe Grandja
* @see OAuth2AuthorizationServerMetadataClaimAccessor
* @since 0.1.1
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2. Authorization Server Metadata Response</a>
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2. OpenID Provider Configuration Response</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2.
* Authorization Server Metadata Response</a>
* @see <a target="_blank" href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2.
* OpenID Provider Configuration Response</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
* Device Authorization Grant Metadata</a>
* @see <a target="_blank" href=
* "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
* Certificate-Bound Access Tokens Metadata</a>
*/
public abstract class AbstractOAuth2AuthorizationServerMetadata
implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
@@ -358,14 +364,16 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata
}
/**
* Use this {@code tls_client_certificate_bound_access_tokens} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
*
* @param tlsClientCertificateBoundAccessTokens {@code true} to indicate support for mutual-TLS client certificate-bound access tokens
* Use this {@code tls_client_certificate_bound_access_tokens} in the resulting
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
* @param tlsClientCertificateBoundAccessTokens {@code true} to indicate support
* for mutual-TLS client certificate-bound access tokens
* @return the {@link AbstractBuilder} for further configuration
* @since 1.3
*/
public B tlsClientCertificateBoundAccessTokens(boolean tlsClientCertificateBoundAccessTokens) {
return claim(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS, tlsClientCertificateBoundAccessTokens);
return claim(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS,
tlsClientCertificateBoundAccessTokens);
}
/**

View File

@@ -30,10 +30,16 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
* @since 0.1.1
* @see ClaimAccessor
* @see OAuth2AuthorizationServerMetadataClaimNames
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2.
* Authorization Server Metadata</a>
* @see <a target="_blank" href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
* Provider Metadata</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
* Device Authorization Grant Metadata</a>
* @see <a target="_blank" href=
* "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
* Certificate-Bound Access Tokens Metadata</a>
*/
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
@@ -176,13 +182,15 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
}
/**
* Returns {@code true} to indicate support for mutual-TLS client certificate-bound access tokens {@code (tls_client_certificate_bound_access_tokens)}.
*
* @return {@code true} to indicate support for mutual-TLS client certificate-bound access tokens, {@code false} otherwise
* Returns {@code true} to indicate support for mutual-TLS client certificate-bound
* access tokens {@code (tls_client_certificate_bound_access_tokens)}.
* @return {@code true} to indicate support for mutual-TLS client certificate-bound
* access tokens, {@code false} otherwise
* @since 1.3
*/
default boolean isTlsClientCertificateBoundAccessTokens() {
return Boolean.TRUE.equals(getClaimAsBoolean(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));
return Boolean.TRUE.equals(getClaimAsBoolean(
OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));
}
}

View File

@@ -22,10 +22,16 @@ package org.springframework.security.oauth2.server.authorization;
* @author Daniel Garnier-Moiroux
* @author Joe Grandja
* @since 0.1.1
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client Certificate-Bound Access Tokens Metadata</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2.
* Authorization Server Metadata</a>
* @see <a target="_blank" href=
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
* Provider Metadata</a>
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
* Device Authorization Grant Metadata</a>
* @see <a target="_blank" href=
* "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
* Certificate-Bound Access Tokens Metadata</a>
*/
public class OAuth2AuthorizationServerMetadataClaimNames {
@@ -118,7 +124,8 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
public static final String CODE_CHALLENGE_METHODS_SUPPORTED = "code_challenge_methods_supported";
/**
* {@code tls_client_certificate_bound_access_tokens} - {@code true} to indicate support for mutual-TLS client certificate-bound access tokens
* {@code tls_client_certificate_bound_access_tokens} - {@code true} to indicate
* support for mutual-TLS client certificate-bound access tokens
* @since 1.3
*/
public static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS = "tls_client_certificate_bound_access_tokens";

View File

@@ -150,14 +150,14 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
loadClass("org.springframework.security.jackson2.UserMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.jackson2.SimpleGrantedAuthorityMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2ActorAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2CompositeAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2ActorAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2CompositeAuthenticationTokenMixin"));
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
// Check if Spring Security OAuth2 Client is on classpath
if (ClassUtils.isPresent("org.springframework.security.oauth2.client.registration.ClientRegistration",

View File

@@ -26,8 +26,9 @@ import org.springframework.security.oauth2.server.authorization.web.authenticati
import org.springframework.util.Assert;
/**
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AccessTokenAuthenticationToken} and additional information
* and is used when customizing the {@link OAuth2AccessTokenResponse}.
* An {@link OAuth2AuthenticationContext} that holds an
* {@link OAuth2AccessTokenAuthenticationToken} and additional information and is used
* when customizing the {@link OAuth2AccessTokenResponse}.
*
* @author Dmitriy Dubson
* @since 1.3
@@ -37,6 +38,7 @@ import org.springframework.util.Assert;
* @see OAuth2AccessTokenResponseAuthenticationSuccessHandler#setAccessTokenResponseCustomizer(Consumer)
*/
public final class OAuth2AccessTokenAuthenticationContext implements OAuth2AuthenticationContext {
private final Map<Object, Object> context;
private OAuth2AccessTokenAuthenticationContext(Map<Object, Object> context) {
@@ -57,8 +59,8 @@ public final class OAuth2AccessTokenAuthenticationContext implements OAuth2Authe
}
/**
* Returns the {@link OAuth2AccessTokenResponse.Builder access token response builder}.
*
* Returns the {@link OAuth2AccessTokenResponse.Builder access token response
* builder}.
* @return the {@link OAuth2AccessTokenResponse.Builder}
*/
public OAuth2AccessTokenResponse.Builder getAccessTokenResponse() {
@@ -66,8 +68,8 @@ public final class OAuth2AccessTokenAuthenticationContext implements OAuth2Authe
}
/**
* Constructs a new {@link Builder} with the provided {@link OAuth2AccessTokenAuthenticationToken}.
*
* Constructs a new {@link Builder} with the provided
* {@link OAuth2AccessTokenAuthenticationToken}.
* @param authentication the {@link OAuth2AccessTokenAuthenticationToken}
* @return the {@link Builder}
*/
@@ -85,8 +87,8 @@ public final class OAuth2AccessTokenAuthenticationContext implements OAuth2Authe
}
/**
* Sets the {@link OAuth2AccessTokenResponse.Builder access token response builder}.
*
* Sets the {@link OAuth2AccessTokenResponse.Builder access token response
* builder}.
* @param accessTokenResponse the {@link OAuth2AccessTokenResponse.Builder}
* @return the {@link Builder} for further configuration
*/
@@ -96,7 +98,6 @@ public final class OAuth2AccessTokenAuthenticationContext implements OAuth2Authe
/**
* Builds a new {@link OAuth2AccessTokenAuthenticationContext}.
*
* @return the {@link OAuth2AccessTokenAuthenticationContext}
*/
public OAuth2AccessTokenAuthenticationContext build() {

View File

@@ -81,11 +81,11 @@ final class OAuth2AuthenticationProviderUtils {
static <T extends OAuth2Token> OAuth2AccessToken accessToken(OAuth2Authorization.Builder builder, T token,
OAuth2TokenContext accessTokenContext) {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
token.getTokenValue(), token.getIssuedAt(), token.getExpiresAt(),
accessTokenContext.getAuthorizedScopes());
OAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient().getTokenSettings()
.getAccessTokenFormat();
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token.getTokenValue(),
token.getIssuedAt(), token.getExpiresAt(), accessTokenContext.getAuthorizedScopes());
OAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient()
.getTokenSettings()
.getAccessTokenFormat();
builder.token(accessToken, (metadata) -> {
if (token instanceof ClaimAccessor claimAccessor) {
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claimAccessor.getClaims());

View File

@@ -157,11 +157,12 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
if (StringUtils.hasText(authorizationRequest.getRedirectUri()) &&
!authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) {
if (StringUtils.hasText(authorizationRequest.getRedirectUri())
&& !authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: redirect_uri does not match" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: redirect_uri does not match" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}

View File

@@ -28,8 +28,10 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.util.Assert;
/**
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information
* and is used when validating the OAuth 2.0 Authorization Request parameters, as well as, determining if authorization consent is required.
* An {@link OAuth2AuthenticationContext} that holds an
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information
* and is used when validating the OAuth 2.0 Authorization Request parameters, as well as,
* determining if authorization consent is required.
*
* @author Joe Grandja
* @since 0.4.0
@@ -69,7 +71,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
/**
* Returns the {@link OAuth2AuthorizationRequest authorization request}.
*
* @return the {@link OAuth2AuthorizationRequest}
* @since 1.3
*/
@@ -80,7 +81,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
/**
* Returns the {@link OAuth2AuthorizationConsent authorization consent}.
*
* @return the {@link OAuth2AuthorizationConsent}
* @since 1.3
*/
@@ -89,10 +89,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
return get(OAuth2AuthorizationConsent.class);
}
/**
* Constructs a new {@link Builder} with the provided {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
*
* Constructs a new {@link Builder} with the provided
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
* @param authentication the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
* @return the {@link Builder}
*/
@@ -121,7 +120,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
/**
* Sets the {@link OAuth2AuthorizationRequest authorization request}.
*
* @param authorizationRequest the {@link OAuth2AuthorizationRequest}
* @return the {@link Builder} for further configuration
* @since 1.3
@@ -132,7 +130,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
/**
* Sets the {@link OAuth2AuthorizationConsent authorization consent}.
*
* @param authorizationConsent the {@link OAuth2AuthorizationConsent}
* @return the {@link Builder} for further configuration
* @since 1.3

View File

@@ -90,10 +90,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
private OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator();
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =
new OAuth2AuthorizationCodeRequestAuthenticationValidator();
private Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired =
OAuth2AuthorizationCodeRequestAuthenticationProvider::isAuthorizationConsentRequired;
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = new OAuth2AuthorizationCodeRequestAuthenticationValidator();
private Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired = OAuth2AuthorizationCodeRequestAuthenticationProvider::isAuthorizationConsentRequired;
/**
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using
@@ -128,15 +127,16 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
this.logger.trace("Retrieved registered client");
}
OAuth2AuthorizationCodeRequestAuthenticationContext.Builder authenticationContextBuilder =
OAuth2AuthorizationCodeRequestAuthenticationContext.with(authorizationCodeRequestAuthentication)
.registeredClient(registeredClient);
OAuth2AuthorizationCodeRequestAuthenticationContext.Builder authenticationContextBuilder = OAuth2AuthorizationCodeRequestAuthenticationContext
.with(authorizationCodeRequestAuthentication)
.registeredClient(registeredClient);
this.authenticationValidator.accept(authenticationContextBuilder.build());
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: requested grant_type is not allowed" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: requested grant_type is not allowed" + " for registered client '%s'",
registeredClient.getId()));
}
throwError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID,
authorizationCodeRequestAuthentication, registeredClient);
@@ -176,17 +176,17 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
}
OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())
.clientId(registeredClient.getClientId())
.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())
.scopes(authorizationCodeRequestAuthentication.getScopes())
.state(authorizationCodeRequestAuthentication.getState())
.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())
.build();
.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())
.clientId(registeredClient.getClientId())
.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())
.scopes(authorizationCodeRequestAuthentication.getScopes())
.state(authorizationCodeRequestAuthentication.getState())
.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())
.build();
authenticationContextBuilder.authorizationRequest(authorizationRequest);
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
registeredClient.getId(), principal.getName());
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
.findById(registeredClient.getId(), principal.getName());
if (currentAuthorizationConsent != null) {
authenticationContextBuilder.authorizationConsent(currentAuthorizationConsent);
}
@@ -295,42 +295,48 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
* Sets the {@code Predicate} used to determine if authorization consent is required.
*
* <p>
* The {@link OAuth2AuthorizationCodeRequestAuthenticationContext} gives the predicate access to the {@link OAuth2AuthorizationCodeRequestAuthenticationToken},
* as well as, the following context attributes:
* The {@link OAuth2AuthorizationCodeRequestAuthenticationContext} gives the predicate
* access to the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}, as well
* as, the following context attributes:
* <ul>
* <li>The {@link RegisteredClient} associated with the authorization request.</li>
* <li>The {@link OAuth2AuthorizationRequest} containing the authorization request parameters.</li>
* <li>The {@link OAuth2AuthorizationConsent} previously granted to the {@link RegisteredClient}, or {@code null} if not available.</li>
* <li>The {@link OAuth2AuthorizationRequest} containing the authorization request
* parameters.</li>
* <li>The {@link OAuth2AuthorizationConsent} previously granted to the
* {@link RegisteredClient}, or {@code null} if not available.</li>
* </ul>
*
* @param authorizationConsentRequired the {@code Predicate} used to determine if authorization consent is required
* @param authorizationConsentRequired the {@code Predicate} used to determine if
* authorization consent is required
* @since 1.3
*/
public void setAuthorizationConsentRequired(Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired) {
public void setAuthorizationConsentRequired(
Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired) {
Assert.notNull(authorizationConsentRequired, "authorizationConsentRequired cannot be null");
this.authorizationConsentRequired = authorizationConsentRequired;
}
private static boolean isAuthorizationConsentRequired(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
private static boolean isAuthorizationConsentRequired(
OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
if (!authenticationContext.getRegisteredClient().getClientSettings().isRequireAuthorizationConsent()) {
return false;
}
// 'openid' scope does not require consent
if (authenticationContext.getAuthorizationRequest().getScopes().contains(OidcScopes.OPENID) &&
authenticationContext.getAuthorizationRequest().getScopes().size() == 1) {
if (authenticationContext.getAuthorizationRequest().getScopes().contains(OidcScopes.OPENID)
&& authenticationContext.getAuthorizationRequest().getScopes().size() == 1) {
return false;
}
if (authenticationContext.getAuthorizationConsent() != null &&
authenticationContext.getAuthorizationConsent().getScopes().containsAll(authenticationContext.getAuthorizationRequest().getScopes())) {
if (authenticationContext.getAuthorizationConsent() != null && authenticationContext.getAuthorizationConsent()
.getScopes()
.containsAll(authenticationContext.getAuthorizationRequest().getScopes())) {
return false;
}
return true;
}
private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient, Authentication principal,
OAuth2AuthorizationRequest authorizationRequest) {
private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient,
Authentication principal, OAuth2AuthorizationRequest authorizationRequest) {
return OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(principal.getName())
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)

View File

@@ -25,8 +25,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.util.Assert;
/**
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2ClientAuthenticationToken} and additional information
* and is used when validating an OAuth 2.0 Client Authentication.
* An {@link OAuth2AuthenticationContext} that holds an
* {@link OAuth2ClientAuthenticationToken} and additional information and is used when
* validating an OAuth 2.0 Client Authentication.
*
* @author Joe Grandja
* @since 1.3
@@ -35,6 +36,7 @@ import org.springframework.util.Assert;
* @see X509ClientCertificateAuthenticationProvider#setCertificateVerifier(Consumer)
*/
public final class OAuth2ClientAuthenticationContext implements OAuth2AuthenticationContext {
private final Map<Object, Object> context;
private OAuth2ClientAuthenticationContext(Map<Object, Object> context) {
@@ -56,7 +58,6 @@ public final class OAuth2ClientAuthenticationContext implements OAuth2Authentica
/**
* Returns the {@link RegisteredClient registered client}.
*
* @return the {@link RegisteredClient}
*/
public RegisteredClient getRegisteredClient() {
@@ -64,8 +65,8 @@ public final class OAuth2ClientAuthenticationContext implements OAuth2Authentica
}
/**
* Constructs a new {@link Builder} with the provided {@link OAuth2ClientAuthenticationToken}.
*
* Constructs a new {@link Builder} with the provided
* {@link OAuth2ClientAuthenticationToken}.
* @param authentication the {@link OAuth2ClientAuthenticationToken}
* @return the {@link Builder}
*/
@@ -84,7 +85,6 @@ public final class OAuth2ClientAuthenticationContext implements OAuth2Authentica
/**
* Sets the {@link RegisteredClient registered client}.
*
* @param registeredClient the {@link RegisteredClient}
* @return the {@link Builder} for further configuration
*/
@@ -94,7 +94,6 @@ public final class OAuth2ClientAuthenticationContext implements OAuth2Authentica
/**
* Builds a new {@link OAuth2ClientAuthenticationContext}.
*
* @return the {@link OAuth2ClientAuthenticationContext}
*/
public OAuth2ClientAuthenticationContext build() {

View File

@@ -25,8 +25,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.util.Assert;
/**
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2ClientCredentialsAuthenticationToken} and additional information
* and is used when validating the OAuth 2.0 Client Credentials Grant Request.
* An {@link OAuth2AuthenticationContext} that holds an
* {@link OAuth2ClientCredentialsAuthenticationToken} and additional information and is
* used when validating the OAuth 2.0 Client Credentials Grant Request.
*
* @author Adam Pilling
* @since 1.3
@@ -35,6 +36,7 @@ import org.springframework.util.Assert;
* @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)
*/
public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth2AuthenticationContext {
private final Map<Object, Object> context;
private OAuth2ClientCredentialsAuthenticationContext(Map<Object, Object> context) {
@@ -56,7 +58,6 @@ public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth
/**
* Returns the {@link RegisteredClient registered client}.
*
* @return the {@link RegisteredClient}
*/
public RegisteredClient getRegisteredClient() {
@@ -64,8 +65,8 @@ public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth
}
/**
* Constructs a new {@link Builder} with the provided {@link OAuth2ClientCredentialsAuthenticationToken}.
*
* Constructs a new {@link Builder} with the provided
* {@link OAuth2ClientCredentialsAuthenticationToken}.
* @param authentication the {@link OAuth2ClientCredentialsAuthenticationToken}
* @return the {@link Builder}
*/
@@ -84,7 +85,6 @@ public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth
/**
* Sets the {@link RegisteredClient registered client}.
*
* @param registeredClient the {@link RegisteredClient}
* @return the {@link Builder} for further configuration
*/
@@ -94,7 +94,6 @@ public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth
/**
* Builds a new {@link OAuth2ClientCredentialsAuthenticationContext}.
*
* @return the {@link OAuth2ClientCredentialsAuthenticationContext}
*/
public OAuth2ClientCredentialsAuthenticationContext build() {

View File

@@ -71,8 +71,8 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
private Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator =
new OAuth2ClientCredentialsAuthenticationValidator();
private Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = new OAuth2ClientCredentialsAuthenticationValidator();
/**
* Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the
@@ -103,16 +103,17 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.CLIENT_CREDENTIALS)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: requested grant_type is not allowed" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: requested grant_type is not allowed" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
OAuth2ClientCredentialsAuthenticationContext authenticationContext =
OAuth2ClientCredentialsAuthenticationContext.with(clientCredentialsAuthentication)
.registeredClient(registeredClient)
.build();
OAuth2ClientCredentialsAuthenticationContext authenticationContext = OAuth2ClientCredentialsAuthenticationContext
.with(clientCredentialsAuthentication)
.registeredClient(registeredClient)
.build();
this.authenticationValidator.accept(authenticationContext);
Set<String> authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());
@@ -173,18 +174,22 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
}
/**
* Sets the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext}
* and is responsible for validating specific OAuth 2.0 Client Credentials Grant Request parameters
* associated in the {@link OAuth2ClientCredentialsAuthenticationToken}.
* The default authentication validator is {@link OAuth2ClientCredentialsAuthenticationValidator}.
* Sets the {@code Consumer} providing access to the
* {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for
* validating specific OAuth 2.0 Client Credentials Grant Request parameters
* associated in the {@link OAuth2ClientCredentialsAuthenticationToken}. The default
* authentication validator is {@link OAuth2ClientCredentialsAuthenticationValidator}.
*
* <p>
* <b>NOTE:</b> The authentication validator MUST throw {@link OAuth2AuthenticationException} if validation fails.
*
* @param authenticationValidator the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for validating specific OAuth 2.0 Client Credentials Grant Request parameters
* <b>NOTE:</b> The authentication validator MUST throw
* {@link OAuth2AuthenticationException} if validation fails.
* @param authenticationValidator the {@code Consumer} providing access to the
* {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for
* validating specific OAuth 2.0 Client Credentials Grant Request parameters
* @since 1.3
*/
public void setAuthenticationValidator(Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator) {
public void setAuthenticationValidator(
Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator) {
Assert.notNull(authenticationValidator, "authenticationValidator cannot be null");
this.authenticationValidator = authenticationValidator;
}

View File

@@ -27,14 +27,17 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
/**
* A {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext}
* containing an {@link OAuth2ClientCredentialsAuthenticationToken}
* and is the default {@link OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer) authentication validator}
* used for validating specific OAuth 2.0 Client Credentials Grant Request parameters.
* A {@code Consumer} providing access to the
* {@link OAuth2ClientCredentialsAuthenticationContext} containing an
* {@link OAuth2ClientCredentialsAuthenticationToken} and is the default
* {@link OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)
* authentication validator} used for validating specific OAuth 2.0 Client Credentials
* Grant Request parameters.
*
* <p>
* The default implementation validates {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}.
* If validation fails, an {@link OAuth2AuthenticationException} is thrown.
* The default implementation validates
* {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}. If validation fails, an
* {@link OAuth2AuthenticationException} is thrown.
*
* @author Adam Pilling
* @since 1.3
@@ -42,14 +45,16 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
* @see OAuth2ClientCredentialsAuthenticationToken
* @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)
*/
public final class OAuth2ClientCredentialsAuthenticationValidator implements Consumer<OAuth2ClientCredentialsAuthenticationContext> {
public final class OAuth2ClientCredentialsAuthenticationValidator
implements Consumer<OAuth2ClientCredentialsAuthenticationContext> {
private static final Log LOGGER = LogFactory.getLog(OAuth2ClientCredentialsAuthenticationValidator.class);
/**
* The default validator for {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}.
* The default validator for
* {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}.
*/
public static final Consumer<OAuth2ClientCredentialsAuthenticationContext> DEFAULT_SCOPE_VALIDATOR =
OAuth2ClientCredentialsAuthenticationValidator::validateScope;
public static final Consumer<OAuth2ClientCredentialsAuthenticationContext> DEFAULT_SCOPE_VALIDATOR = OAuth2ClientCredentialsAuthenticationValidator::validateScope;
private final Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = DEFAULT_SCOPE_VALIDATOR;
@@ -59,16 +64,17 @@ public final class OAuth2ClientCredentialsAuthenticationValidator implements Con
}
private static void validateScope(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
authenticationContext.getAuthentication();
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = authenticationContext
.getAuthentication();
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
Set<String> requestedScopes = clientCredentialsAuthentication.getScopes();
Set<String> allowedScopes = registeredClient.getScopes();
if (!requestedScopes.isEmpty() && !allowedScopes.containsAll(requestedScopes)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(LogMessage.format("Invalid request: requested scope is not allowed" +
" for registered client '%s'", registeredClient.getId()));
LOGGER.debug(LogMessage.format(
"Invalid request: requested scope is not allowed" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
}

View File

@@ -108,8 +108,9 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.DEVICE_CODE)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: requested grant_type is not allowed" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: requested grant_type is not allowed" + " for registered client '%s'",
registeredClient.getId()));
}
throwError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID);
}

View File

@@ -127,8 +127,9 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: requested grant_type is not allowed" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: requested grant_type is not allowed" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
@@ -137,10 +138,12 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
if (!refreshToken.isActive()) {
// As per https://tools.ietf.org/html/rfc6749#section-5.2
// invalid_grant: The provided authorization grant (e.g., authorization code,
// resource owner credentials) or refresh token is invalid, expired, revoked [...].
// resource owner credentials) or refresh token is invalid, expired, revoked
// [...].
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalid request: refresh_token is not active" +
" for registered client '%s'", registeredClient.getId()));
this.logger.debug(LogMessage.format(
"Invalid request: refresh_token is not active" + " for registered client '%s'",
registeredClient.getId()));
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}

View File

@@ -25,8 +25,9 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.util.Assert;
/**
* A {@link ClaimAccessor} used for the OAuth 2.0 Token Exchange Grant to represent an actor in a
* {@link OAuth2TokenExchangeCompositeAuthenticationToken} (e.g. the "delegation" use case).
* A {@link ClaimAccessor} used for the OAuth 2.0 Token Exchange Grant to represent an
* actor in a {@link OAuth2TokenExchangeCompositeAuthenticationToken} (e.g. the
* "delegation" use case).
*
* @author Steve Riesenberg
* @since 1.3

View File

@@ -55,7 +55,8 @@ import org.springframework.util.StringUtils;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
/**
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Token Exchange Grant.
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Token Exchange
* Grant.
*
* @author Steve Riesenberg
* @since 1.3
@@ -63,8 +64,10 @@ import static org.springframework.security.oauth2.server.authorization.authentic
* @see OAuth2AccessTokenAuthenticationToken
* @see OAuth2AuthorizationService
* @see OAuth2TokenGenerator
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8693#section-1">Section 1 Introduction</a>
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8693#section-2.1">Section 2.1 Request</a>
* @see <a target="_blank" href=
* "https://datatracker.ietf.org/doc/html/rfc8693#section-1">Section 1 Introduction</a>
* @see <a target="_blank" href=
* "https://datatracker.ietf.org/doc/html/rfc8693#section-2.1">Section 2.1 Request</a>
*/
public final class OAuth2TokenExchangeAuthenticationProvider implements AuthenticationProvider {
@@ -83,8 +86,8 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
/**
* Constructs an {@code OAuth2TokenExchangeAuthenticationProvider} using the provided parameters.
*
* Constructs an {@code OAuth2TokenExchangeAuthenticationProvider} using the provided
* parameters.
* @param authorizationService the authorization service
* @param tokenGenerator the token generator
*/
@@ -98,11 +101,10 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication =
(OAuth2TokenExchangeAuthenticationToken) authentication;
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = (OAuth2TokenExchangeAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(tokenExchangeAuthentication);
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
tokenExchangeAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
if (this.logger.isTraceEnabled()) {
@@ -113,13 +115,14 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
if (JWT_TOKEN_TYPE_VALUE.equals(tokenExchangeAuthentication.getRequestedTokenType()) &&
!OAuth2TokenFormat.SELF_CONTAINED.equals(registeredClient.getTokenSettings().getAccessTokenFormat())) {
if (JWT_TOKEN_TYPE_VALUE.equals(tokenExchangeAuthentication.getRequestedTokenType())
&& !OAuth2TokenFormat.SELF_CONTAINED
.equals(registeredClient.getTokenSettings().getAccessTokenFormat())) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
}
OAuth2Authorization subjectAuthorization = this.authorizationService.findByToken(
tokenExchangeAuthentication.getSubjectToken(), OAuth2TokenType.ACCESS_TOKEN);
OAuth2Authorization subjectAuthorization = this.authorizationService
.findByToken(tokenExchangeAuthentication.getSubjectToken(), OAuth2TokenType.ACCESS_TOKEN);
if (subjectAuthorization == null) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
@@ -128,12 +131,13 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
this.logger.trace("Retrieved authorization with subject token");
}
OAuth2Authorization.Token<OAuth2Token> subjectToken = subjectAuthorization.getToken(
tokenExchangeAuthentication.getSubjectToken());
OAuth2Authorization.Token<OAuth2Token> subjectToken = subjectAuthorization
.getToken(tokenExchangeAuthentication.getSubjectToken());
if (!subjectToken.isActive()) {
// As per https://tools.ietf.org/html/rfc6749#section-5.2
// invalid_grant: The provided authorization grant (e.g., authorization code,
// resource owner credentials) or refresh token is invalid, expired, revoked [...].
// resource owner credentials) or refresh token is invalid, expired, revoked
// [...].
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
@@ -152,16 +156,15 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
// The may_act claim makes a statement that one party is authorized to
// become the actor and act on behalf of another party.
Map<String, Object> authorizedActorClaims = null;
if (subjectToken.getClaims() != null &&
subjectToken.getClaims().containsKey(MAY_ACT) &&
subjectToken.getClaims().get(MAY_ACT) instanceof Map<?, ?> mayAct) {
if (subjectToken.getClaims() != null && subjectToken.getClaims().containsKey(MAY_ACT)
&& subjectToken.getClaims().get(MAY_ACT) instanceof Map<?, ?> mayAct) {
authorizedActorClaims = (Map<String, Object>) mayAct;
}
OAuth2Authorization actorAuthorization = null;
if (StringUtils.hasText(tokenExchangeAuthentication.getActorToken())) {
actorAuthorization = this.authorizationService.findByToken(
tokenExchangeAuthentication.getActorToken(), OAuth2TokenType.ACCESS_TOKEN);
actorAuthorization = this.authorizationService.findByToken(tokenExchangeAuthentication.getActorToken(),
OAuth2TokenType.ACCESS_TOKEN);
if (actorAuthorization == null) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
@@ -170,12 +173,14 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
this.logger.trace("Retrieved authorization with actor token");
}
OAuth2Authorization.Token<OAuth2Token> actorToken = actorAuthorization.getToken(
tokenExchangeAuthentication.getActorToken());
OAuth2Authorization.Token<OAuth2Token> actorToken = actorAuthorization
.getToken(tokenExchangeAuthentication.getActorToken());
if (!actorToken.isActive()) {
// As per https://tools.ietf.org/html/rfc6749#section-5.2
// invalid_grant: The provided authorization grant (e.g., authorization code,
// resource owner credentials) or refresh token is invalid, expired, revoked [...].
// invalid_grant: The provided authorization grant (e.g., authorization
// code,
// resource owner credentials) or refresh token is invalid, expired,
// revoked [...].
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
@@ -187,14 +192,16 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
validateClaims(authorizedActorClaims, actorToken.getClaims(), OAuth2TokenClaimNames.ISS,
OAuth2TokenClaimNames.SUB);
}
} else if (authorizedActorClaims != null) {
}
else if (authorizedActorClaims != null) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
Set<String> authorizedScopes = Collections.emptySet();
if (!CollectionUtils.isEmpty(tokenExchangeAuthentication.getScopes())) {
authorizedScopes = validateRequestedScopes(registeredClient, tokenExchangeAuthentication.getScopes());
} else if (!CollectionUtils.isEmpty(subjectAuthorization.getAuthorizedScopes())) {
}
else if (!CollectionUtils.isEmpty(subjectAuthorization.getAuthorizedScopes())) {
authorizedScopes = validateRequestedScopes(registeredClient, subjectAuthorization.getAuthorizedScopes());
}
@@ -248,21 +255,21 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
}
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(OAuth2ParameterNames.ISSUED_TOKEN_TYPE, tokenExchangeAuthentication.getRequestedTokenType());
additionalParameters.put(OAuth2ParameterNames.ISSUED_TOKEN_TYPE,
tokenExchangeAuthentication.getRequestedTokenType());
if (this.logger.isTraceEnabled()) {
this.logger.trace("Authenticated token request");
}
return new OAuth2AccessTokenAuthenticationToken(
registeredClient, clientPrincipal, accessToken, null, additionalParameters);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, null,
additionalParameters);
}
private static boolean isValidTokenType(String tokenType, OAuth2Authorization.Token<OAuth2Token> token) {
String tokenFormat = token.getMetadata(OAuth2TokenFormat.class.getName());
return ACCESS_TOKEN_TYPE_VALUE.equals(tokenType) ||
JWT_TOKEN_TYPE_VALUE.equals(tokenType) &&
OAuth2TokenFormat.SELF_CONTAINED.getValue().equals(tokenFormat);
return ACCESS_TOKEN_TYPE_VALUE.equals(tokenType) || JWT_TOKEN_TYPE_VALUE.equals(tokenType)
&& OAuth2TokenFormat.SELF_CONTAINED.getValue().equals(tokenFormat);
}
private static Set<String> validateRequestedScopes(RegisteredClient registeredClient, Set<String> requestedScopes) {
@@ -275,7 +282,8 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
return new LinkedHashSet<>(requestedScopes);
}
private static void validateClaims(Map<String, Object> expectedClaims, Map<String, Object> actualClaims, String... claimNames) {
private static void validateClaims(Map<String, Object> expectedClaims, Map<String, Object> actualClaims,
String... claimNames) {
if (actualClaims == null) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
}
@@ -287,7 +295,8 @@ public final class OAuth2TokenExchangeAuthenticationProvider implements Authenti
}
}
private static Authentication getPrincipal(OAuth2Authorization subjectAuthorization, OAuth2Authorization actorAuthorization) {
private static Authentication getPrincipal(OAuth2Authorization subjectAuthorization,
OAuth2Authorization actorAuthorization) {
Authentication subjectPrincipal = subjectAuthorization.getAttribute(Principal.class.getName());
if (actorAuthorization == null) {
if (subjectPrincipal instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {

View File

@@ -53,8 +53,8 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
private final Set<String> scopes;
/**
* Constructs an {@code OAuth2TokenExchangeAuthenticationToken} using the provided parameters.
*
* Constructs an {@code OAuth2TokenExchangeAuthenticationToken} using the provided
* parameters.
* @param requestedTokenType the requested token type
* @param subjectToken the subject token
* @param subjectTokenType the subject token type
@@ -79,17 +79,15 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
this.subjectTokenType = subjectTokenType;
this.actorToken = actorToken;
this.actorTokenType = actorTokenType;
this.resources = Collections.unmodifiableSet(
resources != null ? new LinkedHashSet<>(resources) : Collections.emptySet());
this.audiences = Collections.unmodifiableSet(
audiences != null ? new LinkedHashSet<>(audiences) : Collections.emptySet());
this.scopes = Collections.unmodifiableSet(
scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
this.resources = Collections
.unmodifiableSet(resources != null ? new LinkedHashSet<>(resources) : Collections.emptySet());
this.audiences = Collections
.unmodifiableSet(audiences != null ? new LinkedHashSet<>(audiences) : Collections.emptySet());
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
}
/**
* Returns the requested token type.
*
* @return the requested token type
*/
public String getRequestedTokenType() {
@@ -98,7 +96,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the subject token.
*
* @return the subject token
*/
public String getSubjectToken() {
@@ -107,7 +104,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the subject token type.
*
* @return the subject token type
*/
public String getSubjectTokenType() {
@@ -116,7 +112,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the actor token.
*
* @return the actor token
*/
public String getActorToken() {
@@ -125,7 +120,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the actor token type.
*
* @return the actor token type
*/
public String getActorTokenType() {
@@ -134,7 +128,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the requested resource URI(s).
*
* @return the requested resource URI(s), or an empty {@code Set} if not available
*/
public Set<String> getResources() {
@@ -143,7 +136,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the requested audience value(s).
*
* @return the requested audience value(s), or an empty {@code Set} if not available
*/
public Set<String> getAudiences() {
@@ -152,7 +144,6 @@ public class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationG
/**
* Returns the requested scope(s).
*
* @return the requested scope(s), or an empty {@code Set} if not available
*/
public Set<String> getScopes() {

View File

@@ -26,8 +26,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link Authentication} implementation used for the OAuth 2.0 Token Exchange Grant
* to represent the principal in a composite token (e.g. the "delegation" use case).
* An {@link Authentication} implementation used for the OAuth 2.0 Token Exchange Grant to
* represent the principal in a composite token (e.g. the "delegation" use case).
*
* @author Steve Riesenberg
* @since 1.3
@@ -39,7 +39,8 @@ public class OAuth2TokenExchangeCompositeAuthenticationToken extends AbstractAut
private final List<OAuth2TokenExchangeActor> actors;
public OAuth2TokenExchangeCompositeAuthenticationToken(Authentication subject, List<OAuth2TokenExchangeActor> actors) {
public OAuth2TokenExchangeCompositeAuthenticationToken(Authentication subject,
List<OAuth2TokenExchangeActor> actors) {
super(subject != null ? subject.getAuthorities() : null);
Assert.notNull(subject, "subject cannot be null");
Assert.notNull(actors, "actors cannot be null");
@@ -72,8 +73,8 @@ public class OAuth2TokenExchangeCompositeAuthenticationToken extends AbstractAut
if (!(obj instanceof OAuth2TokenExchangeCompositeAuthenticationToken other)) {
return false;
}
return super.equals(obj) && Objects.equals(this.subject, other.subject) &&
Objects.equals(this.actors, other.actors);
return super.equals(obj) && Objects.equals(this.subject, other.subject)
&& Objects.equals(this.actors, other.actors);
}
@Override

View File

@@ -37,9 +37,10 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client Authentication,
* which authenticates the client {@code X509Certificate} received
* when the {@code tls_client_auth} or {@code self_signed_tls_client_auth} authentication method is used.
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client
* Authentication, which authenticates the client {@code X509Certificate} received when
* the {@code tls_client_auth} or {@code self_signed_tls_client_auth} authentication
* method is used.
*
* @author Joe Grandja
* @since 1.3
@@ -49,17 +50,22 @@ import org.springframework.util.StringUtils;
* @see OAuth2AuthorizationService
*/
public final class X509ClientCertificateAuthenticationProvider implements AuthenticationProvider {
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
private final Log logger = LogFactory.getLog(getClass());
private final RegisteredClientRepository registeredClientRepository;
private final CodeVerifierAuthenticator codeVerifierAuthenticator;
private final Consumer<OAuth2ClientAuthenticationContext> selfSignedCertificateVerifier =
new X509SelfSignedCertificateVerifier();
private final Consumer<OAuth2ClientAuthenticationContext> selfSignedCertificateVerifier = new X509SelfSignedCertificateVerifier();
private Consumer<OAuth2ClientAuthenticationContext> certificateVerifier = this::verifyX509Certificate;
/**
* Constructs a {@code X509ClientCertificateAuthenticationProvider} using the provided parameters.
*
* Constructs a {@code X509ClientCertificateAuthenticationProvider} using the provided
* parameters.
* @param registeredClientRepository the repository of registered clients
* @param authorizationService the authorization service
*/
@@ -73,11 +79,11 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2ClientAuthenticationToken clientAuthentication =
(OAuth2ClientAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;
if (!ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod()) &&
!ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())) {
if (!ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())
&& !ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
.equals(clientAuthentication.getClientAuthenticationMethod())) {
return null;
}
@@ -91,8 +97,8 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
this.logger.trace("Retrieved registered client");
}
if (!registeredClient.getClientAuthenticationMethods().contains(
clientAuthentication.getClientAuthenticationMethod())) {
if (!registeredClient.getClientAuthenticationMethods()
.contains(clientAuthentication.getClientAuthenticationMethod())) {
throwInvalidClient("authentication_method");
}
@@ -100,17 +106,18 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
throwInvalidClient("credentials");
}
OAuth2ClientAuthenticationContext authenticationContext =
OAuth2ClientAuthenticationContext.with(clientAuthentication)
.registeredClient(registeredClient)
.build();
OAuth2ClientAuthenticationContext authenticationContext = OAuth2ClientAuthenticationContext
.with(clientAuthentication)
.registeredClient(registeredClient)
.build();
this.certificateVerifier.accept(authenticationContext);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Validated client authentication parameters");
}
// Validate the "code_verifier" parameter for the confidential client, if available
// Validate the "code_verifier" parameter for the confidential client, if
// available
this.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);
if (this.logger.isTraceEnabled()) {
@@ -127,15 +134,20 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
}
/**
* Sets the {@code Consumer} providing access to the {@link OAuth2ClientAuthenticationContext}
* and is responsible for verifying the client {@code X509Certificate} associated in the {@link OAuth2ClientAuthenticationToken}.
* The default implementation for the {@code tls_client_auth} authentication method
* verifies the {@link ClientSettings#getX509CertificateSubjectDN() expected subject distinguished name}.
* Sets the {@code Consumer} providing access to the
* {@link OAuth2ClientAuthenticationContext} and is responsible for verifying the
* client {@code X509Certificate} associated in the
* {@link OAuth2ClientAuthenticationToken}. The default implementation for the
* {@code tls_client_auth} authentication method verifies the
* {@link ClientSettings#getX509CertificateSubjectDN() expected subject distinguished
* name}.
*
* <p>
* <b>NOTE:</b> If verification fails, an {@link OAuth2AuthenticationException} MUST be thrown.
*
* @param certificateVerifier the {@code Consumer} providing access to the {@link OAuth2ClientAuthenticationContext} and is responsible for verifying the client {@code X509Certificate}
* <b>NOTE:</b> If verification fails, an {@link OAuth2AuthenticationException} MUST
* be thrown.
* @param certificateVerifier the {@code Consumer} providing access to the
* {@link OAuth2ClientAuthenticationContext} and is responsible for verifying the
* client {@code X509Certificate}
*/
public void setCertificateVerifier(Consumer<OAuth2ClientAuthenticationContext> certificateVerifier) {
Assert.notNull(certificateVerifier, "certificateVerifier cannot be null");
@@ -144,9 +156,11 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
private void verifyX509Certificate(OAuth2ClientAuthenticationContext clientAuthenticationContext) {
OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();
if (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())) {
if (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
.equals(clientAuthentication.getClientAuthenticationMethod())) {
this.selfSignedCertificateVerifier.accept(clientAuthenticationContext);
} else {
}
else {
verifyX509CertificateSubjectDN(clientAuthenticationContext);
}
}
@@ -157,8 +171,8 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
X509Certificate clientCertificate = clientCertificateChain[0];
String expectedSubjectDN = registeredClient.getClientSettings().getX509CertificateSubjectDN();
if (!StringUtils.hasText(expectedSubjectDN) ||
!clientCertificate.getSubjectX500Principal().getName().equals(expectedSubjectDN)) {
if (!StringUtils.hasText(expectedSubjectDN)
|| !clientCertificate.getSubjectX500Principal().getName().equals(expectedSubjectDN)) {
throwInvalidClient("x509_certificate_subject_dn");
}
}
@@ -168,11 +182,8 @@ public final class X509ClientCertificateAuthenticationProvider implements Authen
}
private static void throwInvalidClient(String parameterName, Throwable cause) {
OAuth2Error error = new OAuth2Error(
OAuth2ErrorCodes.INVALID_CLIENT,
"Client authentication failed: " + parameterName,
ERROR_URI
);
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
"Client authentication failed: " + parameterName, ERROR_URI);
throw new OAuth2AuthenticationException(error, error.toString(), cause);
}

View File

@@ -52,15 +52,20 @@ import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
/**
* The default {@code X509Certificate} verifier for the {@code self_signed_tls_client_auth} authentication method.
* The default {@code X509Certificate} verifier for the
* {@code self_signed_tls_client_auth} authentication method.
*
* @author Joe Grandja
* @since 1.3
* @see X509ClientCertificateAuthenticationProvider#setCertificateVerifier(Consumer)
*/
final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAuthenticationContext> {
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
private static final JWKMatcher HAS_X509_CERT_CHAIN_MATCHER = new JWKMatcher.Builder().hasX509CertChain(true).build();
private static final JWKMatcher HAS_X509_CERT_CHAIN_MATCHER = new JWKMatcher.Builder().hasX509CertChain(true)
.build();
private final Function<RegisteredClient, JWKSet> jwkSetSupplier = new JwkSetSupplier();
@Override
@@ -98,17 +103,17 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
}
private static void throwInvalidClient(String parameterName, Throwable cause) {
OAuth2Error error = new OAuth2Error(
OAuth2ErrorCodes.INVALID_CLIENT,
"Client authentication failed: " + parameterName,
ERROR_URI
);
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
"Client authentication failed: " + parameterName, ERROR_URI);
throw new OAuth2AuthenticationException(error, error.toString(), cause);
}
private static class JwkSetSupplier implements Function<RegisteredClient, JWKSet> {
private static final MediaType APPLICATION_JWK_SET_JSON = new MediaType("application", "jwk-set+json");
private final RestOperations restOperations;
private final Map<String, Supplier<JWKSet>> jwkSets = new ConcurrentHashMap<>();
private JwkSetSupplier() {
@@ -120,13 +125,12 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
@Override
public JWKSet apply(RegisteredClient registeredClient) {
Supplier<JWKSet> jwkSetSupplier = this.jwkSets.computeIfAbsent(
registeredClient.getId(), (key) -> {
if (!StringUtils.hasText(registeredClient.getClientSettings().getJwkSetUrl())) {
throwInvalidClient("client_jwk_set_url");
}
return new JwkSetHolder(registeredClient.getClientSettings().getJwkSetUrl());
});
Supplier<JWKSet> jwkSetSupplier = this.jwkSets.computeIfAbsent(registeredClient.getId(), (key) -> {
if (!StringUtils.hasText(registeredClient.getClientSettings().getJwkSetUrl())) {
throwInvalidClient("client_jwk_set_url");
}
return new JwkSetHolder(registeredClient.getClientSettings().getJwkSetUrl());
});
return jwkSetSupplier.get();
}
@@ -134,7 +138,8 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
URI jwkSetUri = null;
try {
jwkSetUri = new URI(jwkSetUrl);
} catch (URISyntaxException ex) {
}
catch (URISyntaxException ex) {
throwInvalidClient("jwk_set_uri", ex);
}
@@ -144,7 +149,8 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
ResponseEntity<String> response = null;
try {
response = this.restOperations.exchange(request, String.class);
} catch (Exception ex) {
}
catch (Exception ex) {
throwInvalidClient("jwk_set_response_error", ex);
}
if (response.getStatusCode().value() != 200) {
@@ -154,7 +160,8 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
JWKSet jwkSet = null;
try {
jwkSet = JWKSet.parse(response.getBody());
} catch (ParseException ex) {
}
catch (ParseException ex) {
throwInvalidClient("jwk_set_response_body", ex);
}
@@ -162,10 +169,15 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
}
private class JwkSetHolder implements Supplier<JWKSet> {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Clock clock = Clock.systemUTC();
private final String jwkSetUrl;
private JWKSet jwkSet;
private Instant lastUpdatedAt;
private JwkSetHolder(String jwkSetUrl) {
@@ -184,22 +196,24 @@ final class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAu
this.lastUpdatedAt = Instant.now();
}
this.rwLock.readLock().lock();
} finally {
}
finally {
this.rwLock.writeLock().unlock();
}
}
try {
return this.jwkSet;
} finally {
}
finally {
this.rwLock.readLock().unlock();
}
}
private boolean shouldRefresh() {
// Refresh every 5 minutes
return (this.jwkSet == null ||
this.clock.instant().isAfter(this.lastUpdatedAt.plus(5, ChronoUnit.MINUTES)));
return (this.jwkSet == null
|| this.clock.instant().isAfter(this.lastUpdatedAt.plus(5, ChronoUnit.MINUTES)));
}
}

View File

@@ -48,6 +48,7 @@ import org.springframework.web.util.UriComponentsBuilder;
final class AuthorizationServerContextFilter extends OncePerRequestFilter {
private final AuthorizationServerSettings authorizationServerSettings;
private final IssuerResolver issuerResolver;
AuthorizationServerContextFilter(AuthorizationServerSettings authorizationServerSettings) {
@@ -62,8 +63,8 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
try {
String issuer = this.issuerResolver.resolve(request);
AuthorizationServerContext authorizationServerContext =
new DefaultAuthorizationServerContext(issuer, this.authorizationServerSettings);
AuthorizationServerContext authorizationServerContext = new DefaultAuthorizationServerContext(issuer,
this.authorizationServerSettings);
AuthorizationServerContextHolder.setContext(authorizationServerContext);
filterChain.doFilter(request, response);
}
@@ -73,14 +74,17 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
}
private static final class IssuerResolver {
private final String issuer;
private final Set<String> endpointUris;
private IssuerResolver(AuthorizationServerSettings authorizationServerSettings) {
if (authorizationServerSettings.getIssuer() != null) {
this.issuer = authorizationServerSettings.getIssuer();
this.endpointUris = Collections.emptySet();
} else {
}
else {
this.issuer = null;
this.endpointUris = new HashSet<>();
this.endpointUris.add("/.well-known/oauth-authorization-server");
@@ -102,7 +106,8 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
String path = request.getRequestURI();
if (!StringUtils.hasText(path)) {
path = "";
} else {
}
else {
for (String endpointUri : this.endpointUris) {
if (path.contains(endpointUri)) {
path = path.replace(endpointUri, "");
@@ -124,10 +129,13 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
}
private static final class DefaultAuthorizationServerContext implements AuthorizationServerContext {
private final String issuer;
private final AuthorizationServerSettings authorizationServerSettings;
private DefaultAuthorizationServerContext(String issuer, AuthorizationServerSettings authorizationServerSettings) {
private DefaultAuthorizationServerContext(String issuer,
AuthorizationServerSettings authorizationServerSettings) {
this.issuer = issuer;
this.authorizationServerSettings = authorizationServerSettings;
}

View File

@@ -54,13 +54,14 @@ final class DefaultOAuth2TokenCustomizers {
private static void customize(OAuth2TokenContext tokenContext, Map<String, Object> claims) {
// Add 'cnf' claim for Mutual-TLS Client Certificate-Bound Access Tokens
if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType()) &&
tokenContext.getAuthorizationGrant() != null &&
tokenContext.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {
if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType())
&& tokenContext.getAuthorizationGrant() != null && tokenContext.getAuthorizationGrant()
.getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {
if ((ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod()) ||
ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())) &&
tokenContext.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
if ((ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())
|| ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
.equals(clientAuthentication.getClientAuthenticationMethod()))
&& tokenContext.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
try {
@@ -68,7 +69,8 @@ final class DefaultOAuth2TokenCustomizers {
Map<String, Object> x5tClaim = new HashMap<>();
x5tClaim.put("x5t#S256", sha256Thumbprint);
claims.put("cnf", x5tClaim);
} catch (Exception ex) {
}
catch (Exception ex) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"Failed to compute SHA-256 Thumbprint for client X509Certificate.", null);
throw new OAuth2AuthenticationException(error, ex);
@@ -77,8 +79,10 @@ final class DefaultOAuth2TokenCustomizers {
}
// Add 'act' claim for delegation use case of Token Exchange Grant.
// If more than one actor is present, we create a chain of delegation by nesting "act" claims.
if (tokenContext.getPrincipal() instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {
// If more than one actor is present, we create a chain of delegation by nesting
// "act" claims.
if (tokenContext
.getPrincipal() instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {
Map<String, Object> currentClaims = claims;
for (OAuth2TokenExchangeActor actor : compositeAuthenticationToken.getActors()) {
Map<String, Object> actorClaims = actor.getClaims();

View File

@@ -236,10 +236,11 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint()) :
authorizationServerSettings.getAuthorizationEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint())
: authorizationServerSettings.getAuthorizationEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(authorizationEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(authorizationEndpointUri, HttpMethod.POST.name()));
@@ -256,12 +257,13 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
@Override
void configure(HttpSecurity httpSecurity) {
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint()) :
authorizationServerSettings.getAuthorizationEndpoint();
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter =
new OAuth2AuthorizationEndpointFilter(authenticationManager, authorizationEndpointUri);
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint())
: authorizationServerSettings.getAuthorizationEndpoint();
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter(
authenticationManager, authorizationEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.authorizationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.authorizationRequestConverters);

View File

@@ -326,9 +326,9 @@ public final class OAuth2AuthorizationServerConfigurer
configurer.init(httpSecurity);
requestMatchers.add(configurer.getRequestMatcher());
});
String jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint()) :
authorizationServerSettings.getJwkSetEndpoint();
String jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint())
: authorizationServerSettings.getJwkSetEndpoint();
requestMatchers.add(new AntPathRequestMatcher(jwkSetEndpointUri, HttpMethod.GET.name()));
this.endpointsMatcher = new OrRequestMatcher(requestMatchers);
@@ -356,12 +356,13 @@ public final class OAuth2AuthorizationServerConfigurer
JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
if (jwkSource != null) {
String jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint()) :
authorizationServerSettings.getJwkSetEndpoint();
NimbusJwkSetEndpointFilter jwkSetEndpointFilter =
new NimbusJwkSetEndpointFilter(jwkSource, jwkSetEndpointUri);
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
String jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint())
: authorizationServerSettings.getJwkSetEndpoint();
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(jwkSource,
jwkSetEndpointUri);
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter),
AbstractPreAuthenticatedProcessingFilter.class);
}
}

View File

@@ -74,10 +74,10 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String authorizationServerMetadataEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
"/.well-known/oauth-authorization-server/**" :
"/.well-known/oauth-authorization-server";
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String authorizationServerMetadataEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? "/.well-known/oauth-authorization-server/**" : "/.well-known/oauth-authorization-server";
this.requestMatcher = new AntPathRequestMatcher(authorizationServerMetadataEndpointUri, HttpMethod.GET.name());
}

View File

@@ -180,21 +180,21 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint()) :
authorizationServerSettings.getTokenEndpoint();
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()) :
authorizationServerSettings.getTokenIntrospectionEndpoint();
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint()) :
authorizationServerSettings.getTokenRevocationEndpoint();
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()) :
authorizationServerSettings.getDeviceAuthorizationEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name()),
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())
: authorizationServerSettings.getTokenEndpoint();
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())
: authorizationServerSettings.getTokenIntrospectionEndpoint();
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())
: authorizationServerSettings.getTokenRevocationEndpoint();
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())
: authorizationServerSettings.getDeviceAuthorizationEndpoint();
this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(tokenIntrospectionEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(tokenRevocationEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(deviceAuthorizationEndpointUri, HttpMethod.POST.name()));
@@ -257,12 +257,12 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
registeredClientRepository, authorizationService);
authenticationProviders.add(jwtClientAssertionAuthenticationProvider);
X509ClientCertificateAuthenticationProvider x509ClientCertificateAuthenticationProvider =
new X509ClientCertificateAuthenticationProvider(registeredClientRepository, authorizationService);
X509ClientCertificateAuthenticationProvider x509ClientCertificateAuthenticationProvider = new X509ClientCertificateAuthenticationProvider(
registeredClientRepository, authorizationService);
authenticationProviders.add(x509ClientCertificateAuthenticationProvider);
ClientSecretAuthenticationProvider clientSecretAuthenticationProvider =
new ClientSecretAuthenticationProvider(registeredClientRepository, authorizationService);
ClientSecretAuthenticationProvider clientSecretAuthenticationProvider = new ClientSecretAuthenticationProvider(
registeredClientRepository, authorizationService);
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);
if (passwordEncoder != null) {
clientSecretAuthenticationProvider.setPasswordEncoder(passwordEncoder);

View File

@@ -59,9 +59,7 @@ final class OAuth2ConfigurerUtils {
static String withMultipleIssuersPattern(String endpointUri) {
Assert.hasText(endpointUri, "endpointUri cannot be empty");
return endpointUri.startsWith("/") ?
"/**" + endpointUri :
"/**/" + endpointUri;
return endpointUri.startsWith("/") ? "/**" + endpointUri : "/**/" + endpointUri;
}
static RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) {
@@ -168,8 +166,10 @@ final class OAuth2ConfigurerUtils {
}
private static OAuth2TokenCustomizer<JwtEncodingContext> getJwtCustomizer(HttpSecurity httpSecurity) {
final OAuth2TokenCustomizer<JwtEncodingContext> defaultJwtCustomizer = DefaultOAuth2TokenCustomizers.jwtCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class);
final OAuth2TokenCustomizer<JwtEncodingContext> defaultJwtCustomizer = DefaultOAuth2TokenCustomizers
.jwtCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,
JwtEncodingContext.class);
final OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer = getOptionalBean(httpSecurity, type);
if (jwtCustomizer == null) {
return defaultJwtCustomizer;
@@ -181,8 +181,10 @@ final class OAuth2ConfigurerUtils {
}
private static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> getAccessTokenCustomizer(HttpSecurity httpSecurity) {
final OAuth2TokenCustomizer<OAuth2TokenClaimsContext> defaultAccessTokenCustomizer = DefaultOAuth2TokenCustomizers.accessTokenCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, OAuth2TokenClaimsContext.class);
final OAuth2TokenCustomizer<OAuth2TokenClaimsContext> defaultAccessTokenCustomizer = DefaultOAuth2TokenCustomizers
.accessTokenCustomizer();
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,
OAuth2TokenClaimsContext.class);
OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getOptionalBean(httpSecurity, type);
if (accessTokenCustomizer == null) {
return defaultAccessTokenCustomizer;

View File

@@ -197,11 +197,11 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
@Override
public void init(HttpSecurity builder) {
AuthorizationServerSettings authorizationServerSettings =
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()) :
authorizationServerSettings.getDeviceAuthorizationEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(builder);
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())
: authorizationServerSettings.getDeviceAuthorizationEndpoint();
this.requestMatcher = new AntPathRequestMatcher(deviceAuthorizationEndpointUri, HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);
@@ -219,11 +219,11 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(builder);
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()) :
authorizationServerSettings.getDeviceAuthorizationEndpoint();
OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter =
new OAuth2DeviceAuthorizationEndpointFilter(authenticationManager, deviceAuthorizationEndpointUri);
String deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())
: authorizationServerSettings.getDeviceAuthorizationEndpoint();
OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter = new OAuth2DeviceAuthorizationEndpointFilter(
authenticationManager, deviceAuthorizationEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.deviceAuthorizationRequestConverters.isEmpty()) {

View File

@@ -232,11 +232,11 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
@Override
public void init(HttpSecurity builder) {
AuthorizationServerSettings authorizationServerSettings =
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
String deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint()) :
authorizationServerSettings.getDeviceVerificationEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(builder);
String deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint())
: authorizationServerSettings.getDeviceVerificationEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(deviceVerificationEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(deviceVerificationEndpointUri, HttpMethod.POST.name()));
@@ -256,11 +256,11 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(builder);
String deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint()) :
authorizationServerSettings.getDeviceVerificationEndpoint();
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter =
new OAuth2DeviceVerificationEndpointFilter(authenticationManager, deviceVerificationEndpointUri);
String deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint())
: authorizationServerSettings.getDeviceVerificationEndpoint();
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter = new OAuth2DeviceVerificationEndpointFilter(
authenticationManager, deviceVerificationEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.deviceVerificationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.deviceVerificationRequestConverters);

View File

@@ -185,10 +185,11 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint()) :
authorizationServerSettings.getTokenEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())
: authorizationServerSettings.getTokenEndpoint();
this.requestMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
@@ -206,11 +207,11 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint()) :
authorizationServerSettings.getTokenEndpoint();
OAuth2TokenEndpointFilter tokenEndpointFilter =
new OAuth2TokenEndpointFilter(authenticationManager, tokenEndpointUri);
String tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())
: authorizationServerSettings.getTokenEndpoint();
OAuth2TokenEndpointFilter tokenEndpointFilter = new OAuth2TokenEndpointFilter(authenticationManager,
tokenEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.accessTokenRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.accessTokenRequestConverters);
@@ -270,8 +271,8 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
authorizationService, tokenGenerator);
authenticationProviders.add(deviceCodeAuthenticationProvider);
OAuth2TokenExchangeAuthenticationProvider tokenExchangeAuthenticationProvider =
new OAuth2TokenExchangeAuthenticationProvider(authorizationService, tokenGenerator);
OAuth2TokenExchangeAuthenticationProvider tokenExchangeAuthenticationProvider = new OAuth2TokenExchangeAuthenticationProvider(
authorizationService, tokenGenerator);
authenticationProviders.add(tokenExchangeAuthenticationProvider);
return authenticationProviders;

View File

@@ -180,10 +180,11 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()) :
authorizationServerSettings.getTokenIntrospectionEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())
: authorizationServerSettings.getTokenIntrospectionEndpoint();
this.requestMatcher = new AntPathRequestMatcher(tokenIntrospectionEndpointUri, HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
@@ -198,12 +199,13 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
@Override
void configure(HttpSecurity httpSecurity) {
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()) :
authorizationServerSettings.getTokenIntrospectionEndpoint();
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter =
new OAuth2TokenIntrospectionEndpointFilter(authenticationManager, tokenIntrospectionEndpointUri);
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())
: authorizationServerSettings.getTokenIntrospectionEndpoint();
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter = new OAuth2TokenIntrospectionEndpointFilter(
authenticationManager, tokenIntrospectionEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.introspectionRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.introspectionRequestConverters);

View File

@@ -179,10 +179,11 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint()) :
authorizationServerSettings.getTokenRevocationEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())
: authorizationServerSettings.getTokenRevocationEndpoint();
this.requestMatcher = new AntPathRequestMatcher(tokenRevocationEndpointUri, HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
@@ -200,11 +201,11 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint()) :
authorizationServerSettings.getTokenRevocationEndpoint();
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter =
new OAuth2TokenRevocationEndpointFilter(authenticationManager, tokenRevocationEndpointUri);
String tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())
: authorizationServerSettings.getTokenRevocationEndpoint();
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter = new OAuth2TokenRevocationEndpointFilter(
authenticationManager, tokenRevocationEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.revocationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.revocationRequestConverters);

View File

@@ -190,10 +190,11 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint()) :
authorizationServerSettings.getOidcClientRegistrationEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint())
: authorizationServerSettings.getOidcClientRegistrationEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name()));
@@ -213,11 +214,11 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint()) :
authorizationServerSettings.getOidcClientRegistrationEndpoint();
OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter =
new OidcClientRegistrationEndpointFilter(authenticationManager, clientRegistrationEndpointUri);
String clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint())
: authorizationServerSettings.getOidcClientRegistrationEndpoint();
OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = new OidcClientRegistrationEndpointFilter(
authenticationManager, clientRegistrationEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.clientRegistrationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.clientRegistrationRequestConverters);

View File

@@ -165,14 +165,13 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint()) :
authorizationServerSettings.getOidcLogoutEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name())
);
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint())
: authorizationServerSettings.getOidcLogoutEndpoint();
this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
@@ -189,11 +188,11 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint()) :
authorizationServerSettings.getOidcLogoutEndpoint();
OidcLogoutEndpointFilter oidcLogoutEndpointFilter =
new OidcLogoutEndpointFilter(authenticationManager, logoutEndpointUri);
String logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint())
: authorizationServerSettings.getOidcLogoutEndpoint();
OidcLogoutEndpointFilter oidcLogoutEndpointFilter = new OidcLogoutEndpointFilter(authenticationManager,
logoutEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.logoutRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.logoutRequestConverters);

View File

@@ -74,10 +74,10 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String oidcProviderConfigurationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
"/**/.well-known/openid-configuration" :
"/.well-known/openid-configuration";
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String oidcProviderConfigurationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? "/**/.well-known/openid-configuration" : "/.well-known/openid-configuration";
this.requestMatcher = new AntPathRequestMatcher(oidcProviderConfigurationEndpointUri, HttpMethod.GET.name());
}

View File

@@ -208,10 +208,11 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint()) :
authorizationServerSettings.getOidcUserInfoEndpoint();
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint())
: authorizationServerSettings.getOidcUserInfoEndpoint();
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.POST.name()));
@@ -231,11 +232,11 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
.getAuthorizationServerSettings(httpSecurity);
String userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed() ?
withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint()) :
authorizationServerSettings.getOidcUserInfoEndpoint();
OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter =
new OidcUserInfoEndpointFilter(authenticationManager, userInfoEndpointUri);
String userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()
? withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint())
: authorizationServerSettings.getOidcUserInfoEndpoint();
OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter = new OidcUserInfoEndpointFilter(authenticationManager,
userInfoEndpointUri);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.userInfoRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.userInfoRequestConverters);

View File

@@ -32,18 +32,24 @@ public interface AuthorizationServerContext {
* resolves the issuer identifier from the <i>"current"</i> request.
*
* <p>
* The issuer identifier may contain a path component to support {@link AuthorizationServerSettings#isMultipleIssuersAllowed() multiple issuers per host} in a multi-tenant hosting configuration.
* The issuer identifier may contain a path component to support
* {@link AuthorizationServerSettings#isMultipleIssuersAllowed() multiple issuers per
* host} in a multi-tenant hosting configuration.
*
* <p>
* For example:
* <ul>
* <li>{@code https://example.com/issuer1/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/issuer1}</li>
* <li>{@code https://example.com/issuer2/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/issuer2}</li>
* <li>{@code https://example.com/authz/issuer1/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/authz/issuer1}</li>
* <li>{@code https://example.com/authz/issuer2/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/authz/issuer2}</li>
* <li>{@code https://example.com/issuer1/oauth2/token} &mdash; resolves the issuer to
* {@code https://example.com/issuer1}</li>
* <li>{@code https://example.com/issuer2/oauth2/token} &mdash; resolves the issuer to
* {@code https://example.com/issuer2}</li>
* <li>{@code https://example.com/authz/issuer1/oauth2/token} &mdash; resolves the
* issuer to {@code https://example.com/authz/issuer1}</li>
* <li>{@code https://example.com/authz/issuer2/oauth2/token} &mdash; resolves the
* issuer to {@code https://example.com/authz/issuer2}</li>
* </ul>
*
* @return {@link AuthorizationServerSettings#getIssuer()} if available, otherwise, resolves the issuer identifier from the <i>"current"</i> request
* @return {@link AuthorizationServerSettings#getIssuer()} if available, otherwise,
* resolves the issuer identifier from the <i>"current"</i> request
*/
String getIssuer();

View File

@@ -28,7 +28,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
/**
* This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeCompositeAuthenticationToken}.
* This mixin class is used to serialize/deserialize
* {@link OAuth2TokenExchangeCompositeAuthenticationToken}.
*
* @author Steve Riesenberg
* @since 1.3

View File

@@ -67,11 +67,11 @@ final class OidcAuthenticationProviderUtils {
static <T extends OAuth2Token> OAuth2AccessToken accessToken(OAuth2Authorization.Builder builder, T token,
OAuth2TokenContext accessTokenContext) {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
token.getTokenValue(), token.getIssuedAt(), token.getExpiresAt(),
accessTokenContext.getAuthorizedScopes());
OAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient().getTokenSettings()
.getAccessTokenFormat();
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token.getTokenValue(),
token.getIssuedAt(), token.getExpiresAt(), accessTokenContext.getAuthorizedScopes());
OAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient()
.getTokenSettings()
.getAccessTokenFormat();
builder.token(accessToken, (metadata) -> {
if (token instanceof ClaimAccessor claimAccessor) {
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claimAccessor.getClaims());

View File

@@ -63,9 +63,11 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration";
private final RequestMatcher requestMatcher = createRequestMatcher();
private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter =
new OidcProviderConfigurationHttpMessageConverter();
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = (providerConfiguration) -> {};
private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = new OidcProviderConfigurationHttpMessageConverter();
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = (providerConfiguration) -> {
};
/**
* Sets the {@code Consumer} providing access to the
@@ -96,29 +98,29 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
.getAuthorizationServerSettings();
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
.userInfoEndpoint(asUrl(issuer, authorizationServerSettings.getOidcUserInfoEndpoint()))
.endSessionEndpoint(asUrl(issuer, authorizationServerSettings.getOidcLogoutEndpoint()))
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true)
.subjectType("public")
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
.scope(OidcScopes.OPENID);
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
.userInfoEndpoint(asUrl(issuer, authorizationServerSettings.getOidcUserInfoEndpoint()))
.endSessionEndpoint(asUrl(issuer, authorizationServerSettings.getOidcLogoutEndpoint()))
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true)
.subjectType("public")
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
.scope(OidcScopes.OPENID);
this.providerConfigurationCustomizer.accept(providerConfiguration);
@@ -132,10 +134,10 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name());
final RequestMatcher multipleIssuersRequestMatcher = new AntPathRequestMatcher(
"/**" + DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name());
return (request) ->
AuthorizationServerContextHolder.getContext().getAuthorizationServerSettings().isMultipleIssuersAllowed() ?
multipleIssuersRequestMatcher.matches(request) :
defaultRequestMatcher.matches(request);
return (request) -> AuthorizationServerContextHolder.getContext()
.getAuthorizationServerSettings()
.isMultipleIssuersAllowed() ? multipleIssuersRequestMatcher.matches(request)
: defaultRequestMatcher.matches(request);
}
private static Consumer<List<String>> clientAuthenticationMethods() {

View File

@@ -44,8 +44,9 @@ public final class AuthorizationServerSettings extends AbstractSettings {
}
/**
* Returns {@code true} if multiple issuers are allowed per host. The default is {@code false}.
* Using path components in the URL of the issuer identifier enables supporting multiple issuers per host in a multi-tenant hosting configuration.
* Returns {@code true} if multiple issuers are allowed per host. The default is
* {@code false}. Using path components in the URL of the issuer identifier enables
* supporting multiple issuers per host in a multi-tenant hosting configuration.
*
* <p>
* For example:
@@ -53,8 +54,8 @@ public final class AuthorizationServerSettings extends AbstractSettings {
* <li>{@code https://example.com/issuer1}</li>
* <li>{@code https://example.com/authz/issuer2}</li>
* </ul>
*
* @return {@code true} if multiple issuers are allowed per host, {@code false} otherwise
* @return {@code true} if multiple issuers are allowed per host, {@code false}
* otherwise
* @since 1.3
* @see AuthorizationServerContext#getIssuer()
*/
@@ -198,8 +199,9 @@ public final class AuthorizationServerSettings extends AbstractSettings {
}
/**
* Set to {@code true} if multiple issuers are allowed per host.
* Using path components in the URL of the issuer identifier enables supporting multiple issuers per host in a multi-tenant hosting configuration.
* Set to {@code true} if multiple issuers are allowed per host. Using path
* components in the URL of the issuer identifier enables supporting multiple
* issuers per host in a multi-tenant hosting configuration.
*
* <p>
* For example:
@@ -209,17 +211,20 @@ public final class AuthorizationServerSettings extends AbstractSettings {
* </ul>
*
* <p>
* <b>NOTE:</b> Explicitly configuring the issuer identifier via {@link #issuer(String)} forces to a single-tenant configuration.
* Avoid configuring the issuer identifier when using a multi-tenant hosting configuration,
* allowing the issuer identifier to be resolved from the <i>"current"</i> request.
*
* @param multipleIssuersAllowed {@code true} if multiple issuers are allowed per host, {@code false} otherwise
* <b>NOTE:</b> Explicitly configuring the issuer identifier via
* {@link #issuer(String)} forces to a single-tenant configuration. Avoid
* configuring the issuer identifier when using a multi-tenant hosting
* configuration, allowing the issuer identifier to be resolved from the
* <i>"current"</i> request.
* @param multipleIssuersAllowed {@code true} if multiple issuers are allowed per
* host, {@code false} otherwise
* @return the {@link Builder} for further configuration
* @since 1.3
* @see AuthorizationServerContext#getIssuer()
*/
public Builder multipleIssuersAllowed(boolean multipleIssuersAllowed) {
return setting(ConfigurationSettingNames.AuthorizationServer.MULTIPLE_ISSUERS_ALLOWED, multipleIssuersAllowed);
return setting(ConfigurationSettingNames.AuthorizationServer.MULTIPLE_ISSUERS_ALLOWED,
multipleIssuersAllowed);
}
/**
@@ -328,9 +333,10 @@ public final class AuthorizationServerSettings extends AbstractSettings {
@Override
public AuthorizationServerSettings build() {
AuthorizationServerSettings authorizationServerSettings = new AuthorizationServerSettings(getSettings());
if (authorizationServerSettings.getIssuer() != null && authorizationServerSettings.isMultipleIssuersAllowed()) {
throw new IllegalArgumentException("The issuer identifier (" + authorizationServerSettings.getIssuer() +
") cannot be set when isMultipleIssuersAllowed() is true.");
if (authorizationServerSettings.getIssuer() != null
&& authorizationServerSettings.isMultipleIssuersAllowed()) {
throw new IllegalArgumentException("The issuer identifier (" + authorizationServerSettings.getIssuer()
+ ") cannot be set when isMultipleIssuersAllowed() is true.");
}
return authorizationServerSettings;
}

View File

@@ -81,10 +81,11 @@ public final class ClientSettings extends AbstractSettings {
}
/**
* Returns the expected subject distinguished name associated to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} method.
*
* @return the expected subject distinguished name associated to the client {@code X509Certificate} received during client authentication
* Returns the expected subject distinguished name associated to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} method.
* @return the expected subject distinguished name associated to the client
* {@code X509Certificate} received during client authentication
* @since 1.3
*/
public String getX509CertificateSubjectDN() {
@@ -168,10 +169,12 @@ public final class ClientSettings extends AbstractSettings {
}
/**
* Sets the expected subject distinguished name associated to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} method.
*
* @param x509CertificateSubjectDN the expected subject distinguished name associated to the client {@code X509Certificate} received during client authentication * @return the {@link Builder} for further configuration
* Sets the expected subject distinguished name associated to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} method.
* @param x509CertificateSubjectDN the expected subject distinguished name
* associated to the client {@code X509Certificate} received during client
* authentication * @return the {@link Builder} for further configuration
* @return the {@link Builder} for further configuration
* @since 1.3
*/

View File

@@ -73,11 +73,13 @@ public final class ConfigurationSettingNames {
.concat("token-endpoint-authentication-signing-algorithm");
/**
* Set the expected subject distinguished name associated to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} method.
* Set the expected subject distinguished name associated to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} method.
* @since 1.3
*/
public static final String X509_CERTIFICATE_SUBJECT_DN = CLIENT_SETTINGS_NAMESPACE.concat("x509-certificate-subject-dn");
public static final String X509_CERTIFICATE_SUBJECT_DN = CLIENT_SETTINGS_NAMESPACE
.concat("x509-certificate-subject-dn");
private Client() {
}
@@ -101,7 +103,8 @@ public final class ConfigurationSettingNames {
* Set to {@code true} if multiple issuers are allowed per host.
* @since 1.3
*/
public static final String MULTIPLE_ISSUERS_ALLOWED = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("multiple-issuers-allowed");
public static final String MULTIPLE_ISSUERS_ALLOWED = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE
.concat("multiple-issuers-allowed");
/**
* Set the OAuth 2.0 Authorization endpoint.
@@ -221,11 +224,13 @@ public final class ConfigurationSettingNames {
.concat("id-token-signature-algorithm");
/**
* Set to {@code true} if access tokens must be bound to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
* Set to {@code true} if access tokens must be bound to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
* @since 1.3
*/
public static final String X509_CERTIFICATE_BOUND_ACCESS_TOKENS = TOKEN_SETTINGS_NAMESPACE.concat("x509-certificate-bound-access-tokens");
public static final String X509_CERTIFICATE_BOUND_ACCESS_TOKENS = TOKEN_SETTINGS_NAMESPACE
.concat("x509-certificate-bound-access-tokens");
private Token() {
}

View File

@@ -101,11 +101,12 @@ public final class TokenSettings extends AbstractSettings {
}
/**
* Returns {@code true} if access tokens must be bound to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
* The default is {@code false}.
*
* @return {@code true} if access tokens must be bound to the client {@code X509Certificate}, {@code false} otherwise
* Returns {@code true} if access tokens must be bound to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} or {@code self_signed_tls_client_auth} method. The default
* is {@code false}.
* @return {@code true} if access tokens must be bound to the client
* {@code X509Certificate}, {@code false} otherwise
* @since 1.3
*/
public boolean isX509CertificateBoundAccessTokens() {
@@ -117,15 +118,14 @@ public final class TokenSettings extends AbstractSettings {
* @return the {@link Builder}
*/
public static Builder builder() {
return new Builder()
.authorizationCodeTimeToLive(Duration.ofMinutes(5))
.accessTokenTimeToLive(Duration.ofMinutes(5))
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
.deviceCodeTimeToLive(Duration.ofMinutes(5))
.reuseRefreshTokens(true)
.refreshTokenTimeToLive(Duration.ofMinutes(60))
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
.x509CertificateBoundAccessTokens(false);
return new Builder().authorizationCodeTimeToLive(Duration.ofMinutes(5))
.accessTokenTimeToLive(Duration.ofMinutes(5))
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
.deviceCodeTimeToLive(Duration.ofMinutes(5))
.reuseRefreshTokens(true)
.refreshTokenTimeToLive(Duration.ofMinutes(60))
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
.x509CertificateBoundAccessTokens(false);
}
/**
@@ -237,15 +237,17 @@ public final class TokenSettings extends AbstractSettings {
}
/**
* Set to {@code true} if access tokens must be bound to the client {@code X509Certificate}
* received during client authentication when using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
*
* @param x509CertificateBoundAccessTokens {@code true} if access tokens must be bound to the client {@code X509Certificate}, {@code false} otherwise
* Set to {@code true} if access tokens must be bound to the client
* {@code X509Certificate} received during client authentication when using the
* {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
* @param x509CertificateBoundAccessTokens {@code true} if access tokens must be
* bound to the client {@code X509Certificate}, {@code false} otherwise
* @return the {@link Builder} for further configuration
* @since 1.3
*/
public Builder x509CertificateBoundAccessTokens(boolean x509CertificateBoundAccessTokens) {
return setting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS, x509CertificateBoundAccessTokens);
return setting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS,
x509CertificateBoundAccessTokens);
}
/**

View File

@@ -61,9 +61,12 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server";
private final RequestMatcher requestMatcher = createRequestMatcher();
private final OAuth2AuthorizationServerMetadataHttpMessageConverter authorizationServerMetadataHttpMessageConverter =
new OAuth2AuthorizationServerMetadataHttpMessageConverter();
private Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = (authorizationServerMetadata) -> {};
private final OAuth2AuthorizationServerMetadataHttpMessageConverter authorizationServerMetadataHttpMessageConverter = new OAuth2AuthorizationServerMetadataHttpMessageConverter();
private Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = (
authorizationServerMetadata) -> {
};
/**
* Sets the {@code Consumer} providing access to the
@@ -93,25 +96,26 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext
.getAuthorizationServerSettings();
OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true);
OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata
.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true);
this.authorizationServerMetadataCustomizer.accept(authorizationServerMetadata);
@@ -125,10 +129,10 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI, HttpMethod.GET.name());
final RequestMatcher multipleIssuersRequestMatcher = new AntPathRequestMatcher(
DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI + "/**", HttpMethod.GET.name());
return (request) ->
AuthorizationServerContextHolder.getContext().getAuthorizationServerSettings().isMultipleIssuersAllowed() ?
multipleIssuersRequestMatcher.matches(request) :
defaultRequestMatcher.matches(request);
return (request) -> AuthorizationServerContextHolder.getContext()
.getAuthorizationServerSettings()
.isMultipleIssuersAllowed() ? multipleIssuersRequestMatcher.matches(request)
: defaultRequestMatcher.matches(request);
}
private static Consumer<List<String>> clientAuthenticationMethods() {

View File

@@ -220,8 +220,9 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
OAuth2UserCode userCode = deviceAuthorizationRequestAuthentication.getUserCode();
// Generate the fully-qualified verification URI
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(this.verificationUri);
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder
.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(this.verificationUri);
String verificationUri = uriComponentsBuilder.build().toUriString();
// @formatter:off
String verificationUriComplete = uriComponentsBuilder

View File

@@ -104,10 +104,12 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
private final RequestMatcher tokenEndpointMatcher;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource =
new WebAuthenticationDetailsSource();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationConverter authenticationConverter;
private AuthenticationSuccessHandler authenticationSuccessHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler();
private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();
/**

View File

@@ -44,8 +44,9 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* An implementation of an {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken}
* and returning the {@link OAuth2AccessTokenResponse Access Token Response}.
* An implementation of an {@link AuthenticationSuccessHandler} used for handling an
* {@link OAuth2AccessTokenAuthenticationToken} and returning the
* {@link OAuth2AccessTokenResponse Access Token Response}.
*
* @author Dmitriy Dubson
* @since 1.3
@@ -53,9 +54,11 @@ import org.springframework.util.CollectionUtils;
* @see OAuth2AccessTokenResponseHttpMessageConverter
*/
public final class OAuth2AccessTokenResponseAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final Log logger = LogFactory.getLog(getClass());
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
private Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer;
@Override
@@ -63,11 +66,12 @@ public final class OAuth2AccessTokenResponseAuthenticationSuccessHandler impleme
Authentication authentication) throws IOException, ServletException {
if (!(authentication instanceof OAuth2AccessTokenAuthenticationToken accessTokenAuthentication)) {
if (this.logger.isErrorEnabled()) {
this.logger.error(Authentication.class.getSimpleName() + " must be of type " +
OAuth2AccessTokenAuthenticationToken.class.getName() +
" but was " + authentication.getClass().getName());
this.logger.error(Authentication.class.getSimpleName() + " must be of type "
+ OAuth2AccessTokenAuthenticationToken.class.getName() + " but was "
+ authentication.getClass().getName());
}
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "Unable to process the access token response.", null);
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"Unable to process the access token response.", null);
throw new OAuth2AuthenticationException(error);
}
@@ -75,10 +79,9 @@ public final class OAuth2AccessTokenResponseAuthenticationSuccessHandler impleme
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
OAuth2AccessTokenResponse.Builder builder =
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
.scopes(accessToken.getScopes());
OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
.scopes(accessToken.getScopes());
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
@@ -108,12 +111,15 @@ public final class OAuth2AccessTokenResponseAuthenticationSuccessHandler impleme
}
/**
* Sets the {@code Consumer} providing access to the {@link OAuth2AccessTokenAuthenticationContext}
* containing an {@link OAuth2AccessTokenResponse.Builder} and additional context information.
*
* @param accessTokenResponseCustomizer the {@code Consumer} providing access to the {@link OAuth2AccessTokenAuthenticationContext} containing an {@link OAuth2AccessTokenResponse.Builder}
* Sets the {@code Consumer} providing access to the
* {@link OAuth2AccessTokenAuthenticationContext} containing an
* {@link OAuth2AccessTokenResponse.Builder} and additional context information.
* @param accessTokenResponseCustomizer the {@code Consumer} providing access to the
* {@link OAuth2AccessTokenAuthenticationContext} containing an
* {@link OAuth2AccessTokenResponse.Builder}
*/
public void setAccessTokenResponseCustomizer(Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer) {
public void setAccessTokenResponseCustomizer(
Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer) {
Assert.notNull(accessTokenResponseCustomizer, "accessTokenResponseCustomizer cannot be null");
this.accessTokenResponseCustomizer = accessTokenResponseCustomizer;
}

View File

@@ -44,8 +44,10 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* Attempts to extract an Access Token Request from {@link HttpServletRequest} for the OAuth 2.0 Token Exchange Grant
* and then converts it to an {@link OAuth2TokenExchangeAuthenticationToken} used for authenticating the authorization grant.
* Attempts to extract an Access Token Request from {@link HttpServletRequest} for the
* OAuth 2.0 Token Exchange Grant and then converts it to an
* {@link OAuth2TokenExchangeAuthenticationToken} used for authenticating the
* authorization grant.
*
* @author Steve Riesenberg
* @since 1.3
@@ -81,9 +83,7 @@ public final class OAuth2TokenExchangeAuthenticationConverter implements Authent
if (!CollectionUtils.isEmpty(resources)) {
for (String resource : resources) {
if (!isValidUri(resource)) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.RESOURCE,
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.RESOURCE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
}
@@ -94,64 +94,52 @@ public final class OAuth2TokenExchangeAuthenticationConverter implements Authent
// scope (OPTIONAL)
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
if (StringUtils.hasText(scope) &&
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SCOPE,
if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Set<String> requestedScopes = null;
if (StringUtils.hasText(scope)) {
requestedScopes = new HashSet<>(
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
}
// requested_token_type (OPTIONAL)
String requestedTokenType = parameters.getFirst(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE);
if (StringUtils.hasText(requestedTokenType)) {
if (parameters.get(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.REQUESTED_TOKEN_TYPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
validateTokenType(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, requestedTokenType);
} else {
}
else {
requestedTokenType = ACCESS_TOKEN_TYPE_VALUE;
}
// subject_token (REQUIRED)
String subjectToken = parameters.getFirst(OAuth2ParameterNames.SUBJECT_TOKEN);
if (!StringUtils.hasText(subjectToken) ||
parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SUBJECT_TOKEN,
if (!StringUtils.hasText(subjectToken) || parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN).size() != 1) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SUBJECT_TOKEN,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
// subject_token_type (REQUIRED)
String subjectTokenType = parameters.getFirst(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE);
if (!StringUtils.hasText(subjectTokenType) ||
parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SUBJECT_TOKEN_TYPE,
if (!StringUtils.hasText(subjectTokenType)
|| parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE).size() != 1) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SUBJECT_TOKEN_TYPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
} else {
}
else {
validateTokenType(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, subjectTokenType);
}
// actor_token (OPTIONAL, REQUIRED if actor_token_type is provided)
String actorToken = parameters.getFirst(OAuth2ParameterNames.ACTOR_TOKEN);
if (StringUtils.hasText(actorToken) &&
parameters.get(OAuth2ParameterNames.ACTOR_TOKEN).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.ACTOR_TOKEN,
if (StringUtils.hasText(actorToken) && parameters.get(OAuth2ParameterNames.ACTOR_TOKEN).size() != 1) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
@@ -159,9 +147,7 @@ public final class OAuth2TokenExchangeAuthenticationConverter implements Authent
String actorTokenType = parameters.getFirst(OAuth2ParameterNames.ACTOR_TOKEN_TYPE);
if (StringUtils.hasText(actorTokenType)) {
if (parameters.get(OAuth2ParameterNames.ACTOR_TOKEN_TYPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.ACTOR_TOKEN_TYPE,
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN_TYPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
@@ -169,28 +155,23 @@ public final class OAuth2TokenExchangeAuthenticationConverter implements Authent
}
if (!StringUtils.hasText(actorToken) && StringUtils.hasText(actorTokenType)) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.ACTOR_TOKEN,
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
} else if (StringUtils.hasText(actorToken) && !StringUtils.hasText(actorTokenType)) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.ACTOR_TOKEN_TYPE,
}
else if (StringUtils.hasText(actorToken) && !StringUtils.hasText(actorTokenType)) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN_TYPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Map<String, Object> additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) &&
!key.equals(OAuth2ParameterNames.RESOURCE) &&
!key.equals(OAuth2ParameterNames.AUDIENCE) &&
!key.equals(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE) &&
!key.equals(OAuth2ParameterNames.SUBJECT_TOKEN) &&
!key.equals(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE) &&
!key.equals(OAuth2ParameterNames.ACTOR_TOKEN) &&
!key.equals(OAuth2ParameterNames.ACTOR_TOKEN_TYPE) &&
!key.equals(OAuth2ParameterNames.SCOPE)) {
if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.RESOURCE)
&& !key.equals(OAuth2ParameterNames.AUDIENCE)
&& !key.equals(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE)
&& !key.equals(OAuth2ParameterNames.SUBJECT_TOKEN)
&& !key.equals(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)
&& !key.equals(OAuth2ParameterNames.ACTOR_TOKEN)
&& !key.equals(OAuth2ParameterNames.ACTOR_TOKEN_TYPE) && !key.equals(OAuth2ParameterNames.SCOPE)) {
additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));
}
});
@@ -219,7 +200,8 @@ public final class OAuth2TokenExchangeAuthenticationConverter implements Authent
try {
URI validUri = new URI(uri);
return validUri.isAbsolute() && validUri.getFragment() == null;
} catch (URISyntaxException ex) {
}
catch (URISyntaxException ex) {
return false;
}
}

View File

@@ -33,9 +33,10 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* Attempts to extract a client {@code X509Certificate} chain from {@link HttpServletRequest}
* and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the client
* using the {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
* Attempts to extract a client {@code X509Certificate} chain from
* {@link HttpServletRequest} and then converts to an
* {@link OAuth2ClientAuthenticationToken} used for authenticating the client using the
* {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.
*
* @author Joe Grandja
* @since 1.3
@@ -48,8 +49,8 @@ public final class X509ClientCertificateAuthenticationConverter implements Authe
@Nullable
@Override
public Authentication convert(HttpServletRequest request) {
X509Certificate[] clientCertificateChain =
(X509Certificate[]) request.getAttribute("jakarta.servlet.request.X509Certificate");
X509Certificate[] clientCertificateChain = (X509Certificate[]) request
.getAttribute("jakarta.servlet.request.X509Certificate");
if (clientCertificateChain == null || clientCertificateChain.length == 0) {
return null;
}
@@ -58,21 +59,18 @@ public final class X509ClientCertificateAuthenticationConverter implements Authe
// client_id (REQUIRED)
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
if (!StringUtils.hasText(clientId) ||
parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
}
Map<String, Object> additionalParameters = OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(
request, OAuth2ParameterNames.CLIENT_ID);
Map<String, Object> additionalParameters = OAuth2EndpointUtils
.getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ID);
ClientAuthenticationMethod clientAuthenticationMethod =
clientCertificateChain.length == 1 ?
ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH :
ClientAuthenticationMethod.TLS_CLIENT_AUTH;
ClientAuthenticationMethod clientAuthenticationMethod = clientCertificateChain.length == 1
? ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH : ClientAuthenticationMethod.TLS_CLIENT_AUTH;
return new OAuth2ClientAuthenticationToken(clientId, clientAuthenticationMethod,
clientCertificateChain, additionalParameters);
return new OAuth2ClientAuthenticationToken(clientId, clientAuthenticationMethod, clientCertificateChain,
additionalParameters);
}
}

View File

@@ -48,23 +48,23 @@ public class OAuth2AuthorizationServerMetadataTests {
@Test
public void buildWhenAllClaimsProvidedThenCreated() {
OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()
.issuer("https://example.com")
.authorizationEndpoint("https://example.com/oauth2/authorize")
.tokenEndpoint("https://example.com/oauth2/token")
.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.jwkSetUrl("https://example.com/oauth2/jwks")
.scope("openid")
.responseType("code")
.grantType("authorization_code")
.grantType("client_credentials")
.tokenRevocationEndpoint("https://example.com/oauth2/revoke")
.tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.tokenIntrospectionEndpoint("https://example.com/oauth2/introspect")
.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true)
.claim("a-claim", "a-value")
.build();
.issuer("https://example.com")
.authorizationEndpoint("https://example.com/oauth2/authorize")
.tokenEndpoint("https://example.com/oauth2/token")
.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.jwkSetUrl("https://example.com/oauth2/jwks")
.scope("openid")
.responseType("code")
.grantType("authorization_code")
.grantType("client_credentials")
.tokenRevocationEndpoint("https://example.com/oauth2/revoke")
.tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.tokenIntrospectionEndpoint("https://example.com/oauth2/introspect")
.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.codeChallengeMethod("S256")
.tlsClientCertificateBoundAccessTokens(true)
.claim("a-claim", "a-value")
.build();
assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com"));
assertThat(authorizationServerMetadata.getAuthorizationEndpoint())

View File

@@ -92,8 +92,8 @@ public class TestOAuth2Authorizations {
.attribute(Principal.class.getName(),
new TestingAuthenticationToken("principal", null, "ROLE_A", "ROLE_B"));
if (accessToken != null) {
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(
"refresh-token", Instant.now(), Instant.now().plus(1, ChronoUnit.HOURS));
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now(),
Instant.now().plus(1, ChronoUnit.HOURS));
builder
.token(accessToken, (metadata) -> metadata.putAll(tokenMetadata(registeredClient, accessTokenClaims)))
.refreshToken(refreshToken);
@@ -102,7 +102,8 @@ public class TestOAuth2Authorizations {
return builder;
}
private static Map<String, Object> tokenMetadata(RegisteredClient registeredClient, Map<String, Object> tokenClaims) {
private static Map<String, Object> tokenMetadata(RegisteredClient registeredClient,
Map<String, Object> tokenClaims) {
Map<String, Object> tokenMetadata = new HashMap<>();
OAuth2TokenFormat accessTokenFormat = registeredClient.getTokenSettings().getAccessTokenFormat();
tokenMetadata.put(OAuth2TokenFormat.class.getName(), accessTokenFormat.getValue());

View File

@@ -34,39 +34,43 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
* @author Dmitriy Dubson
*/
public class OAuth2AccessTokenAuthenticationContextTests {
private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build();
private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());
private final OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken =
new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal,
this.authorization.getAccessToken().getToken(), this.authorization.getRefreshToken().getToken());
private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient)
.build();
private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());
private final OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken = new OAuth2AccessTokenAuthenticationToken(
this.registeredClient, this.clientPrincipal, this.authorization.getAccessToken().getToken(),
this.authorization.getRefreshToken().getToken());
@Test
public void withWhenAuthenticationNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2AccessTokenAuthenticationContext.with(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authentication cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authentication cannot be null");
}
@Test
public void setWhenValueNullThenThrowIllegalArgumentException() {
OAuth2AccessTokenAuthenticationContext.Builder builder =
OAuth2AccessTokenAuthenticationContext.with(this.accessTokenAuthenticationToken);
OAuth2AccessTokenAuthenticationContext.Builder builder = OAuth2AccessTokenAuthenticationContext
.with(this.accessTokenAuthenticationToken);
assertThatThrownBy(() -> builder.accessTokenResponse(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("value cannot be null");
assertThatThrownBy(() -> builder.accessTokenResponse(null)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("value cannot be null");
}
@Test
public void buildWhenAllValuesProvidedThenAllValuesAreSet() {
OAuth2AccessTokenResponse.Builder accessTokenResponseBuilder =
OAuth2AccessTokenResponse.withToken(this.accessTokenAuthenticationToken.getAccessToken().getTokenValue());
OAuth2AccessTokenAuthenticationContext context =
OAuth2AccessTokenAuthenticationContext.with(this.accessTokenAuthenticationToken)
.accessTokenResponse(accessTokenResponseBuilder)
.build();
OAuth2AccessTokenResponse.Builder accessTokenResponseBuilder = OAuth2AccessTokenResponse
.withToken(this.accessTokenAuthenticationToken.getAccessToken().getTokenValue());
OAuth2AccessTokenAuthenticationContext context = OAuth2AccessTokenAuthenticationContext
.with(this.accessTokenAuthenticationToken)
.accessTokenResponse(accessTokenResponseBuilder)
.build();
assertThat(context.<Authentication>getAuthentication()).isEqualTo(this.accessTokenAuthenticationToken);
assertThat(context.getAccessTokenResponse()).isEqualTo(accessTokenResponseBuilder);

View File

@@ -146,8 +146,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
@Test
public void setAuthorizationConsentRequiredWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationConsentRequired(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authorizationConsentRequired cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authorizationConsentRequired cannot be null");
}
@Test
@@ -486,23 +486,24 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
@Test
public void authenticateWhenCustomAuthorizationConsentRequiredThenUsed() {
@SuppressWarnings("unchecked")
Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired = mock(Predicate.class);
Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired = mock(
Predicate.class);
this.authenticationProvider.setAuthorizationConsentRequired(authorizationConsentRequired);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
new OAuth2AuthorizationCodeRequestAuthenticationToken(
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
redirectUri, STATE, registeredClient.getScopes(), null);
OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE,
registeredClient.getScopes(), null);
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult);
assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,
authenticationResult);
verify(authorizationConsentRequired).test(any());
}

View File

@@ -145,8 +145,8 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authenticationValidator cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authenticationValidator cannot be null");
}
@Test
@@ -325,10 +325,10 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void authenticateWhenCustomAuthenticationValidatorThenUsed() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2ClientCredentialsAuthenticationToken authentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, registeredClient.getScopes(), null);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(
clientPrincipal, registeredClient.getScopes(), null);
@SuppressWarnings("unchecked")
Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = mock(Consumer.class);

View File

@@ -70,15 +70,24 @@ import static org.mockito.Mockito.when;
* @author Steve Riesenberg
*/
public class OAuth2TokenExchangeAuthenticationProviderTests {
private static final Set<String> RESOURCES = Set.of("https://mydomain.com/resource1", "https://mydomain.com/resource2");
private static final Set<String> RESOURCES = Set.of("https://mydomain.com/resource1",
"https://mydomain.com/resource2");
private static final Set<String> AUDIENCES = Set.of("audience1", "audience2");
private static final String SUBJECT_TOKEN = "EfYu_0jEL";
private static final String ACTOR_TOKEN = "JlNE_xR1f";
private static final String ACCESS_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:access_token";
private static final String JWT_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:jwt";
private OAuth2AuthorizationService authorizationService;
private OAuth2TokenGenerator<OAuth2Token> tokenGenerator;
private OAuth2TokenExchangeAuthenticationProvider authenticationProvider;
@BeforeEach
@@ -168,7 +177,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenSubjectTokenNotFoundThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(null);
// @formatter:off
@@ -187,10 +197,12 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenSubjectTokenNotActiveThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createExpiredAccessToken(SUBJECT_TOKEN)).build();
.token(createExpiredAccessToken(SUBJECT_TOKEN))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
@@ -208,10 +220,12 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenSubjectTokenTypeJwtAndSubjectTokenFormatReferenceThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createJwtRequest(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE)).build();
.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
@@ -229,7 +243,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenSubjectPrincipalNullThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
// @formatter:off
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
@@ -254,12 +269,14 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenActorTokenNotFoundThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, (OAuth2Authorization) null);
.token(createAccessToken(SUBJECT_TOKEN))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, (OAuth2Authorization) null);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -277,14 +294,17 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenActorTokenNotActiveThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN)).build();
.token(createAccessToken(SUBJECT_TOKEN))
.build();
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createExpiredAccessToken(ACTOR_TOKEN)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createExpiredAccessToken(ACTOR_TOKEN))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -302,14 +322,17 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenActorTokenTypeJwtAndActorTokenFormatReferenceThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createJwtRequest(registeredClient);
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)).build();
.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.SELF_CONTAINED))
.build();
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(ACTOR_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createAccessToken(ACTOR_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -327,7 +350,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenMayActAndActorIssClaimNotAuthorizedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
Map<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, "issuer",
OAuth2TokenClaimNames.SUB, "actor");
@@ -339,9 +363,10 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
Map<String, Object> actorTokenClaims = Map.of(OAuth2TokenClaimNames.ISS, "invalid-issuer",
OAuth2TokenClaimNames.SUB, "actor");
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -359,7 +384,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenMayActAndActorSubClaimNotAuthorizedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
Map<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, "issuer",
OAuth2TokenClaimNames.SUB, "actor");
@@ -371,9 +397,10 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
Map<String, Object> actorTokenClaims = Map.of(OAuth2TokenClaimNames.ISS, "issuer", OAuth2TokenClaimNames.SUB,
"invalid-actor");
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -391,7 +418,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenMayActAndImpersonationThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createImpersonationRequest(registeredClient);
Map<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, "issuer",
OAuth2TokenClaimNames.SUB, "actor");
@@ -400,8 +428,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of("may_act", authorizedActorClaims)))
.build();
// @formatter:on
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization);
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -418,15 +446,18 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenInvalidScopeInRequestThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient,
Set.of("invalid"));
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN)).build();
.token(createAccessToken(SUBJECT_TOKEN))
.build();
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(ACTOR_TOKEN)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createAccessToken(ACTOR_TOKEN))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -444,14 +475,18 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenInvalidScopeInSubjectAuthorizationThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient, Set.of());
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN)).authorizedScopes(Set.of("invalid")).build();
.token(createAccessToken(SUBJECT_TOKEN))
.authorizedScopes(Set.of("invalid"))
.build();
OAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(ACTOR_TOKEN)).build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
.token(createAccessToken(ACTOR_TOKEN))
.build();
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
// @formatter:off
assertThatExceptionOfType(OAuth2AuthenticationException.class)
.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -469,7 +504,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenNoActorTokenAndValidTokenExchangeThenReturnAccessTokenForImpersonation() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createImpersonationRequest(registeredClient);
TestingAuthenticationToken userPrincipal = new TestingAuthenticationToken("user", null, "ROLE_USER");
// @formatter:off
@@ -478,19 +514,19 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
.attribute(Principal.class.getName(), userPrincipal)
.build();
// @formatter:on
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization);
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization);
OAuth2AccessToken accessToken = createAccessToken("token-value");
when(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).thenReturn(accessToken);
OAuth2AccessTokenAuthenticationToken authenticationResult =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());
assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);
assertThat(authenticationResult.getRefreshToken()).isNull();
assertThat(authenticationResult.getAdditionalParameters()).hasSize(1);
assertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
ArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);
@@ -521,32 +557,33 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenNoActorTokenAndPreviousActorThenReturnAccessTokenForImpersonation() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createImpersonationRequest(registeredClient);
TestingAuthenticationToken userPrincipal = new TestingAuthenticationToken("user", null, "ROLE_USER");
OAuth2TokenExchangeActor previousActor = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS, "issuer1",
OAuth2TokenClaimNames.SUB, "actor"));
OAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal =
new OAuth2TokenExchangeCompositeAuthenticationToken(userPrincipal, List.of(previousActor));
OAuth2TokenExchangeActor previousActor = new OAuth2TokenExchangeActor(
Map.of(OAuth2TokenClaimNames.ISS, "issuer1", OAuth2TokenClaimNames.SUB, "actor"));
OAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal = new OAuth2TokenExchangeCompositeAuthenticationToken(
userPrincipal, List.of(previousActor));
// @formatter:off
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN))
.attribute(Principal.class.getName(), subjectPrincipal)
.build();
// @formatter:on
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization);
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization);
OAuth2AccessToken accessToken = createAccessToken("token-value");
when(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).thenReturn(accessToken);
OAuth2AccessTokenAuthenticationToken authenticationResult =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());
assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);
assertThat(authenticationResult.getRefreshToken()).isNull();
assertThat(authenticationResult.getAdditionalParameters()).hasSize(1);
assertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
ArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);
@@ -577,15 +614,16 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
@Test
public void authenticateWhenActorTokenAndValidTokenExchangeThenReturnAccessTokenForDelegation() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
OAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);
TestingAuthenticationToken userPrincipal = new TestingAuthenticationToken("user", null, "ROLE_USER");
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS, "issuer1",
OAuth2TokenClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS, "issuer2",
OAuth2TokenClaimNames.SUB, "actor2"));
OAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal =
new OAuth2TokenExchangeCompositeAuthenticationToken(userPrincipal, List.of(actor1));
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(
Map.of(OAuth2TokenClaimNames.ISS, "issuer1", OAuth2TokenClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(
Map.of(OAuth2TokenClaimNames.ISS, "issuer2", OAuth2TokenClaimNames.SUB, "actor2"));
OAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal = new OAuth2TokenExchangeCompositeAuthenticationToken(
userPrincipal, List.of(actor1));
// @formatter:off
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of("may_act", actor2.getClaims())))
@@ -596,19 +634,19 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
.token(createAccessToken(ACTOR_TOKEN), withClaims(actor2.getClaims()))
.build();
// @formatter:on
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(
subjectAuthorization, actorAuthorization);
when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))
.thenReturn(subjectAuthorization, actorAuthorization);
OAuth2AccessToken accessToken = createAccessToken("token-value");
when(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).thenReturn(accessToken);
OAuth2AccessTokenAuthenticationToken authenticationResult =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());
assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);
assertThat(authenticationResult.getRefreshToken()).isNull();
assertThat(authenticationResult.getAdditionalParameters()).hasSize(1);
assertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
.isEqualTo(JWT_TOKEN_TYPE_VALUE);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
ArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);
@@ -638,8 +676,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
assertThat(authorization.getAccessToken().getToken()).isEqualTo(accessToken);
assertThat(authorization.getRefreshToken()).isNull();
OAuth2TokenExchangeCompositeAuthenticationToken authorizationPrincipal =
authorization.getAttribute(Principal.class.getName());
OAuth2TokenExchangeCompositeAuthenticationToken authorizationPrincipal = authorization
.getAttribute(Principal.class.getName());
assertThat(authorizationPrincipal).isNotNull();
assertThat(authorizationPrincipal.getSubject()).isSameAs(subjectPrincipal.getSubject());
assertThat(authorizationPrincipal.getActors()).containsExactly(actor2, actor1);
@@ -664,7 +702,8 @@ public class OAuth2TokenExchangeAuthenticationProviderTests {
clientPrincipal, ACTOR_TOKEN, ACCESS_TOKEN_TYPE_VALUE, RESOURCES, AUDIENCES, requestedScopes, null);
}
private static OAuth2TokenExchangeAuthenticationToken createImpersonationRequest(RegisteredClient registeredClient) {
private static OAuth2TokenExchangeAuthenticationToken createImpersonationRequest(
RegisteredClient registeredClient) {
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);
return new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE,

View File

@@ -35,17 +35,27 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
* @author Steve Riesenberg
*/
public class OAuth2TokenExchangeAuthenticationTokenTests {
private static final Set<String> RESOURCES = Set.of("https://mydomain.com/resource1", "https://mydomain.com/resource2");
private static final Set<String> RESOURCES = Set.of("https://mydomain.com/resource1",
"https://mydomain.com/resource2");
private static final Set<String> AUDIENCES = Set.of("audience1", "audience2");
private static final String SUBJECT_TOKEN = "EfYu_0jEL";
private static final String ACTOR_TOKEN = "JlNE_xR1f";
private static final String ACCESS_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:access_token";
private static final String JWT_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:jwt";
private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());
private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());
private Set<String> scopes = Collections.singleton("scope1");
private Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
@Test

View File

@@ -57,8 +57,8 @@ public class OAuth2TokenExchangeCompositeAuthenticationTokenTests {
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of("claim1", "value1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of("claim2", "value2"));
List<OAuth2TokenExchangeActor> actors = List.of(actor1, actor2);
OAuth2TokenExchangeCompositeAuthenticationToken authentication =
new OAuth2TokenExchangeCompositeAuthenticationToken(subject, actors);
OAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(
subject, actors);
assertThat(authentication.getSubject()).isEqualTo(subject);
assertThat(authentication.getActors()).isEqualTo(actors);
}

View File

@@ -65,18 +65,27 @@ import static org.mockito.Mockito.when;
* @author Joe Grandja
*/
public class X509ClientCertificateAuthenticationProviderTests {
// See RFC 7636: Appendix B. Example for the S256 code_challenge_method
// See RFC 7636: Appendix B. Example for the S256 code_challenge_method
// https://tools.ietf.org/html/rfc7636#appendix-B
private static final String S256_CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
private static final String S256_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM";
private static final String AUTHORIZATION_CODE = "code";
private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);
private JWKSet selfSignedCertificateJwkSet;
private MockWebServer server;
private String clientJwkSetUrl;
private RegisteredClientRepository registeredClientRepository;
private OAuth2AuthorizationService authorizationService;
private X509ClientCertificateAuthenticationProvider authenticationProvider;
@BeforeEach
@@ -102,8 +111,8 @@ public class X509ClientCertificateAuthenticationProviderTests {
this.registeredClientRepository = mock(RegisteredClientRepository.class);
this.authorizationService = mock(OAuth2AuthorizationService.class);
this.authenticationProvider = new X509ClientCertificateAuthenticationProvider(
this.registeredClientRepository, this.authorizationService);
this.authenticationProvider = new X509ClientCertificateAuthenticationProvider(this.registeredClientRepository,
this.authorizationService);
}
@AfterEach
@@ -114,22 +123,22 @@ public class X509ClientCertificateAuthenticationProviderTests {
@Test
public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new X509ClientCertificateAuthenticationProvider(null, this.authorizationService))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("registeredClientRepository cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("registeredClientRepository cannot be null");
}
@Test
public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new X509ClientCertificateAuthenticationProvider(this.registeredClientRepository, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authorizationService cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authorizationService cannot be null");
}
@Test
public void setCertificateVerifierWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.authenticationProvider.setCertificateVerifier(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("certificateVerifier cannot be null");
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("certificateVerifier cannot be null");
}
@Test
@@ -145,36 +154,36 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId() + "-invalid", ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);
});
}
@Test
public void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("authentication_method");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("authentication_method");
});
}
@Test
@@ -185,17 +194,17 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH, null, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("credentials");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("credentials");
});
}
@Test
@@ -211,18 +220,18 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("x509_certificate_subject_dn");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("x509_certificate_subject_dn");
});
}
@Test
@@ -238,20 +247,21 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);
OAuth2ClientAuthenticationToken authenticationResult =
(OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isTrue();
assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());
assertThat(authenticationResult.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
assertThat(authenticationResult.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
}
@Test
@@ -267,18 +277,19 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
// PKI Certificate will have different issuer
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null); // PKI Certificate will have different issuer
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("x509_certificate_issuer");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("x509_certificate_issuer");
});
}
@Test
@@ -289,18 +300,18 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("client_jwk_set_url");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("client_jwk_set_url");
});
}
@Test
@@ -316,18 +327,18 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("jwk_set_uri");
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains("jwk_set_uri");
});
}
@Test
@@ -352,7 +363,8 @@ public class X509ClientCertificateAuthenticationProviderTests {
}
@Test
public void authenticateWhenSelfSignedX509CertificateJwkSetResponseNoMatchingKeysThenThrowOAuth2AuthenticationException() throws Exception {
public void authenticateWhenSelfSignedX509CertificateJwkSetResponseNoMatchingKeysThenThrowOAuth2AuthenticationException()
throws Exception {
// @formatter:off
X509Certificate pkiCertificate = TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0];
RSAKey pkiRSAKey = new RSAKey.Builder((RSAPublicKey) pkiCertificate.getPublicKey())
@@ -396,18 +408,18 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains(expectedErrorDescription);
});
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.satisfies(error -> {
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
assertThat(error.getDescription()).contains(expectedErrorDescription);
});
}
@Test
@@ -423,20 +435,22 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);
OAuth2ClientAuthenticationToken authenticationResult =
(OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isTrue();
assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());
assertThat(authenticationResult.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);
assertThat(authenticationResult.getCredentials())
.isEqualTo(TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);
assertThat(authenticationResult.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);
}
@Test
@@ -452,13 +466,13 @@ public class X509ClientCertificateAuthenticationProviderTests {
.build();
// @formatter:on
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
.thenReturn(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations
.authorization(registeredClient, createPkceAuthorizationParametersS256())
.build();
.authorization(registeredClient, createPkceAuthorizationParametersS256())
.build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))
.thenReturn(authorization);
.thenReturn(authorization);
Map<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);
@@ -466,15 +480,16 @@ public class X509ClientCertificateAuthenticationProviderTests {
registeredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, parameters);
OAuth2ClientAuthenticationToken authenticationResult =
(OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication);
OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider
.authenticate(authentication);
verify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE));
assertThat(authenticationResult.isAuthenticated()).isTrue();
assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());
assertThat(authenticationResult.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
assertThat(authenticationResult.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
assertThat(authenticationResult.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
}
private static Map<String, Object> createPkceAuthorizationParametersS256() {

View File

@@ -37,10 +37,15 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Joe Grandja
*/
class AuthorizationServerContextFilterTests {
private static final String SCHEME = "https";
private static final String HOST = "example.com";
private static final int PORT = 8443;
private static final String DEFAULT_ISSUER = SCHEME + "://" + HOST + ":" + PORT;
private AuthorizationServerContextFilter filter;
@Test
@@ -60,17 +65,17 @@ class AuthorizationServerContextFilterTests {
@Test
public void doFilterWhenCustomEndpointsThenIssuerResolved() throws Exception {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()
.authorizationEndpoint("/oauth2/v1/authorize")
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
.tokenEndpoint("/oauth2/v1/token")
.jwkSetEndpoint("/oauth2/v1/jwks")
.tokenRevocationEndpoint("/oauth2/v1/revoke")
.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
.oidcClientRegistrationEndpoint("/connect/v1/register")
.oidcUserInfoEndpoint("/v1/userinfo")
.oidcLogoutEndpoint("/connect/v1/logout")
.build();
.authorizationEndpoint("/oauth2/v1/authorize")
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
.tokenEndpoint("/oauth2/v1/token")
.jwkSetEndpoint("/oauth2/v1/jwks")
.tokenRevocationEndpoint("/oauth2/v1/revoke")
.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
.oidcClientRegistrationEndpoint("/connect/v1/register")
.oidcUserInfoEndpoint("/v1/userinfo")
.oidcLogoutEndpoint("/connect/v1/logout")
.build();
this.filter = new AuthorizationServerContextFilter(authorizationServerSettings);
String issuerPath = "/issuer2";
@@ -101,8 +106,8 @@ class AuthorizationServerContextFilterTests {
MockHttpServletResponse response = new MockHttpServletResponse();
AtomicReference<String> resolvedIssuer = new AtomicReference<>();
FilterChain filterChain = (req, resp) ->
resolvedIssuer.set(AuthorizationServerContextHolder.getContext().getIssuer());
FilterChain filterChain = (req, resp) -> resolvedIssuer
.set(AuthorizationServerContextHolder.getContext().getIssuer());
this.filter.doFilter(request, response, filterChain);

View File

@@ -55,9 +55,13 @@ import static org.mockito.Mockito.when;
* @author Joe Grandja
*/
class DefaultOAuth2TokenCustomizersTests {
private static final String ISSUER_1 = "issuer-1";
private static final String ISSUER_2 = "issuer-2";
private JwsHeader.Builder jwsHeaderBuilder;
private JwtClaimsSet.Builder jwtClaimsBuilder;
@BeforeEach
@@ -131,10 +135,10 @@ class DefaultOAuth2TokenCustomizersTests {
when(tokenExchangeAuthentication.getAudiences()).thenReturn(Collections.emptySet());
Authentication subject = new TestingAuthenticationToken("subject", null);
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of(JwtClaimNames.ISS, ISSUER_1,
JwtClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of(JwtClaimNames.ISS, ISSUER_2,
JwtClaimNames.SUB, "actor2"));
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(
Map.of(JwtClaimNames.ISS, ISSUER_1, JwtClaimNames.SUB, "actor1"));
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(
Map.of(JwtClaimNames.ISS, ISSUER_2, JwtClaimNames.SUB, "actor2"));
OAuth2TokenExchangeCompositeAuthenticationToken principal = new OAuth2TokenExchangeCompositeAuthenticationToken(
subject, List.of(actor1, actor2));
@@ -177,11 +181,10 @@ class DefaultOAuth2TokenCustomizersTests {
)
.build();
// @formatter:on
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
ClientAuthenticationMethod.TLS_CLIENT_AUTH, TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken(
clientPrincipal, null, null);
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
@@ -215,11 +218,11 @@ class DefaultOAuth2TokenCustomizersTests {
)
.build();
// @formatter:on
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,
TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken(
clientPrincipal, null, null);
// @formatter:off
JwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)
.tokenType(OAuth2TokenType.ACCESS_TOKEN)

View File

@@ -82,13 +82,13 @@ public class JwkSetTests {
public static void init() {
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.HSQL)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
db = new EmbeddedDatabaseBuilder().generateUniqueName(true)
.setType(EmbeddedDatabaseType.HSQL)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript(
"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
}
@AfterEach
@@ -188,7 +188,10 @@ public class JwkSetTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().jwkSetEndpoint("/test/jwks").multipleIssuersAllowed(true).build();
return AuthorizationServerSettings.builder()
.jwkSetEndpoint("/test/jwks")
.multipleIssuersAllowed(true)
.build();
}
}

View File

@@ -918,30 +918,33 @@ public class OAuth2AuthorizationCodeGrantTests {
String issuer = "https://example.com:8443/issuer1";
MvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))
.queryParams(getAuthorizationRequestParameters(registeredClient))
.queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE)
.queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
MvcResult mvcResult = this.mvc
.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))
.queryParams(getAuthorizationRequestParameters(registeredClient))
.queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE)
.queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
String authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), "code");
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,
AUTHORIZATION_CODE_TOKEN_TYPE);
this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
this.mvc
.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER))
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").doesNotExist())
.andExpect(jsonPath("$.scope").isNotEmpty())
.andReturn();
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").doesNotExist())
.andExpect(jsonPath("$.scope").isNotEmpty())
.andReturn();
ArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);
verify(tokenGenerator).generate(tokenContextCaptor.capture());
@@ -1333,7 +1336,8 @@ public class OAuth2AuthorizationCodeGrantTests {
@EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class)
static class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfigurationWithTokenGenerator {
static class AuthorizationServerConfigurationWithMultipleIssuersAllowed
extends AuthorizationServerConfigurationWithTokenGenerator {
@Bean
AuthorizationServerSettings authorizationServerSettings() {

View File

@@ -110,13 +110,14 @@ public class OAuth2AuthorizationServerMetadataTests {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(ISSUER))
.andReturn();
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(ISSUER))
.andReturn();
}
@Test
public void requestWhenAuthorizationServerMetadataRequestIncludesIssuerPathThenMetadataResponseHasIssuerPath() throws Exception {
public void requestWhenAuthorizationServerMetadataRequestIncludesIssuerPathThenMetadataResponseHasIssuerPath()
throws Exception {
this.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();
String host = "https://example.com:8443";
@@ -124,23 +125,23 @@ public class OAuth2AuthorizationServerMetadataTests {
String issuerPath = "/issuer1";
String issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
issuerPath = "/path1/issuer2";
issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
issuerPath = "/path1/path2/issuer3";
issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
}
// gh-616
@@ -150,9 +151,9 @@ public class OAuth2AuthorizationServerMetadataTests {
this.spring.register(AuthorizationServerConfigurationWithMetadataCustomizer.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems("scope1", "scope2")));
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems("scope1", "scope2")));
}
@EnableWebSecurity

View File

@@ -298,14 +298,14 @@ public class OAuth2ClientCredentialsGrantTests {
// @formatter:on
this.registeredClientRepository.save(registeredClient);
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
.with(x509(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE))
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
this.mvc
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).with(x509(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE))
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
verify(jwtCustomizer).customize(any());
}
@@ -344,13 +344,12 @@ public class OAuth2ClientCredentialsGrantTests {
.forClass(List.class);
verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());
List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();
assertThat(authenticationConverters).allMatch((converter) ->
converter == authenticationConverter ||
converter instanceof OAuth2AuthorizationCodeAuthenticationConverter ||
converter instanceof OAuth2RefreshTokenAuthenticationConverter ||
converter instanceof OAuth2ClientCredentialsAuthenticationConverter ||
converter instanceof OAuth2DeviceCodeAuthenticationConverter ||
converter instanceof OAuth2TokenExchangeAuthenticationConverter);
assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter
|| converter instanceof OAuth2AuthorizationCodeAuthenticationConverter
|| converter instanceof OAuth2RefreshTokenAuthenticationConverter
|| converter instanceof OAuth2ClientCredentialsAuthenticationConverter
|| converter instanceof OAuth2DeviceCodeAuthenticationConverter
|| converter instanceof OAuth2TokenExchangeAuthenticationConverter);
verify(authenticationProvider).authenticate(eq(clientCredentialsAuthentication));
@@ -359,13 +358,12 @@ public class OAuth2ClientCredentialsGrantTests {
.forClass(List.class);
verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());
List<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();
assertThat(authenticationProviders).allMatch((provider) ->
provider == authenticationProvider ||
provider instanceof OAuth2AuthorizationCodeAuthenticationProvider ||
provider instanceof OAuth2RefreshTokenAuthenticationProvider ||
provider instanceof OAuth2ClientCredentialsAuthenticationProvider ||
provider instanceof OAuth2DeviceCodeAuthenticationProvider ||
provider instanceof OAuth2TokenExchangeAuthenticationProvider);
assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider
|| provider instanceof OAuth2AuthorizationCodeAuthenticationProvider
|| provider instanceof OAuth2RefreshTokenAuthenticationProvider
|| provider instanceof OAuth2ClientCredentialsAuthenticationProvider
|| provider instanceof OAuth2DeviceCodeAuthenticationProvider
|| provider instanceof OAuth2TokenExchangeAuthenticationProvider);
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(accessTokenAuthentication));
}
@@ -395,13 +393,12 @@ public class OAuth2ClientCredentialsGrantTests {
.forClass(List.class);
verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());
List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();
assertThat(authenticationConverters).allMatch((converter) ->
converter == authenticationConverter ||
converter instanceof JwtClientAssertionAuthenticationConverter ||
converter instanceof X509ClientCertificateAuthenticationConverter ||
converter instanceof ClientSecretBasicAuthenticationConverter ||
converter instanceof ClientSecretPostAuthenticationConverter ||
converter instanceof PublicClientAuthenticationConverter);
assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter
|| converter instanceof JwtClientAssertionAuthenticationConverter
|| converter instanceof X509ClientCertificateAuthenticationConverter
|| converter instanceof ClientSecretBasicAuthenticationConverter
|| converter instanceof ClientSecretPostAuthenticationConverter
|| converter instanceof PublicClientAuthenticationConverter);
verify(authenticationProvider).authenticate(eq(clientPrincipal));
@@ -410,12 +407,11 @@ public class OAuth2ClientCredentialsGrantTests {
.forClass(List.class);
verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());
List<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();
assertThat(authenticationProviders).allMatch((provider) ->
provider == authenticationProvider ||
provider instanceof JwtClientAssertionAuthenticationProvider ||
provider instanceof X509ClientCertificateAuthenticationProvider ||
provider instanceof ClientSecretAuthenticationProvider ||
provider instanceof PublicClientAuthenticationProvider);
assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider
|| provider instanceof JwtClientAssertionAuthenticationProvider
|| provider instanceof X509ClientCertificateAuthenticationProvider
|| provider instanceof ClientSecretAuthenticationProvider
|| provider instanceof PublicClientAuthenticationProvider);
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(clientPrincipal));
}
@@ -429,14 +425,15 @@ public class OAuth2ClientCredentialsGrantTests {
String issuer = "https://example.com:8443/issuer1";
this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
this.mvc
.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION,
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
ArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);
verify(jwtCustomizer).customize(jwtEncodingContextCaptor.capture());

View File

@@ -247,9 +247,9 @@ public class OAuth2DeviceCodeGrantTests {
String userCode = deviceAuthorizationResponse.getUserCode().getTokenValue();
assertThat(userCode).matches("[A-Z]{4}-[A-Z]{4}");
assertThat(deviceAuthorizationResponse.getVerificationUri())
.isEqualTo("https://example.com:8443/oauth2/device_verification");
.isEqualTo("https://example.com:8443/oauth2/device_verification");
assertThat(deviceAuthorizationResponse.getVerificationUriComplete())
.isEqualTo("https://example.com:8443/oauth2/device_verification?user_code=" + userCode);
.isEqualTo("https://example.com:8443/oauth2/device_verification?user_code=" + userCode);
String deviceCode = deviceAuthorizationResponse.getDeviceCode().getTokenValue();
OAuth2Authorization authorization = this.authorizationService.findByToken(deviceCode, DEVICE_CODE_TOKEN_TYPE);

View File

@@ -94,18 +94,24 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/
@ExtendWith(SpringTestContextExtension.class)
public class OAuth2TokenExchangeGrantTests {
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token";
private static final String RESOURCE = "https://mydomain.com/resource";
private static final String AUDIENCE = "audience";
private static final String SUBJECT_TOKEN = "EfYu_0jEL";
private static final String ACTOR_TOKEN = "JlNE_xR1f";
private static final String ACCESS_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:access_token";
private static final String JWT_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:jwt";
public final SpringTestContext spring = new SpringTestContext();
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseHttpMessageConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
@Autowired
private MockMvc mvc;
@@ -152,7 +158,8 @@ public class OAuth2TokenExchangeGrantTests {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
this.registeredClientRepository.save(registeredClient);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
@@ -168,23 +175,27 @@ public class OAuth2TokenExchangeGrantTests {
}
@Test
public void requestWhenAccessTokenRequestValidAndNoActorTokenThenReturnAccessTokenResponseForImpersonation() throws Exception {
public void requestWhenAccessTokenRequestValidAndNoActorTokenThenReturnAccessTokenResponseForImpersonation()
throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
this.registeredClientRepository.save(registeredClient);
UsernamePasswordAuthenticationToken userPrincipal = createUserPrincipal("user");
OAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)
.attribute(Principal.class.getName(), userPrincipal).build();
.attribute(Principal.class.getName(), userPrincipal)
.build();
this.authorizationService.save(subjectAuthorization);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());
parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());
parameters.set(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);
parameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, subjectAuthorization.getAccessToken().getToken().getTokenValue());
parameters.set(OAuth2ParameterNames.SUBJECT_TOKEN,
subjectAuthorization.getAccessToken().getToken().getTokenValue());
parameters.set(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);
parameters.set(OAuth2ParameterNames.RESOURCE, RESOURCE);
parameters.set(OAuth2ParameterNames.AUDIENCE, AUDIENCE);
@@ -208,8 +219,8 @@ public class OAuth2TokenExchangeGrantTests {
MockHttpServletResponse servletResponse = mvcResult.getResponse();
MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),
HttpStatus.OK);
OAuth2AccessTokenResponse accessTokenResponse =
this.accessTokenResponseHttpMessageConverter.read(OAuth2AccessTokenResponse.class, httpResponse);
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseHttpMessageConverter
.read(OAuth2AccessTokenResponse.class, httpResponse);
String accessToken = accessTokenResponse.getAccessToken().getTokenValue();
OAuth2Authorization authorization = this.authorizationService.findByToken(accessToken,
@@ -217,19 +228,22 @@ public class OAuth2TokenExchangeGrantTests {
assertThat(authorization).isNotNull();
assertThat(authorization.getAccessToken()).isNotNull();
assertThat(authorization.getAccessToken().getClaims()).isNotNull();
// We do not populate claims (e.g. `aud`) based on the resource or audience parameters
// We do not populate claims (e.g. `aud`) based on the resource or audience
// parameters
assertThat(authorization.getAccessToken().getClaims().get(OAuth2TokenClaimNames.AUD))
.isEqualTo(List.of(registeredClient.getClientId()));
.isEqualTo(List.of(registeredClient.getClientId()));
assertThat(authorization.getRefreshToken()).isNull();
assertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isEqualTo(userPrincipal);
}
@Test
public void requestWhenAccessTokenRequestValidAndActorTokenThenReturnAccessTokenResponseForDelegation() throws Exception {
public void requestWhenAccessTokenRequestValidAndActorTokenThenReturnAccessTokenResponseForDelegation()
throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
this.registeredClientRepository.save(registeredClient);
UsernamePasswordAuthenticationToken userPrincipal = createUserPrincipal("user");
@@ -284,8 +298,8 @@ public class OAuth2TokenExchangeGrantTests {
MockHttpServletResponse servletResponse = mvcResult.getResponse();
MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),
HttpStatus.OK);
OAuth2AccessTokenResponse accessTokenResponse =
this.accessTokenResponseHttpMessageConverter.read(OAuth2AccessTokenResponse.class, httpResponse);
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseHttpMessageConverter
.read(OAuth2AccessTokenResponse.class, httpResponse);
String accessToken = accessTokenResponse.getAccessToken().getTokenValue();
OAuth2Authorization authorization = this.authorizationService.findByToken(accessToken,
@@ -296,7 +310,7 @@ public class OAuth2TokenExchangeGrantTests {
assertThat(authorization.getAccessToken().getClaims().get("act")).isNotNull();
assertThat(authorization.getRefreshToken()).isNull();
assertThat(authorization.<Authentication>getAttribute(Principal.class.getName()))
.isInstanceOf(OAuth2TokenExchangeCompositeAuthenticationToken.class);
.isInstanceOf(OAuth2TokenExchangeCompositeAuthenticationToken.class);
}
private static OAuth2AccessToken createAccessToken(String tokenValue) {
@@ -363,6 +377,7 @@ public class OAuth2TokenExchangeGrantTests {
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
}

View File

@@ -437,11 +437,10 @@ public class OAuth2TokenIntrospectionTests {
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
introspectRegisteredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret());
OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication =
new OAuth2TokenIntrospectionAuthenticationToken(
accessToken.getTokenValue(), clientPrincipal, null, null);
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(introspectRegisteredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret());
OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken(
accessToken.getTokenValue(), clientPrincipal, null, null);
when(authenticationConverter.convert(any())).thenReturn(tokenIntrospectionAuthentication);
when(authenticationProvider.supports(eq(OAuth2TokenIntrospectionAuthenticationToken.class))).thenReturn(true);
@@ -600,10 +599,12 @@ public class OAuth2TokenIntrospectionTests {
}
// @formatter:on
@Override
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().multipleIssuersAllowed(true).tokenIntrospectionEndpoint("/test/introspect").build();
return AuthorizationServerSettings.builder()
.multipleIssuersAllowed(true)
.tokenIntrospectionEndpoint("/test/introspect")
.build();
}
}

View File

@@ -387,7 +387,8 @@ public class OidcClientRegistrationTests {
when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error"));
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI))
.param(OAuth2ParameterNames.CLIENT_ID, "invalid").with(jwt()));
.param(OAuth2ParameterNames.CLIENT_ID, "invalid")
.with(jwt()));
verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());
verifyNoInteractions(authenticationSuccessHandler);
@@ -411,14 +412,16 @@ public class OidcClientRegistrationTests {
OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.with(httpBasic(clientRegistrationResponse.getClientId(), clientRegistrationResponse.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1"))
.andReturn();
this.mvc
.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.with(httpBasic(clientRegistrationResponse.getClientId(),
clientRegistrationResponse.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1"))
.andReturn();
}
// gh-1344
@@ -445,12 +448,12 @@ public class OidcClientRegistrationTests {
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()
.issuer(clientRegistrationResponse.getClientId())
.subject(clientRegistrationResponse.getClientId())
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.build();
.issuer(clientRegistrationResponse.getClientId())
.subject(clientRegistrationResponse.getClientId())
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.build();
JWKSet jwkSet = new JWKSet(
TestJwks.jwk(new SecretKeySpec(clientRegistrationResponse.getClientSecret().getBytes(), "HS256"))
@@ -460,15 +463,17 @@ public class OidcClientRegistrationTests {
Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));
this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
.param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue())
.param(OAuth2ParameterNames.CLIENT_ID, clientRegistrationResponse.getClientId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1"));
this.mvc
.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
.param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue())
.param(OAuth2ParameterNames.CLIENT_ID, clientRegistrationResponse.getClientId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1"));
}
@Test
@@ -533,7 +538,8 @@ public class OidcClientRegistrationTests {
// @formatter:on
Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));
MvcResult mvcResult = this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
MvcResult mvcResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,
@@ -553,8 +559,8 @@ public class OidcClientRegistrationTests {
httpHeaders.setBearerAuth(accessToken.getTokenValue());
// Register the client
mvcResult = this.mvc.perform(post(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI))
.headers(httpHeaders)
mvcResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)).headers(httpHeaders)
.contentType(MediaType.APPLICATION_JSON)
.content(getClientRegistrationRequestContent(clientRegistration)))
.andExpect(status().isCreated())
@@ -569,11 +575,11 @@ public class OidcClientRegistrationTests {
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);
return JwtClaimsSet.builder()
.issuer(registeredClient.getClientId())
.subject(registeredClient.getClientId())
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt);
.issuer(registeredClient.getClientId())
.subject(registeredClient.getClientId())
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt);
}
private static String asUrl(String uri, String path) {
@@ -753,9 +759,7 @@ public class OidcClientRegistrationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.multipleIssuersAllowed(true)
.build();
return AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();
}
@Bean

View File

@@ -80,28 +80,29 @@ public class OidcProviderConfigurationTests {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER));
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER));
}
@Test
public void requestWhenConfigurationRequestIncludesIssuerPathThenConfigurationResponseHasIssuerPath() throws Exception {
public void requestWhenConfigurationRequestIncludesIssuerPathThenConfigurationResponseHasIssuerPath()
throws Exception {
this.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();
String issuer = "https://example.com:8443/issuer1";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
issuer = "https://example.com:8443/path1/issuer2";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
issuer = "https://example.com:8443/path1/path2/issuer3";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
}
// gh-632
@@ -109,10 +110,9 @@ public class OidcProviderConfigurationTests {
public void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
.with(user("user")))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER));
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)).with(user("user")))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER));
}
// gh-616
@@ -122,9 +122,9 @@ public class OidcProviderConfigurationTests {
this.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));
}
@Test
@@ -133,9 +133,10 @@ public class OidcProviderConfigurationTests {
this.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER))
.andExpect(jsonPath("$.registration_endpoint").value(ISSUER.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER))
.andExpect(jsonPath("$.registration_endpoint")
.value(ISSUER.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));
}
private ResultMatcher[] defaultConfigurationMatchers(String issuer) {
@@ -235,9 +236,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer(ISSUER)
.build();
return AuthorizationServerSettings.builder().issuer(ISSUER).build();
}
}
@@ -248,9 +247,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.multipleIssuersAllowed(true)
.build();
return AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();
}
}

View File

@@ -324,12 +324,13 @@ public class OidcTests {
String issuer = "https://example.com:8443/issuer1";
// Login
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient);
MvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))
.queryParams(authorizationRequestParameters)
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI)).queryParams(authorizationRequestParameters)
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
assertThat(session.isNew()).isTrue();
@@ -340,12 +341,13 @@ public class OidcTests {
AUTHORIZATION_CODE_TOKEN_TYPE);
// Get ID Token
mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.params(getTokenRequestParameters(registeredClient, authorization))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andReturn();
mvcResult = this.mvc
.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.params(getTokenRequestParameters(registeredClient, authorization))
.header(HttpHeaders.AUTHORIZATION,
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andReturn();
MockHttpServletResponse servletResponse = mvcResult.getResponse();
MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),
@@ -356,11 +358,11 @@ public class OidcTests {
String idToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);
// Logout
mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI))
.param("id_token_hint", idToken)
.session(session))
.andExpect(status().is3xxRedirection())
.andReturn();
mvcResult = this.mvc
.perform(post(issuer.concat(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI)).param("id_token_hint", idToken)
.session(session))
.andExpect(status().is3xxRedirection())
.andReturn();
redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
assertThat(redirectedUrl).matches("/");

View File

@@ -526,9 +526,7 @@ public class OidcUserInfoTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.multipleIssuersAllowed(true)
.build();
return AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();
}
}

View File

@@ -63,8 +63,8 @@ public class OidcProviderConfigurationEndpointFilterTests {
@Test
public void doFilterWhenNotConfigurationRequestThenNotProcessed() throws Exception {
AuthorizationServerContextHolder.setContext(
new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
AuthorizationServerContextHolder
.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
String requestUri = "/path";
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
@@ -79,8 +79,8 @@ public class OidcProviderConfigurationEndpointFilterTests {
@Test
public void doFilterWhenConfigurationRequestPostThenNotProcessed() throws Exception {
AuthorizationServerContextHolder.setContext(
new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
AuthorizationServerContextHolder
.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
String requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
@@ -137,18 +137,25 @@ public class OidcProviderConfigurationEndpointFilterTests {
assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\"");
assertThat(providerConfigurationResponse).contains("\"scopes_supported\":[\"openid\"]");
assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]");
assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\",\"urn:ietf:params:oauth:grant-type:token-exchange\"]");
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(providerConfigurationResponse).contains(
"\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\",\"urn:ietf:params:oauth:grant-type:token-exchange\"]");
assertThat(providerConfigurationResponse)
.contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
assertThat(providerConfigurationResponse).contains(
"\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(providerConfigurationResponse)
.contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
assertThat(providerConfigurationResponse).contains(
"\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(providerConfigurationResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
assertThat(providerConfigurationResponse).contains("\"tls_client_certificate_bound_access_tokens\":true");
assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/userinfo\"");
assertThat(providerConfigurationResponse).contains("\"end_session_endpoint\":\"https://example.com/connect/logout\"");
assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(providerConfigurationResponse)
.contains("\"end_session_endpoint\":\"https://example.com/connect/logout\"");
assertThat(providerConfigurationResponse).contains(
"\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
}
@Test

View File

@@ -86,13 +86,16 @@ public class AuthorizationServerSettingsTests {
public void buildWhenIssuerSetAndMultipleIssuersAllowedTrueThenThrowIllegalArgumentException() {
String issuer = "https://example.com:9000";
assertThatIllegalArgumentException()
.isThrownBy(() -> AuthorizationServerSettings.builder().issuer(issuer).multipleIssuersAllowed(true).build())
.withMessage("The issuer identifier (" + issuer + ") cannot be set when isMultipleIssuersAllowed() is true.");
.isThrownBy(() -> AuthorizationServerSettings.builder().issuer(issuer).multipleIssuersAllowed(true).build())
.withMessage(
"The issuer identifier (" + issuer + ") cannot be set when isMultipleIssuersAllowed() is true.");
}
@Test
public void buildWhenIssuerNotSetAndMultipleIssuersAllowedTrueThenDefaultsAreSet() {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()
.multipleIssuersAllowed(true)
.build();
assertThat(authorizationServerSettings.getIssuer()).isNull();
assertThat(authorizationServerSettings.isMultipleIssuersAllowed()).isTrue();

View File

@@ -65,9 +65,10 @@ public class ClientSettingsTests {
@Test
public void x509CertificateSubjectDNWhenProvidedThenSet() {
ClientSettings clientSettings = ClientSettings.builder()
.x509CertificateSubjectDN("CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US")
.build();
assertThat(clientSettings.getX509CertificateSubjectDN()).isEqualTo("CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US");
.x509CertificateSubjectDN("CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US")
.build();
assertThat(clientSettings.getX509CertificateSubjectDN())
.isEqualTo("CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US");
}
@Test

View File

@@ -153,18 +153,16 @@ public class TokenSettingsTests {
@Test
public void x509CertificateBoundAccessTokensWhenTrueThenSet() {
TokenSettings tokenSettings = TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build();
TokenSettings tokenSettings = TokenSettings.builder().x509CertificateBoundAccessTokens(true).build();
assertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isTrue();
}
@Test
public void settingWhenCustomThenSet() {
TokenSettings tokenSettings = TokenSettings.builder()
.setting("name1", "value1")
.settings(settings -> settings.put("name2", "value2"))
.build();
.setting("name1", "value1")
.settings(settings -> settings.put("name2", "value2"))
.build();
assertThat(tokenSettings.getSettings()).hasSize(10);
assertThat(tokenSettings.<String>getSetting("name1")).isEqualTo("value1");
assertThat(tokenSettings.<String>getSetting("name2")).isEqualTo("value2");

View File

@@ -29,22 +29,25 @@ public final class TestX509Certificates {
// Generate the Root certificate (Trust Anchor or most-trusted CA)
KeyPair rootKeyPair = X509CertificateUtils.generateRSAKeyPair();
String distinguishedName = "CN=spring-samples-trusted-ca, OU=Spring Samples, O=Spring, C=US";
X509Certificate rootCertificate = X509CertificateUtils.createTrustAnchorCertificate(rootKeyPair, distinguishedName);
X509Certificate rootCertificate = X509CertificateUtils.createTrustAnchorCertificate(rootKeyPair,
distinguishedName);
// Generate the CA (intermediary) certificate
KeyPair caKeyPair = X509CertificateUtils.generateRSAKeyPair();
distinguishedName = "CN=spring-samples-ca, OU=Spring Samples, O=Spring, C=US";
X509Certificate caCertificate = X509CertificateUtils.createCACertificate(
rootCertificate, rootKeyPair.getPrivate(), caKeyPair.getPublic(), distinguishedName);
X509Certificate caCertificate = X509CertificateUtils.createCACertificate(rootCertificate,
rootKeyPair.getPrivate(), caKeyPair.getPublic(), distinguishedName);
// Generate certificate for demo-client-sample
KeyPair demoClientKeyPair = X509CertificateUtils.generateRSAKeyPair();
distinguishedName = "CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US";
X509Certificate demoClientCertificate = X509CertificateUtils.createEndEntityCertificate(
caCertificate, caKeyPair.getPrivate(), demoClientKeyPair.getPublic(), distinguishedName);
X509Certificate demoClientCertificate = X509CertificateUtils.createEndEntityCertificate(caCertificate,
caKeyPair.getPrivate(), demoClientKeyPair.getPublic(), distinguishedName);
DEMO_CLIENT_PKI_CERTIFICATE = new X509Certificate[] { demoClientCertificate, caCertificate, rootCertificate };
} catch (Exception ex) {
DEMO_CLIENT_PKI_CERTIFICATE = new X509Certificate[] { demoClientCertificate, caCertificate,
rootCertificate };
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
@@ -55,10 +58,12 @@ public final class TestX509Certificates {
// Generate self-signed certificate for demo-client-sample
KeyPair keyPair = X509CertificateUtils.generateRSAKeyPair();
String distinguishedName = "CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US";
X509Certificate demoClientSelfSignedCertificate = X509CertificateUtils.createTrustAnchorCertificate(keyPair, distinguishedName);
X509Certificate demoClientSelfSignedCertificate = X509CertificateUtils.createTrustAnchorCertificate(keyPair,
distinguishedName);
DEMO_CLIENT_SELF_SIGNED_CERTIFICATE = new X509Certificate[] { demoClientSelfSignedCertificate };
} catch (Exception ex) {
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}

View File

@@ -44,15 +44,20 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
* @author Joe Grandja
*/
public final class X509CertificateUtils {
private static final String BC_PROVIDER = "BC";
private static final String SHA256_RSA_SIGNATURE_ALGORITHM = "SHA256withRSA";
private static final Date DEFAULT_START_DATE;
private static final Date DEFAULT_END_DATE;
static {
Security.addProvider(new BouncyCastleProvider());
// Setup default certificate start date to yesterday and end date for 1 year validity
// Setup default certificate start date to yesterday and end date for 1 year
// validity
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -1);
DEFAULT_START_DATE = calendar.getTime();
@@ -69,34 +74,31 @@ public final class X509CertificateUtils {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BC_PROVIDER);
keyPairGenerator.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4));
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
public static X509Certificate createTrustAnchorCertificate(KeyPair keyPair, String distinguishedName) throws Exception {
public static X509Certificate createTrustAnchorCertificate(KeyPair keyPair, String distinguishedName)
throws Exception {
X500Principal subject = new X500Principal(distinguishedName);
BigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
subject,
serialNum,
DEFAULT_START_DATE,
DEFAULT_END_DATE,
subject,
keyPair.getPublic());
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serialNum, DEFAULT_START_DATE,
DEFAULT_END_DATE, subject, keyPair.getPublic());
// Add Extensions
JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
certBuilder
// A BasicConstraints to mark root certificate as CA certificate
.addExtension(Extension.basicConstraints, true, new BasicConstraints(true))
.addExtension(Extension.subjectKeyIdentifier, false,
extensionUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
// A BasicConstraints to mark root certificate as CA certificate
.addExtension(Extension.basicConstraints, true, new BasicConstraints(true))
.addExtension(Extension.subjectKeyIdentifier, false,
extensionUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM)
.setProvider(BC_PROVIDER).build(keyPair.getPrivate());
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)
.build(keyPair.getPrivate());
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);
@@ -109,32 +111,26 @@ public final class X509CertificateUtils {
X500Principal subject = new X500Principal(distinguishedName);
BigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
signerCert.getSubjectX500Principal(),
serialNum,
DEFAULT_START_DATE,
DEFAULT_END_DATE,
subject,
certKey);
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),
serialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);
// Add Extensions
JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
certBuilder
// A BasicConstraints to mark as CA certificate and how many CA certificates can follow it in the chain
// (with 0 meaning the chain ends with the next certificate in the chain).
.addExtension(Extension.basicConstraints, true, new BasicConstraints(0))
// KeyUsage specifies what the public key in the certificate can be used for.
// In this case, it can be used for signing other certificates and/or
// signing Certificate Revocation Lists (CRLs).
.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))
.addExtension(Extension.authorityKeyIdentifier, false,
extensionUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier, false,
extensionUtils.createSubjectKeyIdentifier(certKey));
// A BasicConstraints to mark as CA certificate and how many CA certificates
// can follow it in the chain
// (with 0 meaning the chain ends with the next certificate in the chain).
.addExtension(Extension.basicConstraints, true, new BasicConstraints(0))
// KeyUsage specifies what the public key in the certificate can be used for.
// In this case, it can be used for signing other certificates and/or
// signing Certificate Revocation Lists (CRLs).
.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))
.addExtension(Extension.authorityKeyIdentifier, false,
extensionUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM)
.setProvider(BC_PROVIDER).build(signerKey);
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)
.build(signerKey);
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);
@@ -147,26 +143,18 @@ public final class X509CertificateUtils {
X500Principal subject = new X500Principal(distinguishedName);
BigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
signerCert.getSubjectX500Principal(),
serialNum,
DEFAULT_START_DATE,
DEFAULT_END_DATE,
subject,
certKey);
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),
serialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);
JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
certBuilder
.addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.digitalSignature))
.addExtension(Extension.authorityKeyIdentifier, false,
extensionUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier, false,
extensionUtils.createSubjectKeyIdentifier(certKey));
certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature))
.addExtension(Extension.authorityKeyIdentifier, false,
extensionUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM)
.setProvider(BC_PROVIDER).build(signerKey);
ContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)
.build(signerKey);
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);

View File

@@ -63,8 +63,8 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
@Test
public void doFilterWhenNotAuthorizationServerMetadataRequestThenNotProcessed() throws Exception {
AuthorizationServerContextHolder.setContext(
new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
AuthorizationServerContextHolder
.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
String requestUri = "/path";
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
@@ -79,8 +79,8 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
@Test
public void doFilterWhenAuthorizationServerMetadataRequestPostThenNotProcessed() throws Exception {
AuthorizationServerContextHolder.setContext(
new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
AuthorizationServerContextHolder
.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));
String requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
@@ -126,16 +126,24 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);
String authorizationServerMetadataResponse = response.getContentAsString();
assertThat(authorizationServerMetadataResponse).contains("\"issuer\":\"https://example.com\"");
assertThat(authorizationServerMetadataResponse).contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\"");
assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");
assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse)
.contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\"");
assertThat(authorizationServerMetadataResponse)
.contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");
assertThat(authorizationServerMetadataResponse).contains(
"\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\"");
assertThat(authorizationServerMetadataResponse).contains("\"response_types_supported\":[\"code\"]");
assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\",\"urn:ietf:params:oauth:grant-type:token-exchange\"]");
assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse).contains(
"\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\",\"urn:ietf:params:oauth:grant-type:token-exchange\"]");
assertThat(authorizationServerMetadataResponse)
.contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
assertThat(authorizationServerMetadataResponse).contains(
"\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse)
.contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
assertThat(authorizationServerMetadataResponse).contains(
"\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
assertThat(authorizationServerMetadataResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
assertThat(authorizationServerMetadataResponse).contains("\"tls_client_certificate_bound_access_tokens\":true");
}

View File

@@ -452,17 +452,16 @@ public class OAuth2TokenEndpointFilterTests {
@Test
public void doFilterWhenTokenExchangeRequestThenAccessTokenResponse() throws Exception {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE).build();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "token",
.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
.build();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token",
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
new HashSet<>(Arrays.asList("scope1", "scope2")));
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now());
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
new OAuth2AccessTokenAuthenticationToken(
registeredClient, clientPrincipal, accessToken, refreshToken);
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(
registeredClient, clientPrincipal, accessToken, refreshToken);
when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication);
@@ -478,24 +477,22 @@ public class OAuth2TokenEndpointFilterTests {
verifyNoInteractions(filterChain);
ArgumentCaptor<OAuth2TokenExchangeAuthenticationToken> tokenExchangeAuthenticationCaptor =
ArgumentCaptor.forClass(OAuth2TokenExchangeAuthenticationToken.class);
ArgumentCaptor<OAuth2TokenExchangeAuthenticationToken> tokenExchangeAuthenticationCaptor = ArgumentCaptor
.forClass(OAuth2TokenExchangeAuthenticationToken.class);
verify(this.authenticationManager).authenticate(tokenExchangeAuthenticationCaptor.capture());
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthenticationToken =
tokenExchangeAuthenticationCaptor.getValue();
OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthenticationToken = tokenExchangeAuthenticationCaptor
.getValue();
assertThat(tokenExchangeAuthenticationToken.getSubjectToken()).isEqualTo("subject-token");
assertThat(tokenExchangeAuthenticationToken.getSubjectTokenType()).isEqualTo(ACCESS_TOKEN_TYPE);
assertThat(tokenExchangeAuthenticationToken.getPrincipal()).isEqualTo(clientPrincipal);
assertThat(tokenExchangeAuthenticationToken.getScopes()).isEqualTo(registeredClient.getScopes());
assertThat(tokenExchangeAuthenticationToken.getAdditionalParameters())
.containsExactly(entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" }));
assertThat(tokenExchangeAuthenticationToken.getDetails())
.asInstanceOf(type(WebAuthenticationDetails.class))
.extracting(WebAuthenticationDetails::getRemoteAddress)
.isEqualTo(REMOTE_ADDRESS);
assertThat(tokenExchangeAuthenticationToken.getAdditionalParameters()).containsExactly(
entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" }));
assertThat(tokenExchangeAuthenticationToken.getDetails()).asInstanceOf(type(WebAuthenticationDetails.class))
.extracting(WebAuthenticationDetails::getRemoteAddress)
.isEqualTo(REMOTE_ADDRESS);
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);
@@ -503,10 +500,10 @@ public class OAuth2TokenEndpointFilterTests {
OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();
assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());
assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());
assertThat(accessTokenResult.getIssuedAt()).isBetween(
accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResult.getExpiresAt()).isBetween(
accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1),
accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1),
accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());
OAuth2RefreshToken refreshTokenResult = accessTokenResponse.getRefreshToken();

View File

@@ -53,13 +53,16 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
* @author Dmitriy Dubson
*/
public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {
private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
private final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());
private final OAuth2AccessTokenResponseAuthenticationSuccessHandler authenticationSuccessHandler =
new OAuth2AccessTokenResponseAuthenticationSuccessHandler();
this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
this.registeredClient.getClientSecret());
private final OAuth2AccessTokenResponseAuthenticationSuccessHandler authenticationSuccessHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler();
@Test
public void setAccessTokenResponseCustomizerWhenNullThenThrowIllegalArgumentException() {
@@ -79,23 +82,22 @@ public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(
this.registeredClient, this.clientPrincipal, accessToken, refreshToken, additionalParameters);
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient,
this.clientPrincipal, accessToken, refreshToken, additionalParameters);
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);
assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(accessToken.getTokenValue());
assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(accessToken.getTokenType());
assertThat(accessTokenResponse.getAccessToken().getIssuedAt()).isBetween(
accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(
accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getIssuedAt())
.isBetween(accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getExpiresAt())
.isBetween(accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResponse.getRefreshToken()).isNotNull();
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());
assertThat(accessTokenResponse.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(
Map.of("param1", "value1")
);
assertThat(accessTokenResponse.getAdditionalParameters())
.containsExactlyInAnyOrderEntriesOf(Map.of("param1", "value1"));
}
@Test
@@ -103,16 +105,17 @@ public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
assertThatThrownBy(() ->
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, new TestingAuthenticationToken(this.clientPrincipal, null)))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);
assertThatThrownBy(() -> this.authenticationSuccessHandler.onAuthenticationSuccess(request, response,
new TestingAuthenticationToken(this.clientPrincipal, null)))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);
}
@Test
public void onAuthenticationSuccessWhenAccessTokenResponseCustomizerSetThenAccessTokenResponseCustomized() throws Exception {
public void onAuthenticationSuccessWhenAccessTokenResponseCustomizerSetThenAccessTokenResponseCustomized()
throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -120,8 +123,8 @@ public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(
this.registeredClient, this.clientPrincipal, accessToken, refreshToken, additionalParameters);
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient,
this.clientPrincipal, accessToken, refreshToken, additionalParameters);
Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer = (authenticationContext) -> {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = authenticationContext.getAuthentication();
@@ -136,20 +139,19 @@ public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);
assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(accessToken.getTokenValue());
assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(accessToken.getTokenType());
assertThat(accessTokenResponse.getAccessToken().getIssuedAt()).isBetween(
accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(
accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getIssuedAt())
.isBetween(accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResponse.getAccessToken().getExpiresAt())
.isBetween(accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResponse.getRefreshToken()).isNotNull();
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());
assertThat(accessTokenResponse.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(
Map.of("param1", "value1", "authorization_id", "id")
);
assertThat(accessTokenResponse.getAdditionalParameters())
.containsExactlyInAnyOrderEntriesOf(Map.of("param1", "value1", "authorization_id", "id"));
}
private OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception {
MockClientHttpResponse httpResponse = new MockClientHttpResponse(
response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus()));
MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),
HttpStatus.valueOf(response.getStatus()));
return this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);
}

View File

@@ -41,11 +41,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Steve Riesenberg
*/
public class OAuth2TokenExchangeAuthenticationConverterTests {
private static final String CLIENT_ID = "client-1";
private static final String TOKEN_URI = "/oauth2/token";
private static final String SUBJECT_TOKEN = "EfYu_0jEL";
private static final String ACTOR_TOKEN = "JlNE_xR1f";
private static final String ACCESS_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:access_token";
private static final String JWT_TOKEN_TYPE_VALUE = "urn:ietf:params:oauth:token-type:jwt";
private OAuth2TokenExchangeAuthenticationConverter converter;
@@ -222,7 +228,6 @@ public class OAuth2TokenExchangeAuthenticationConverterTests {
// @formatter:on
}
@Test
public void convertWhenMultipleActorTokenParametersThenInvalidRequestError() {
MockHttpServletRequest request = createRequest();
@@ -312,8 +317,8 @@ public class OAuth2TokenExchangeAuthenticationConverterTests {
securityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null));
SecurityContextHolder.setContext(securityContext);
OAuth2TokenExchangeAuthenticationToken authentication =
(OAuth2TokenExchangeAuthenticationToken) this.converter.convert(request);
OAuth2TokenExchangeAuthenticationToken authentication = (OAuth2TokenExchangeAuthenticationToken) this.converter
.convert(request);
assertThat(authentication).isNotNull();
assertThat(authentication.getResources()).containsExactly("https://mydomain.com/resource1",
"https://mydomain.com/resource2");

View File

@@ -39,6 +39,7 @@ import static org.assertj.core.api.Assertions.entry;
* @author Joe Grandja
*/
public class X509ClientCertificateAuthenticationConverterTests {
private final X509ClientCertificateAuthenticationConverter converter = new X509ClientCertificateAuthenticationConverter();
@Test
@@ -51,8 +52,7 @@ public class X509ClientCertificateAuthenticationConverterTests {
@Test
public void convertWhenEmptyX509CertificateThenReturnNull() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute("jakarta.servlet.request.X509Certificate",
new X509Certificate[0]);
request.setAttribute("jakarta.servlet.request.X509Certificate", new X509Certificate[0]);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNull();
}
@@ -62,11 +62,10 @@ public class X509ClientCertificateAuthenticationConverterTests {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute("jakarta.servlet.request.X509Certificate",
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
assertThatThrownBy(() -> this.converter.convert(request))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
}
@Test
@@ -76,11 +75,10 @@ public class X509ClientCertificateAuthenticationConverterTests {
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1");
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-2");
assertThatThrownBy(() -> this.converter.convert(request))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
}
@Test
@@ -93,16 +91,16 @@ public class X509ClientCertificateAuthenticationConverterTests {
request.addParameter(OAuth2ParameterNames.CODE, "code");
request.addParameter("custom-param-1", "custom-value-1");
request.addParameter("custom-param-2", "custom-value-1", "custom-value-2");
OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request);
OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter
.convert(request);
assertThat(authentication.getPrincipal()).isEqualTo("client-1");
assertThat(authentication.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
assertThat(authentication.getAdditionalParameters())
.containsOnly(
entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
entry(OAuth2ParameterNames.CODE, "code"),
entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] {"custom-value-1", "custom-value-2"}));
assertThat(authentication.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);
assertThat(authentication.getAdditionalParameters()).containsOnly(
entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
entry(OAuth2ParameterNames.CODE, "code"), entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" }));
}
@Test
@@ -115,16 +113,16 @@ public class X509ClientCertificateAuthenticationConverterTests {
request.addParameter(OAuth2ParameterNames.CODE, "code");
request.addParameter("custom-param-1", "custom-value-1");
request.addParameter("custom-param-2", "custom-value-1", "custom-value-2");
OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request);
OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter
.convert(request);
assertThat(authentication.getPrincipal()).isEqualTo("client-1");
assertThat(authentication.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);
assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);
assertThat(authentication.getAdditionalParameters())
.containsOnly(
entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
entry(OAuth2ParameterNames.CODE, "code"),
entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] {"custom-value-1", "custom-value-2"}));
assertThat(authentication.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);
assertThat(authentication.getAdditionalParameters()).containsOnly(
entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
entry(OAuth2ParameterNames.CODE, "code"), entry("custom-param-1", "custom-value-1"),
entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" }));
}
}