From 666d569b489c09fa98b7c7898efc49ef0a289adc Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Wed, 10 Nov 2021 15:02:01 -0500 Subject: [PATCH] Support resolving issuer from current request Closes gh-479 --- .../OAuth2AuthorizationServerConfigurer.java | 16 +++--- .../server/authorization/OidcConfigurer.java | 17 +++---- ...thorizationCodeAuthenticationProvider.java | 7 +-- ...2AuthorizationCodeAuthenticationToken.java | 20 ++++++++ ...AuthorizationGrantAuthenticationToken.java | 38 ++++++++++++++ ...ientCredentialsAuthenticationProvider.java | 7 +-- ...2ClientCredentialsAuthenticationToken.java | 18 +++++++ ...th2RefreshTokenAuthenticationProvider.java | 7 +-- ...OAuth2RefreshTokenAuthenticationToken.java | 21 ++++++++ ...entRegistrationAuthenticationProvider.java | 22 +++++---- ...ClientRegistrationAuthenticationToken.java | 39 ++++++++++++++- .../OidcClientRegistrationEndpointFilter.java | 10 +++- ...dcProviderConfigurationEndpointFilter.java | 25 ++++++---- ...orizationServerMetadataEndpointFilter.java | 33 ++++++++++--- .../authorization/web/WebAttributes.java | 37 ++++++++++++++ ...horizationCodeAuthenticationConverter.java | 5 +- ...entCredentialsAuthenticationConverter.java | 6 ++- ...h2RefreshTokenAuthenticationConverter.java | 6 ++- .../OAuth2ClientCredentialsGrantTests.java | 3 +- .../JwtEncodingContextTests.java | 3 +- ...zationCodeAuthenticationProviderTests.java | 29 +++++------ ...orizationCodeAuthenticationTokenTests.java | 17 +++++-- ...redentialsAuthenticationProviderTests.java | 22 ++++++--- ...ntCredentialsAuthenticationTokenTests.java | 16 ++++-- ...freshTokenAuthenticationProviderTests.java | 39 ++++++++++----- ...2RefreshTokenAuthenticationTokenTests.java | 17 +++++-- ...gistrationAuthenticationProviderTests.java | 49 ++++++++++++------- ...tRegistrationAuthenticationTokenTests.java | 22 ++++++--- ...ClientRegistrationEndpointFilterTests.java | 11 ++++- ...viderConfigurationEndpointFilterTests.java | 3 ++ ...tionServerMetadataEndpointFilterTests.java | 32 ++++++++++++ .../web/OAuth2TokenEndpointFilterTests.java | 9 +++- .../config/AuthorizationServerConfig.java | 6 --- .../config/AuthorizationServerConfig.java | 6 --- 34 files changed, 468 insertions(+), 150 deletions(-) create mode 100644 oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/WebAttributes.java diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java index 1a10cb4b..10767522 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java @@ -216,9 +216,17 @@ public final class OAuth2AuthorizationServerConfigurer configurer.configure(builder)); - ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder); AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class); OAuth2TokenIntrospectionEndpointFilter tokenIntrospectionEndpointFilter = @@ -238,12 +246,6 @@ public final class OAuth2AuthorizationServerConfigurer, AbstractOAuth2Configurer> createConfigurers() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcConfigurer.java index dd17955a..8e7962e6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcConfigurer.java @@ -85,16 +85,13 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { } List requestMatchers = new ArrayList<>(); - ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder); - if (providerSettings.getIssuer() != null) { - requestMatchers.add(new AntPathRequestMatcher( - "/.well-known/openid-configuration", HttpMethod.GET.name())); - } + requestMatchers.add(new AntPathRequestMatcher( + "/.well-known/openid-configuration", HttpMethod.GET.name())); requestMatchers.add(this.userInfoEndpointConfigurer.getRequestMatcher()); if (this.clientRegistrationEndpointConfigurer != null) { requestMatchers.add(this.clientRegistrationEndpointConfigurer.getRequestMatcher()); } - this.requestMatcher = requestMatchers.size() > 1 ? new OrRequestMatcher(requestMatchers) : requestMatchers.get(0); + this.requestMatcher = new OrRequestMatcher(requestMatchers); } @Override @@ -105,11 +102,9 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { } ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder); - if (providerSettings.getIssuer() != null) { - OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = - new OidcProviderConfigurationEndpointFilter(providerSettings); - builder.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); - } + OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = + new OidcProviderConfigurationEndpointFilter(providerSettings); + builder.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); } @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java index 7417d558..a91b1e59 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java @@ -26,7 +26,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -87,7 +86,6 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth private final JwtEncoder jwtEncoder; private OAuth2TokenCustomizer jwtCustomizer = (context) -> {}; private Supplier refreshTokenGenerator = DEFAULT_REFRESH_TOKEN_GENERATOR::generateKey; - private ProviderSettings providerSettings; /** * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the provided parameters. @@ -124,9 +122,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth this.refreshTokenGenerator = refreshTokenGenerator; } - @Autowired(required = false) + @Deprecated protected void setProviderSettings(ProviderSettings providerSettings) { - this.providerSettings = providerSettings; } @Override @@ -167,7 +164,7 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } - String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null; + String issuer = authorizationCodeAuthentication.getIssuer(); Set authorizedScopes = authorization.getAttribute( OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java index 6f38750e..877edf5d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java @@ -43,7 +43,9 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat * @param clientPrincipal the authenticated client principal * @param redirectUri the redirect uri * @param additionalParameters the additional parameters + * @deprecated Use {@link #OAuth2AuthorizationCodeAuthenticationToken(String, String, Authentication, String, Map)} instead */ + @Deprecated public OAuth2AuthorizationCodeAuthenticationToken(String code, Authentication clientPrincipal, @Nullable String redirectUri, @Nullable Map additionalParameters) { super(AuthorizationGrantType.AUTHORIZATION_CODE, clientPrincipal, additionalParameters); @@ -52,6 +54,24 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat this.redirectUri = redirectUri; } + /** + * Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided parameters. + * + * @param issuer the issuer identifier + * @param code the authorization code + * @param clientPrincipal the authenticated client principal + * @param redirectUri the redirect uri + * @param additionalParameters the additional parameters + * @since 0.2.1 + */ + public OAuth2AuthorizationCodeAuthenticationToken(String issuer, String code, Authentication clientPrincipal, + @Nullable String redirectUri, @Nullable Map additionalParameters) { + super(AuthorizationGrantType.AUTHORIZATION_CODE, issuer, clientPrincipal, additionalParameters); + Assert.hasText(code, "code cannot be empty"); + this.code = code; + this.redirectUri = redirectUri; + } + /** * Returns the authorization code. * diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java index e43443d2..9871bec5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java @@ -39,6 +39,7 @@ import org.springframework.util.Assert; public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = Version.SERIAL_VERSION_UID; private final AuthorizationGrantType authorizationGrantType; + private final String issuer; private final Authentication clientPrincipal; private final Map additionalParameters; @@ -48,13 +49,40 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent * @param authorizationGrantType the authorization grant type * @param clientPrincipal the authenticated client principal * @param additionalParameters the additional parameters + * @deprecated Use {@link #OAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType, String, Authentication, Map)} instead */ + @Deprecated protected OAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType authorizationGrantType, Authentication clientPrincipal, @Nullable Map additionalParameters) { super(Collections.emptyList()); Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null"); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); this.authorizationGrantType = authorizationGrantType; + this.issuer = null; + this.clientPrincipal = clientPrincipal; + this.additionalParameters = Collections.unmodifiableMap( + additionalParameters != null ? + new HashMap<>(additionalParameters) : + Collections.emptyMap()); + } + + /** + * Sub-class constructor. + * + * @param authorizationGrantType the authorization grant type + * @param issuer the issuer identifier + * @param clientPrincipal the authenticated client principal + * @param additionalParameters the additional parameters + * @since 0.2.1 + */ + protected OAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType authorizationGrantType, + String issuer, Authentication clientPrincipal, @Nullable Map additionalParameters) { + super(Collections.emptyList()); + Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null"); + Assert.hasText(issuer, "issuer cannot be empty"); + Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); + this.authorizationGrantType = authorizationGrantType; + this.issuer = issuer; this.clientPrincipal = clientPrincipal; this.additionalParameters = Collections.unmodifiableMap( additionalParameters != null ? @@ -71,6 +99,16 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent return this.authorizationGrantType; } + /** + * Returns the issuer identifier. + * + * @return the issuer identifier + * @since 0.2.1 + */ + public String getIssuer() { + return this.issuer; + } + @Override public Object getPrincipal() { return this.clientPrincipal; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java index 34ac9731..b938ee90 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java @@ -19,7 +19,6 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.function.Consumer; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -62,7 +61,6 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth private final OAuth2AuthorizationService authorizationService; private final JwtEncoder jwtEncoder; private OAuth2TokenCustomizer jwtCustomizer = (context) -> {}; - private ProviderSettings providerSettings; /** * Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the provided parameters. @@ -90,9 +88,8 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth this.jwtCustomizer = jwtCustomizer; } - @Autowired(required = false) + @Deprecated protected void setProviderSettings(ProviderSettings providerSettings) { - this.providerSettings = providerSettings; } @Override @@ -118,7 +115,7 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes()); } - String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null; + String issuer = clientCredentialsAuthentication.getIssuer(); JoseHeader.Builder headersBuilder = JwtUtils.headers(); JwtClaimsSet.Builder claimsBuilder = JwtUtils.accessTokenClaims( diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java index 83849489..5ff5556c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java @@ -41,7 +41,9 @@ public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2Authorizat * @param clientPrincipal the authenticated client principal * @param scopes the requested scope(s) * @param additionalParameters the additional parameters + * @deprecated Use {@link #OAuth2ClientCredentialsAuthenticationToken(String, Authentication, Set, Map)} instead */ + @Deprecated public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, @Nullable Set scopes, @Nullable Map additionalParameters) { super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters); @@ -49,6 +51,22 @@ public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2Authorizat scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); } + /** + * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters. + * + * @param issuer the issuer identifier + * @param clientPrincipal the authenticated client principal + * @param scopes the requested scope(s) + * @param additionalParameters the additional parameters + * @since 0.2.1 + */ + public OAuth2ClientCredentialsAuthenticationToken(String issuer, Authentication clientPrincipal, + @Nullable Set scopes, @Nullable Map additionalParameters) { + super(AuthorizationGrantType.CLIENT_CREDENTIALS, issuer, clientPrincipal, additionalParameters); + this.scopes = Collections.unmodifiableSet( + scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); + } + /** * Returns the requested scope(s). * diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java index b9e1dec0..d930088e 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java @@ -26,7 +26,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -80,7 +79,6 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic private final JwtEncoder jwtEncoder; private OAuth2TokenCustomizer jwtCustomizer = (context) -> {}; private Supplier refreshTokenGenerator = DEFAULT_REFRESH_TOKEN_GENERATOR::generateKey; - private ProviderSettings providerSettings; /** * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters. @@ -118,9 +116,8 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic this.refreshTokenGenerator = refreshTokenGenerator; } - @Autowired(required = false) + @Deprecated protected void setProviderSettings(ProviderSettings providerSettings) { - this.providerSettings = providerSettings; } @Override @@ -166,7 +163,7 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic scopes = authorizedScopes; } - String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null; + String issuer = refreshTokenAuthentication.getIssuer(); JoseHeader.Builder headersBuilder = JwtUtils.headers(); JwtClaimsSet.Builder claimsBuilder = JwtUtils.accessTokenClaims( diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java index 26ce4ad0..a8f50599 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java @@ -44,7 +44,9 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr * @param clientPrincipal the authenticated client principal * @param scopes the requested scope(s) * @param additionalParameters the additional parameters + * @deprecated Use {@link #OAuth2RefreshTokenAuthenticationToken(String, String, Authentication, Set, Map)} instead */ + @Deprecated public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal, @Nullable Set scopes, @Nullable Map additionalParameters) { super(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, additionalParameters); @@ -54,6 +56,25 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); } + /** + * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters. + * + * @param issuer the issuer identifier + * @param refreshToken the refresh token + * @param clientPrincipal the authenticated client principal + * @param scopes the requested scope(s) + * @param additionalParameters the additional parameters + * @since 0.2.1 + */ + public OAuth2RefreshTokenAuthenticationToken(String issuer, String refreshToken, Authentication clientPrincipal, + @Nullable Set scopes, @Nullable Map additionalParameters) { + super(AuthorizationGrantType.REFRESH_TOKEN, issuer, clientPrincipal, additionalParameters); + Assert.hasText(refreshToken, "refreshToken cannot be empty"); + this.refreshToken = refreshToken; + this.scopes = Collections.unmodifiableSet( + scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); + } + /** * Returns the refresh token. * diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java index e07cf893..fb8f46ff 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java @@ -178,9 +178,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT); } - OidcClientRegistration clientRegistration = buildRegistration(registeredClient).build(); + OidcClientRegistration clientRegistration = buildRegistration( + registeredClient, clientRegistrationAuthentication.getIssuer()) + .build(); - return new OidcClientRegistrationAuthenticationToken( + return new OidcClientRegistrationAuthenticationToken(clientRegistrationAuthentication.getIssuer(), (Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration); } @@ -198,7 +200,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe RegisteredClient registeredClient = createClient(clientRegistrationAuthentication.getClientRegistration()); this.registeredClientRepository.save(registeredClient); - OAuth2Authorization registeredClientAuthorization = registerAccessToken(registeredClient); + OAuth2Authorization registeredClientAuthorization = registerAccessToken( + registeredClient, clientRegistrationAuthentication.getIssuer()); // Invalidate the "initial" access token as it can only be used once authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorizedAccessToken.getToken()); @@ -207,21 +210,22 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe } this.authorizationService.save(authorization); - OidcClientRegistration clientRegistration = buildRegistration(registeredClient) + OidcClientRegistration clientRegistration = buildRegistration( + registeredClient, clientRegistrationAuthentication.getIssuer()) .registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue()) .build(); - return new OidcClientRegistrationAuthenticationToken( + return new OidcClientRegistrationAuthenticationToken(clientRegistrationAuthentication.getIssuer(), (Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration); } - private OAuth2Authorization registerAccessToken(RegisteredClient registeredClient) { + private OAuth2Authorization registerAccessToken(RegisteredClient registeredClient, String issuer) { JoseHeader headers = JwtUtils.headers().build(); Set authorizedScopes = Collections.singleton(DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE); JwtClaimsSet claims = JwtUtils.accessTokenClaims( - registeredClient, this.providerSettings.getIssuer(), registeredClient.getClientId(), authorizedScopes) + registeredClient, issuer, registeredClient.getClientId(), authorizedScopes) .build(); Jwt registrationAccessToken = this.jwtEncoder.encode(headers, claims); @@ -246,7 +250,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe return registeredClientAuthorization; } - private OidcClientRegistration.Builder buildRegistration(RegisteredClient registeredClient) { + private OidcClientRegistration.Builder buildRegistration(RegisteredClient registeredClient, String issuer) { // @formatter:off OidcClientRegistration.Builder builder = OidcClientRegistration.builder() .clientId(registeredClient.getClientId()) @@ -270,7 +274,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe scopes.addAll(registeredClient.getScopes())); } - String registrationClientUri = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer()) + String registrationClientUri = UriComponentsBuilder.fromUriString(issuer) .path(this.providerSettings.getOidcClientRegistrationEndpoint()) .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .toUriString(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java index 6637f500..83c00f65 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java @@ -36,6 +36,7 @@ import org.springframework.util.Assert; */ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = Version.SERIAL_VERSION_UID; + private final String issuer; private final Authentication principal; private final OidcClientRegistration clientRegistration; private final String clientId; @@ -45,11 +46,14 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic * * @param principal the authenticated principal * @param clientRegistration the client registration + * @deprecated Use {@link #OidcClientRegistrationAuthenticationToken(String, Authentication, OidcClientRegistration)} instead */ + @Deprecated public OidcClientRegistrationAuthenticationToken(Authentication principal, OidcClientRegistration clientRegistration) { super(Collections.emptyList()); Assert.notNull(principal, "principal cannot be null"); Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + this.issuer = null; this.principal = principal; this.clientRegistration = clientRegistration; this.clientId = null; @@ -59,20 +63,53 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic /** * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters. * + * @param issuer the issuer identifier + * @param principal the authenticated principal + * @param clientRegistration the client registration + * @since 0.2.1 + */ + public OidcClientRegistrationAuthenticationToken(String issuer, Authentication principal, OidcClientRegistration clientRegistration) { + super(Collections.emptyList()); + Assert.hasText(issuer, "issuer cannot be empty"); + Assert.notNull(principal, "principal cannot be null"); + Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + this.issuer = issuer; + this.principal = principal; + this.clientRegistration = clientRegistration; + this.clientId = null; + setAuthenticated(principal.isAuthenticated()); + } + + /** + * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters. + * + * @param issuer the issuer identifier * @param principal the authenticated principal * @param clientId the client identifier * @since 0.2.1 */ - public OidcClientRegistrationAuthenticationToken(Authentication principal, String clientId) { + public OidcClientRegistrationAuthenticationToken(String issuer, Authentication principal, String clientId) { super(Collections.emptyList()); + Assert.hasText(issuer, "issuer cannot be empty"); Assert.notNull(principal, "principal cannot be null"); Assert.hasText(clientId, "clientId cannot be empty"); + this.issuer = issuer; this.principal = principal; this.clientRegistration = null; this.clientId = clientId; setAuthenticated(principal.isAuthenticated()); } + /** + * Returns the issuer identifier. + * + * @return the issuer identifier + * @since 0.2.1 + */ + public String getIssuer() { + return this.issuer; + } + @Override public Object getPrincipal() { return this.principal; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java index 7c4911f8..7e8a928a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java @@ -38,6 +38,7 @@ import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMe import org.springframework.security.oauth2.core.oidc.OidcClientRegistration; import org.springframework.security.oauth2.core.oidc.http.converter.OidcClientRegistrationHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -148,7 +149,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi if ("POST".equals(request.getMethod())) { OidcClientRegistration clientRegistration = this.clientRegistrationHttpMessageConverter.read( OidcClientRegistration.class, new ServletServerHttpRequest(request)); - return new OidcClientRegistrationAuthenticationToken(principal, clientRegistration); + + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + + return new OidcClientRegistrationAuthenticationToken(issuer, principal, clientRegistration); } // client_id (REQUIRED) @@ -158,7 +162,9 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } - return new OidcClientRegistrationAuthenticationToken(principal, clientId); + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + + return new OidcClientRegistrationAuthenticationToken(issuer, principal, clientId); } private void sendClientRegistrationResponse(HttpServletResponse response, HttpStatus httpStatus, OidcClientRegistration clientRegistration) throws IOException { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java index b865575d..5697d5f0 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java @@ -15,6 +15,13 @@ */ package org.springframework.security.oauth2.server.authorization.oidc.web; +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.ServletServerHttpResponse; @@ -26,18 +33,13 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.security.oauth2.core.oidc.http.converter.OidcProviderConfigurationHttpMessageConverter; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - /** * A {@code Filter} that processes OpenID Provider Configuration Requests. * @@ -76,13 +78,15 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques return; } + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder() - .issuer(this.providerSettings.getIssuer()) - .authorizationEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getAuthorizationEndpoint())) - .tokenEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getTokenEndpoint())) + .issuer(issuer) + .authorizationEndpoint(asUrl(issuer, this.providerSettings.getAuthorizationEndpoint())) + .tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint())) .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()) - .jwkSetUrl(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getJwkSetEndpoint())) + .jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint())) .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) @@ -100,4 +104,5 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques private static String asUrl(String issuer, String endpoint) { return UriComponentsBuilder.fromUriString(issuer).path(endpoint).build().toUriString(); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java index 97f13420..bf736ba1 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java @@ -33,6 +33,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthorizationServerMetadat import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType; import org.springframework.security.oauth2.core.http.converter.OAuth2AuthorizationServerMetadataHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; +import org.springframework.security.web.util.UrlUtils; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; @@ -43,6 +44,7 @@ import org.springframework.web.util.UriComponentsBuilder; * A {@code Filter} that processes OAuth 2.0 Authorization Server Metadata Requests. * * @author Daniel Garnier-Moiroux + * @author Joe Grandja * @since 0.1.1 * @see OAuth2AuthorizationServerMetadata * @see ProviderSettings @@ -72,24 +74,32 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // Resolve the current issuer identifier + String issuer = this.providerSettings.getIssuer(); + if (issuer == null) { + issuer = resolveIssuer(request); + } + // Set the current issuer identifier as a request attribute (for use by upstream components) + request.setAttribute(WebAttributes.ISSUER, issuer); + if (!this.requestMatcher.matches(request)) { filterChain.doFilter(request, response); return; } OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() - .issuer(this.providerSettings.getIssuer()) - .authorizationEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getAuthorizationEndpoint())) - .tokenEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getTokenEndpoint())) + .issuer(issuer) + .authorizationEndpoint(asUrl(issuer, this.providerSettings.getAuthorizationEndpoint())) + .tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint())) .tokenEndpointAuthenticationMethods(clientAuthenticationMethods()) - .jwkSetUrl(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getJwkSetEndpoint())) + .jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint())) .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) .grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue()) - .tokenRevocationEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getTokenRevocationEndpoint())) + .tokenRevocationEndpoint(asUrl(issuer, this.providerSettings.getTokenRevocationEndpoint())) .tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods()) - .tokenIntrospectionEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getTokenIntrospectionEndpoint())) + .tokenIntrospectionEndpoint(asUrl(issuer, this.providerSettings.getTokenIntrospectionEndpoint())) .tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods()) .codeChallengeMethod("plain") .codeChallengeMethod("S256") @@ -100,6 +110,17 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP authorizationServerMetadata, MediaType.APPLICATION_JSON, httpResponse); } + private static String resolveIssuer(HttpServletRequest request) { + // @formatter:off + return UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) + .replacePath(request.getContextPath()) + .replaceQuery(null) + .fragment(null) + .build() + .toUriString(); + // @formatter:on + } + private static Consumer> clientAuthenticationMethods() { return (authenticationMethods) -> { authenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/WebAttributes.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/WebAttributes.java new file mode 100644 index 00000000..3d837266 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/WebAttributes.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.oauth2.server.authorization.web; + +import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; + +/** + * Well-known attribute names which are used to store information in request or session scope. + * + * @author Joe Grandja + * @since 0.2.1 + */ +public final class WebAttributes { + + private WebAttributes() { + } + + /** + * The {@link javax.servlet.http.HttpServletRequest#getAttribute(String) request attribute} name that holds the current issuer identifier. + * The issuer identifier is resolved from {@link ProviderSettings#getIssuer()} or dynamically from the current {@link javax.servlet.http.HttpServletRequest}. + */ + public static final String ISSUER = WebAttributes.class.getName().concat(".ISSUER"); + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java index bd534891..ec222c29 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java @@ -28,6 +28,7 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -88,8 +89,10 @@ public final class OAuth2AuthorizationCodeAuthenticationConverter implements Aut } }); + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + return new OAuth2AuthorizationCodeAuthenticationToken( - code, clientPrincipal, redirectUri, additionalParameters); + issuer, code, clientPrincipal, redirectUri, additionalParameters); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java index 5bf12514..73d80e06 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java @@ -31,6 +31,7 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -83,7 +84,10 @@ public final class OAuth2ClientCredentialsAuthenticationConverter implements Aut } }); + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + return new OAuth2ClientCredentialsAuthenticationToken( - clientPrincipal, requestedScopes, additionalParameters); + issuer, clientPrincipal, requestedScopes, additionalParameters); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java index 20882163..7f08a11b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java @@ -31,6 +31,7 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -94,7 +95,10 @@ public final class OAuth2RefreshTokenAuthenticationConverter implements Authenti } }); + String issuer = (String) request.getAttribute(WebAttributes.ISSUER); + return new OAuth2RefreshTokenAuthenticationToken( - refreshToken, clientPrincipal, requestedScopes, additionalParameters); + issuer, refreshToken, clientPrincipal, requestedScopes, additionalParameters); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientCredentialsGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientCredentialsGrantTests.java index 0a7a44c8..a50644eb 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientCredentialsGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientCredentialsGrantTests.java @@ -213,10 +213,11 @@ public class OAuth2ClientCredentialsGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); this.registeredClientRepository.save(registeredClient); + String issuer = "https://example.com/issuer1"; OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, null, null); when(authenticationConverter.convert(any())).thenReturn(clientCredentialsAuthentication); OAuth2AccessToken accessToken = new OAuth2AccessToken( diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JwtEncodingContextTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JwtEncodingContextTests.java index f6eefdef..08f40abc 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JwtEncodingContextTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JwtEncodingContextTests.java @@ -88,9 +88,10 @@ public class JwtEncodingContextTests { registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); + String issuer = "https://provider.com"; OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = new OAuth2AuthorizationCodeAuthenticationToken( - "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + issuer, "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); JwtEncodingContext context = JwtEncodingContext.with(headers, claims) .registeredClient(registeredClient) diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java index a641f4e2..f3147233 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java @@ -34,6 +34,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2AuthorizationCode; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2TokenType; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; @@ -48,7 +49,6 @@ import org.springframework.security.oauth2.jwt.JwtClaimsSet; import org.springframework.security.oauth2.jwt.JwtEncoder; import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; -import org.springframework.security.oauth2.core.OAuth2AuthorizationCode; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; @@ -74,6 +74,7 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationCodeAuthenticationProviderTests { + private static final String ISSUER = "https://example.com/issuer1"; private static final String AUTHORIZATION_CODE = "code"; private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); private OAuth2AuthorizationService authorizationService; @@ -130,7 +131,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( registeredClient.getClientId(), registeredClient.getClientSecret()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) @@ -144,7 +145,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) @@ -158,7 +159,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) @@ -176,7 +177,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) @@ -203,7 +204,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri() + "-invalid", null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri() + "-invalid", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) @@ -227,7 +228,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -253,7 +254,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -274,7 +275,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt()); @@ -330,7 +331,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt()); @@ -404,7 +405,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt()); @@ -467,7 +468,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); Instant accessTokenIssuedAt = Instant.now(); Instant accessTokenExpiresAt = accessTokenIssuedAt.plus(accessTokenTTL); @@ -506,7 +507,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt()); @@ -539,7 +540,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( OAuth2AuthorizationRequest.class.getName()); OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + new OAuth2AuthorizationCodeAuthenticationToken(ISSUER, AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java index 9df894c4..6847f34e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java @@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationCodeAuthenticationTokenTests { + private String issuer = "https://example.com/issuer1"; private String code = "code"; private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( @@ -42,16 +43,23 @@ public class OAuth2AuthorizationCodeAuthenticationTokenTests { private String redirectUri = "redirectUri"; private Map additionalParameters = Collections.singletonMap("param1", "value1"); + @Test + public void constructorWhenIssuerNullThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.code, this.clientPrincipal, this.redirectUri, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("issuer cannot be empty"); + } + @Test public void constructorWhenCodeNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.clientPrincipal, this.redirectUri, null)) + assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.issuer, null, this.clientPrincipal, this.redirectUri, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("code cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.code, null, this.redirectUri, null)) + assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.issuer, this.code, null, this.redirectUri, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("clientPrincipal cannot be null"); } @@ -59,8 +67,9 @@ public class OAuth2AuthorizationCodeAuthenticationTokenTests { @Test public void constructorWhenClientPrincipalProvidedThenCreated() { OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( - this.code, this.clientPrincipal, this.redirectUri, this.additionalParameters); + this.issuer, this.code, this.clientPrincipal, this.redirectUri, this.additionalParameters); assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getCode()).isEqualTo(this.code); @@ -71,7 +80,7 @@ public class OAuth2AuthorizationCodeAuthenticationTokenTests { @Test public void getAdditionalParametersWhenUpdateThenThrowUnsupportedOperationException() { OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( - this.code, this.clientPrincipal, this.redirectUri, this.additionalParameters); + this.issuer, this.code, this.clientPrincipal, this.redirectUri, this.additionalParameters); assertThatThrownBy(() -> authentication.getAdditionalParameters().put("another_key", 1)) .isInstanceOf(UnsupportedOperationException.class); assertThatThrownBy(() -> authentication.getAdditionalParameters().remove("some_key")) diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java index c02321c4..341a9694 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java @@ -36,12 +36,12 @@ import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; import org.springframework.security.oauth2.jwt.JoseHeaderNames; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; -import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; -import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -105,11 +105,12 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( registeredClient.getClientId(), registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -120,11 +121,12 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -135,13 +137,14 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenClientNotAuthorizedToRequestTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2() .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS)) .build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -152,11 +155,12 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenInvalidScopeThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( - clientPrincipal, Collections.singleton("invalid-scope"), null); + issuer, clientPrincipal, Collections.singleton("invalid-scope"), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -167,12 +171,13 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenScopeRequestedThenAccessTokenContainsScope() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); Set requestedScope = Collections.singleton("scope1"); OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScope, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, requestedScope, null); when(this.jwtEncoder.encode(any(), any())) .thenReturn(createJwt(Collections.singleton("mapped-scoped"))); @@ -184,11 +189,12 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenValidAuthenticationThenReturnAccessToken() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + new OAuth2ClientCredentialsAuthenticationToken(issuer, clientPrincipal, null, null); when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt(registeredClient.getScopes())); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java index 34b97c02..64b64287 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java @@ -35,15 +35,23 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Alexey Nesterov */ public class OAuth2ClientCredentialsAuthenticationTokenTests { + private String issuer = "https://example.com/issuer1"; private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); private final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); private Set scopes = Collections.singleton("scope1"); private Map additionalParameters = Collections.singletonMap("param1", "value1"); + @Test + public void constructorWhenIssuerNullThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null, this.clientPrincipal, this.scopes, this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("issuer cannot be empty"); + } + @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null, this.scopes, this.additionalParameters)) + assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(this.issuer, null, this.scopes, this.additionalParameters)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("clientPrincipal cannot be null"); } @@ -51,9 +59,10 @@ public class OAuth2ClientCredentialsAuthenticationTokenTests { @Test public void constructorWhenClientPrincipalProvidedThenCreated() { OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( - this.clientPrincipal, this.scopes, this.additionalParameters); + this.issuer, this.clientPrincipal, this.scopes, this.additionalParameters); assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getScopes()).isEqualTo(this.scopes); @@ -65,9 +74,10 @@ public class OAuth2ClientCredentialsAuthenticationTokenTests { Set expectedScopes = Collections.singleton("test-scope"); OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( - this.clientPrincipal, expectedScopes, this.additionalParameters); + this.issuer, this.clientPrincipal, expectedScopes, this.additionalParameters); assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getScopes()).isEqualTo(expectedScopes); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java index 08c90677..91e3a39c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java @@ -132,6 +132,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenValidRefreshTokenThenReturnAccessToken() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken( @@ -142,7 +143,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -176,6 +177,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenValidRefreshTokenThenReturnIdToken() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken( @@ -186,7 +188,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -243,6 +245,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenReuseRefreshTokensFalseThenReturnNewRefreshToken() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient() .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) .build(); @@ -255,7 +258,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -270,6 +273,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenRequestedScopesAuthorizedThenAccessTokenIncludesScopes() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient() .scope("scope2") .scope("scope3") @@ -286,7 +290,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { Set requestedScopes = new HashSet<>(authorizedScopes); requestedScopes.remove("scope1"); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -296,6 +300,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenCustomRefreshTokenGeneratorThenUsed() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient() .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) .build(); @@ -317,7 +322,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -328,6 +333,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken( @@ -341,7 +347,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { Set requestedScopes = new HashSet<>(authorizedScopes); requestedScopes.add("unauthorized"); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -352,11 +358,12 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenInvalidRefreshTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - "invalid", clientPrincipal, null, null); + issuer, "invalid", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -367,11 +374,12 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( registeredClient.getClientId(), registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - "refresh-token", clientPrincipal, null, null); + issuer, "refresh-token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -382,11 +390,12 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - "refresh-token", clientPrincipal, null, null); + issuer, "refresh-token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -397,6 +406,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenRefreshTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken( @@ -408,7 +418,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient2, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient2.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -419,6 +429,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenClientNotAuthorizedToRefreshTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient() .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN)) .build(); @@ -431,7 +442,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -442,6 +453,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenExpiredRefreshTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); OAuth2RefreshToken expiredRefreshToken = new OAuth2RefreshToken( @@ -455,7 +467,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -466,6 +478,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenRevokedRefreshTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( "refresh-token", Instant.now().minusSeconds(120), Instant.now().plusSeconds(1000)); @@ -480,7 +493,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); + issuer, authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java index 095b2d1a..c34cb7e8 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java @@ -36,25 +36,33 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @since 0.0.3 */ public class OAuth2RefreshTokenAuthenticationTokenTests { + private String issuer = "https://example.com/issuer1"; private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); private Set scopes = Collections.singleton("scope1"); private Map additionalParameters = Collections.singletonMap("param1", "value1"); + @Test + public void constructorWhenIssuerNullThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, "refresh-token", this.clientPrincipal, this.scopes, this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("issuer cannot be empty"); + } + @Test public void constructorWhenRefreshTokenNullOrEmptyThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, this.clientPrincipal, this.scopes, this.additionalParameters)) + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(this.issuer, null, this.clientPrincipal, this.scopes, this.additionalParameters)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("refreshToken cannot be empty"); - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", this.clientPrincipal, this.scopes, this.additionalParameters)) + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(this.issuer, "", this.clientPrincipal, this.scopes, this.additionalParameters)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("refreshToken cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("refresh-token", null, this.scopes, this.additionalParameters)) + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(this.issuer, "refresh-token", null, this.scopes, this.additionalParameters)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("clientPrincipal cannot be null"); } @@ -62,8 +70,9 @@ public class OAuth2RefreshTokenAuthenticationTokenTests { @Test public void constructorWhenScopesProvidedThenCreated() { OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - "refresh-token", this.clientPrincipal, this.scopes, this.additionalParameters); + this.issuer, "refresh-token", this.clientPrincipal, this.scopes, this.additionalParameters); assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getRefreshToken()).isEqualTo("refresh-token"); assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); assertThat(authentication.getCredentials().toString()).isEmpty(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java index db9368dd..b2640d8a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java @@ -83,7 +83,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { this.registeredClientRepository = mock(RegisteredClientRepository.class); this.authorizationService = mock(OAuth2AuthorizationService.class); this.jwtEncoder = mock(JwtEncoder.class); - this.providerSettings = ProviderSettings.builder().issuer("https://auth-server:9000").build(); + this.providerSettings = ProviderSettings.builder().build(); this.authenticationProvider = new OidcClientRegistrationAuthenticationProvider( this.registeredClientRepository, this.authorizationService, this.jwtEncoder); this.authenticationProvider.setProviderSettings(this.providerSettings); @@ -117,13 +117,14 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com") .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -133,13 +134,14 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; JwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientRegistration()); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com") .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -149,6 +151,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientRegistration(); JwtAuthenticationToken principal = new JwtAuthenticationToken( jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); @@ -157,7 +160,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -169,6 +172,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -188,7 +192,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -200,6 +204,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientRegistrationRequestAndAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt(Collections.singleton("unauthorized.scope")); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -218,7 +223,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -230,6 +235,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientRegistrationRequestAndAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.create", "scope1"))); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -248,7 +254,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -260,6 +266,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientRegistrationRequestAndInvalidRedirectUriThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -280,7 +287,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { // @formatter:on OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -292,6 +299,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientRegistrationRequestAndRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -312,7 +320,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { // @formatter:on OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -324,6 +332,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientRegistrationRequestAndValidAccessTokenThenReturnClientRegistration() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -350,7 +359,8 @@ public class OidcClientRegistrationAuthenticationProviderTests { // @formatter:on OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, clientRegistration); + issuer, principal, clientRegistration); + OidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -415,7 +425,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm()) .isEqualTo(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); - String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer()) + String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(issuer) .path(this.providerSettings.getOidcClientRegistrationEndpoint()) .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClientResult.getClientId()).toUriString(); @@ -425,6 +435,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientConfigurationRequestAndAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt(Collections.singleton("unauthorized.scope")); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -440,7 +451,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { jwt, AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, registeredClient.getClientId()); + issuer, principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -452,6 +463,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientConfigurationRequestAndAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.read", "scope1"))); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -467,7 +479,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read", "SCOPE_scope1")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, registeredClient.getClientId()); + issuer, principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -479,6 +491,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientConfigurationRequestAndRegisteredClientNotFoundThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -494,7 +507,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, registeredClient.getClientId()); + issuer, principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -508,6 +521,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientConfigurationRequestClientIdNotEqualToAuthorizedClientThenThrowOAuth2AuthenticationException() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -527,7 +541,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, registeredClient.getClientId()); + issuer, principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) .isInstanceOf(OAuth2AuthenticationException.class) @@ -541,6 +555,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void authenticateWhenClientConfigurationRequestAndValidAccessTokenThenReturnClientRegistration() { + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), @@ -560,7 +575,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - principal, registeredClient.getClientId()); + issuer, principal, registeredClient.getClientId()); OidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -597,7 +612,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm()) .isEqualTo(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); - String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer()) + String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(issuer) .path(this.providerSettings.getOidcClientRegistrationEndpoint()) .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()).toUriString(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java index 7d1f83f6..3bfe578a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java @@ -29,43 +29,52 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Joe Grandja */ public class OidcClientRegistrationAuthenticationTokenTests { + private String issuer = "https://example.com/issuer1"; private TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); private OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com").build(); + @Test + public void constructorWhenIssuerNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(null, this.principal, this.clientRegistration)) + .withMessage("issuer cannot be empty"); + } + @Test public void constructorWhenPrincipalNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(null, this.clientRegistration)) + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.issuer, null, this.clientRegistration)) .withMessage("principal cannot be null"); } @Test public void constructorWhenClientRegistrationNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (OidcClientRegistration) null)) + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.issuer, this.principal, (OidcClientRegistration) null)) .withMessage("clientRegistration cannot be null"); } @Test public void constructorWhenClientIdNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (String) null)) + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.issuer, this.principal, (String) null)) .withMessage("clientId cannot be empty"); } @Test public void constructorWhenClientIdEmptyThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, "")) + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.issuer, this.principal, "")) .withMessage("clientId cannot be empty"); } @Test public void constructorWhenOidcClientRegistrationProvidedThenCreated() { OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - this.principal, this.clientRegistration); + this.issuer, this.principal, this.clientRegistration); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getPrincipal()).isEqualTo(this.principal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration); @@ -76,8 +85,9 @@ public class OidcClientRegistrationAuthenticationTokenTests { @Test public void constructorWhenClientIdProvidedThenCreated() { OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( - this.principal, "client-1"); + this.issuer, this.principal, "client-1"); + assertThat(authentication.getIssuer()).isEqualTo(this.issuer); assertThat(authentication.getPrincipal()).isEqualTo(this.principal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getClientRegistration()).isNull(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java index 9bb78136..4f71b8fa 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java @@ -53,6 +53,7 @@ import org.springframework.security.oauth2.jwt.JwtClaimsSet; import org.springframework.security.oauth2.jwt.TestJoseHeaders; import org.springframework.security.oauth2.jwt.TestJwtClaimsSets; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import static org.assertj.core.api.Assertions.assertThat; @@ -188,6 +189,7 @@ public class OidcClientRegistrationEndpointFilterTests { MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri); request.setServletPath(requestUri); writeClientRegistrationRequest(request, clientRegistrationRequest); + request.setAttribute(WebAttributes.ISSUER, "https://example.com/issuer1"); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -225,12 +227,13 @@ public class OidcClientRegistrationEndpointFilterTests { .build(); // @formatter:on + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt("client.create"); JwtAuthenticationToken principal = new JwtAuthenticationToken( jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = - new OidcClientRegistrationAuthenticationToken(principal, expectedClientRegistrationResponse); + new OidcClientRegistrationAuthenticationToken(issuer, principal, expectedClientRegistrationResponse); when(this.authenticationManager.authenticate(any())).thenReturn(clientRegistrationAuthenticationResult); @@ -242,6 +245,7 @@ public class OidcClientRegistrationEndpointFilterTests { MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri); request.setServletPath(requestUri); writeClientRegistrationRequest(request, clientRegistrationRequest); + request.setAttribute(WebAttributes.ISSUER, issuer); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -370,6 +374,7 @@ public class OidcClientRegistrationEndpointFilterTests { MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1"); + request.setAttribute(WebAttributes.ISSUER, "https://example.com/issuer1"); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -402,12 +407,13 @@ public class OidcClientRegistrationEndpointFilterTests { .build(); // @formatter:on + String issuer = "https://example.com/issuer1"; Jwt jwt = createJwt("client.read"); JwtAuthenticationToken principal = new JwtAuthenticationToken( jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken clientConfigurationAuthenticationResult = - new OidcClientRegistrationAuthenticationToken(principal, expectedClientRegistrationResponse); + new OidcClientRegistrationAuthenticationToken(issuer, principal, expectedClientRegistrationResponse); when(this.authenticationManager.authenticate(any())).thenReturn(clientConfigurationAuthenticationResult); @@ -419,6 +425,7 @@ public class OidcClientRegistrationEndpointFilterTests { MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); request.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId()); + request.setAttribute(WebAttributes.ISSUER, issuer); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java index d20ffe9c..07aeac9b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java @@ -25,6 +25,7 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; +import org.springframework.security.oauth2.server.authorization.web.WebAttributes; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -98,6 +99,7 @@ public class OidcProviderConfigurationEndpointFilterTests { String requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); + request.setAttribute(WebAttributes.ISSUER, providerSettings.getIssuer()); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -130,6 +132,7 @@ public class OidcProviderConfigurationEndpointFilterTests { String requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); + request.setAttribute(WebAttributes.ISSUER, providerSettings.getIssuer()); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java index 8874dd54..26dbe70f 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java @@ -146,4 +146,36 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { .withMessage("issuer must be a valid URL"); } + @Test + public void doFilterWhenProviderSettingsWithIssuerNotSetThenIssuerResolvesFromRequest() throws Exception { + ProviderSettings providerSettings = ProviderSettings.builder().build(); + OAuth2AuthorizationServerMetadataEndpointFilter filter = + new OAuth2AuthorizationServerMetadataEndpointFilter(providerSettings); + + String requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI; + MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); + request.setServletPath(requestUri); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = mock(FilterChain.class); + + filter.doFilter(request, response, filterChain); + + verifyNoInteractions(filterChain); + + assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE); + String authorizationServerMetadataResponse = response.getContentAsString(); + assertThat(authorizationServerMetadataResponse).contains("\"issuer\":\"http://localhost\""); + assertThat(authorizationServerMetadataResponse).contains("\"authorization_endpoint\":\"http://localhost/oauth2/authorize\""); + assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint\":\"http://localhost/oauth2/token\""); + assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\"]"); + assertThat(authorizationServerMetadataResponse).contains("\"jwks_uri\":\"http://localhost/oauth2/jwks\""); + assertThat(authorizationServerMetadataResponse).contains("\"response_types_supported\":[\"code\"]"); + assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]"); + assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint\":\"http://localhost/oauth2/revoke\""); + assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\"]"); + assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint\":\"http://localhost/oauth2/introspect\""); + assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\"]"); + assertThat(authorizationServerMetadataResponse).contains("\"code_challenge_methods_supported\":[\"plain\",\"S256\"]"); + } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java index 73529b58..1102211c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java @@ -489,12 +489,13 @@ public class OAuth2TokenEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { + String issuer = "https://example.com/issuer1"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = - new OAuth2AuthorizationCodeAuthenticationToken("code", clientPrincipal, null, null); + new OAuth2AuthorizationCodeAuthenticationToken(issuer, "code", clientPrincipal, null, null); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(authorizationCodeAuthentication); @@ -613,6 +614,8 @@ public class OAuth2TokenEndpointFilterTests { request.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); request.addParameter("custom-param-1", "custom-value-1"); + request.setAttribute(WebAttributes.ISSUER, "https://example.com/issuer1"); + return request; } @@ -627,6 +630,8 @@ public class OAuth2TokenEndpointFilterTests { StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " ")); request.addParameter("custom-param-1", "custom-value-1"); + request.setAttribute(WebAttributes.ISSUER, "https://example.com/issuer1"); + return request; } @@ -642,6 +647,8 @@ public class OAuth2TokenEndpointFilterTests { StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " ")); request.addParameter("custom-param-1", "custom-value-1"); + request.setAttribute(WebAttributes.ISSUER, "https://example.com/issuer1"); + return request; } } diff --git a/samples/custom-consent-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java b/samples/custom-consent-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java index d7064bb4..5c910452 100644 --- a/samples/custom-consent-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java +++ b/samples/custom-consent-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java @@ -39,7 +39,6 @@ import org.springframework.security.oauth2.server.authorization.client.InMemoryR import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.config.ClientSettings; -import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -101,11 +100,6 @@ public class AuthorizationServerConfig { return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); } - @Bean - public ProviderSettings providerSettings() { - return ProviderSettings.builder().issuer("http://auth-server:9000").build(); - } - @Bean public OAuth2AuthorizationConsentService authorizationConsentService() { // Will be used by the ConsentController diff --git a/samples/default-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java b/samples/default-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java index 70e706d1..ee796c54 100644 --- a/samples/default-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java +++ b/samples/default-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java @@ -45,7 +45,6 @@ import org.springframework.security.oauth2.server.authorization.client.JdbcRegis import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.config.ClientSettings; -import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; import org.springframework.security.web.SecurityFilterChain; /** @@ -105,11 +104,6 @@ public class AuthorizationServerConfig { return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); } - @Bean - public ProviderSettings providerSettings() { - return ProviderSettings.builder().issuer("http://auth-server:9000").build(); - } - @Bean public EmbeddedDatabase embeddedDatabase() { // @formatter:off