Allow customization of redirect strategy
The default redirect strategy will provide authorization redirect URI within HTTP 302 response Location header. Allowing the configuration of custom redirect strategy will provide an option for the clients to obtain the authorization URI from e.g. HTTP response body as JSON payload, without a need to handle automatic redirection initiated by the HTTP Location header. Closes gh-11373
This commit is contained in:
parent
c9f8d2b111
commit
efaee4e56b
|
@ -34,6 +34,7 @@ import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequest
|
|||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -171,6 +172,8 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private AuthorizationCodeGrantConfigurer() {
|
||||
|
@ -202,6 +205,17 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
|
||||
*/
|
||||
public AuthorizationCodeGrantConfigurer authorizationRedirectStrategy(
|
||||
RedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client used for requesting the access token credential from the Token
|
||||
* Endpoint.
|
||||
|
@ -247,6 +261,9 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
authorizationRequestRedirectFilter
|
||||
.setAuthorizationRequestRepository(this.authorizationRequestRepository);
|
||||
}
|
||||
if (this.authorizationRedirectStrategy != null) {
|
||||
authorizationRequestRedirectFilter.setAuthorizationRedirectStrategy(this.authorizationRedirectStrategy);
|
||||
}
|
||||
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestRedirectFilter.setRequestCache(requestCache);
|
||||
|
|
|
@ -68,6 +68,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
|||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
|
@ -368,6 +369,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
authorizationRequestFilter
|
||||
.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
}
|
||||
if (this.authorizationEndpointConfig.authorizationRedirectStrategy != null) {
|
||||
authorizationRequestFilter
|
||||
.setAuthorizationRedirectStrategy(this.authorizationEndpointConfig.authorizationRedirectStrategy);
|
||||
}
|
||||
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestFilter.setRequestCache(requestCache);
|
||||
|
@ -540,6 +545,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private AuthorizationEndpointConfig() {
|
||||
}
|
||||
|
||||
|
@ -582,6 +589,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link AuthorizationEndpointConfig} for further configuration
|
||||
*/
|
||||
public AuthorizationEndpointConfig authorizationRedirectStrategy(
|
||||
RedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
|
||||
* @return the {@link OAuth2LoginConfigurer}
|
||||
|
|
|
@ -44,6 +44,8 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
|
||||
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
|
||||
private final BeanReference requestCache;
|
||||
|
@ -87,6 +89,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(
|
||||
authorizationCodeGrantElt);
|
||||
BeanMetadataElement authorizationRedirectStrategy = getAuthorizationRedirectStrategy(authorizationCodeGrantElt);
|
||||
BeanDefinitionBuilder authorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);
|
||||
String authorizationRequestResolverRef = (authorizationCodeGrantElt != null)
|
||||
|
@ -99,6 +102,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("authorizationRedirectStrategy", authorizationRedirectStrategy)
|
||||
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
|
||||
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
||||
|
@ -130,6 +134,16 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
|
||||
String authorizationRedirectStrategyRef = (element != null)
|
||||
? element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF) : null;
|
||||
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
|
||||
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAccessTokenResponseClient(Element element) {
|
||||
String accessTokenResponseClientRef = (element != null)
|
||||
? element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null;
|
||||
|
|
|
@ -87,6 +87,8 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
|
||||
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
|
||||
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
|
||||
private static final String ATT_USER_AUTHORITIES_MAPPER_REF = "user-authorities-mapper-ref";
|
||||
|
@ -203,6 +205,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||
}
|
||||
oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("authorizationRedirectStrategy", getAuthorizationRedirectStrategy(element))
|
||||
.addPropertyValue("requestCache", this.requestCache);
|
||||
this.oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.getBeanDefinition();
|
||||
|
@ -267,6 +270,15 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
|
||||
String authorizationRedirectStrategyRef = element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF);
|
||||
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
|
||||
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanDefinition getOidcAuthProvider(Element element, BeanMetadataElement accessTokenResponseClient,
|
||||
String userAuthoritiesMapperRef) {
|
||||
boolean oidcAuthenticationProviderEnabled = ClassUtils
|
||||
|
|
|
@ -102,12 +102,14 @@ import org.springframework.security.oauth2.server.resource.web.server.ServerBear
|
|||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
|
||||
import org.springframework.security.web.server.ExchangeMatcherRedirectWebFilter;
|
||||
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||
|
@ -3375,6 +3377,8 @@ public class ServerHttpSecurity {
|
|||
|
||||
private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private ServerWebExchangeMatcher authenticationMatcher;
|
||||
|
||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
@ -3547,6 +3551,16 @@ public class ServerHttpSecurity {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link OAuth2LoginSpec} for further configuration
|
||||
*/
|
||||
public OAuth2LoginSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ServerWebExchangeMatcher matcher} used for determining if the
|
||||
* request is an authentication request.
|
||||
|
@ -3581,7 +3595,9 @@ public class ServerHttpSecurity {
|
|||
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();
|
||||
ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = getAuthorizationRequestRepository();
|
||||
oauthRedirectFilter.setAuthorizationRequestRepository(authorizationRequestRepository);
|
||||
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
|
||||
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
|
||||
|
||||
ReactiveAuthenticationManager manager = getAuthenticationManager();
|
||||
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager,
|
||||
authorizedClientRepository);
|
||||
|
@ -3591,6 +3607,7 @@ public class ServerHttpSecurity {
|
|||
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
|
||||
authenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
|
||||
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
|
||||
|
||||
setDefaultEntryPoints(http);
|
||||
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
|
||||
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
|
@ -3737,6 +3754,13 @@ public class ServerHttpSecurity {
|
|||
return this.authorizationRequestRepository;
|
||||
}
|
||||
|
||||
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
|
||||
if (this.authorizationRedirectStrategy == null) {
|
||||
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
}
|
||||
return this.authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {
|
||||
ReactiveOAuth2AuthorizedClientService bean = getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);
|
||||
if (bean != null) {
|
||||
|
@ -3759,6 +3783,8 @@ public class ServerHttpSecurity {
|
|||
|
||||
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private OAuth2ClientSpec() {
|
||||
}
|
||||
|
||||
|
@ -3851,6 +3877,23 @@ public class ServerHttpSecurity {
|
|||
return this.authorizationRequestRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
* @return the {@link OAuth2ClientSpec} for further configuration
|
||||
*/
|
||||
public OAuth2ClientSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
|
||||
if (this.authorizationRedirectStrategy == null) {
|
||||
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
}
|
||||
return this.authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
|
||||
* @return the {@link ServerHttpSecurity} to continue configuring
|
||||
|
@ -3870,12 +3913,15 @@ public class ServerHttpSecurity {
|
|||
if (http.requestCache != null) {
|
||||
codeGrantWebFilter.setRequestCache(http.requestCache.requestCache);
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(
|
||||
clientRegistrationRepository);
|
||||
oauthRedirectFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());
|
||||
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
|
||||
if (http.requestCache != null) {
|
||||
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
|
||||
}
|
||||
|
||||
http.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.OAUTH2_AUTHORIZATION_CODE);
|
||||
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
|||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
|
||||
|
@ -37,6 +38,7 @@ import org.springframework.web.server.ServerWebExchange
|
|||
* @property clientRegistrationRepository the repository of client registrations.
|
||||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
*/
|
||||
@ServerSecurityMarker
|
||||
class ServerOAuth2ClientDsl {
|
||||
|
@ -45,6 +47,7 @@ class ServerOAuth2ClientDsl {
|
|||
var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null
|
||||
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
|
||||
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
|
||||
|
||||
internal fun get(): (ServerHttpSecurity.OAuth2ClientSpec) -> Unit {
|
||||
return { oauth2Client ->
|
||||
|
@ -53,6 +56,7 @@ class ServerOAuth2ClientDsl {
|
|||
clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }
|
||||
authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizationRequestRepository?.also { oauth2Client.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { oauth2Client.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.security.oauth2.client.web.server.ServerAuthorization
|
|||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
||||
|
@ -49,6 +50,7 @@ import org.springframework.web.server.ServerWebExchange
|
|||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @property authenticationMatcher the [ServerWebExchangeMatcher] used for determining if the request is an
|
||||
* authentication request.
|
||||
*/
|
||||
|
@ -64,6 +66,7 @@ class ServerOAuth2LoginDsl {
|
|||
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
|
||||
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRequestResolver: ServerOAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
|
||||
var authenticationMatcher: ServerWebExchangeMatcher? = null
|
||||
|
||||
internal fun get(): (ServerHttpSecurity.OAuth2LoginSpec) -> Unit {
|
||||
|
@ -78,6 +81,7 @@ class ServerOAuth2LoginDsl {
|
|||
authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizationRequestRepository?.also { oauth2Login.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRequestResolver?.also { oauth2Login.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRedirectStrategy?.also { oauth2Login.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
authenticationMatcher?.also { oauth2Login.authenticationMatcher(authenticationMatcher) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCo
|
|||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure OAuth 2.0 Authorization Code Grant.
|
||||
|
@ -31,6 +32,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
|||
* @since 5.3
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @property accessTokenResponseClient the client used for requesting the access token credential
|
||||
* from the Token Endpoint.
|
||||
*/
|
||||
|
@ -38,12 +40,14 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
|||
class AuthorizationCodeGrantDsl {
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: RedirectStrategy? = null
|
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null
|
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit {
|
||||
return { authorizationCodeGrant ->
|
||||
authorizationRequestResolver?.also { authorizationCodeGrant.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationCodeGrant.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { authorizationCodeGrant.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
accessTokenResponseClient?.also { authorizationCodeGrant.accessTokenResponseClient(accessTokenResponseClient) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
|
|||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's Authorization Endpoint using
|
||||
|
@ -31,18 +32,21 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequ
|
|||
* @property baseUri the base URI used for authorization requests.
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
|
||||
*/
|
||||
@OAuth2LoginSecurityMarker
|
||||
class AuthorizationEndpointDsl {
|
||||
var baseUri: String? = null
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var authorizationRedirectStrategy: RedirectStrategy? = null
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit {
|
||||
return { authorizationEndpoint ->
|
||||
baseUri?.also { authorizationEndpoint.baseUri(baseUri) }
|
||||
authorizationRequestResolver?.also { authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationEndpoint.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
authorizationRedirectStrategy?.also { authorizationEndpoint.authorizationRedirectStrategy(authorizationRedirectStrategy) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -500,6 +500,9 @@ oauth2-login.attlist &=
|
|||
oauth2-login.attlist &=
|
||||
## Reference to the OAuth2AuthorizationRequestResolver
|
||||
attribute authorization-request-resolver-ref {xsd:token}?
|
||||
oauth2-login.attlist &=
|
||||
## Reference to the authorization RedirectStrategy
|
||||
attribute authorization-redirect-strategy-ref {xsd:token}?
|
||||
oauth2-login.attlist &=
|
||||
## Reference to the OAuth2AccessTokenResponseClient
|
||||
attribute access-token-response-client-ref {xsd:token}?
|
||||
|
@ -547,6 +550,9 @@ authorization-code-grant =
|
|||
authorization-code-grant.attlist &=
|
||||
## Reference to the AuthorizationRequestRepository
|
||||
attribute authorization-request-repository-ref {xsd:token}?
|
||||
authorization-code-grant.attlist &=
|
||||
## Reference to the authorization RedirectStrategy
|
||||
attribute authorization-redirect-strategy-ref {xsd:token}?
|
||||
authorization-code-grant.attlist &=
|
||||
## Reference to the OAuth2AuthorizationRequestResolver
|
||||
attribute authorization-request-resolver-ref {xsd:token}?
|
||||
|
|
|
@ -1651,6 +1651,12 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-redirect-strategy-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the authorization RedirectStrategy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-token-response-client-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the OAuth2AccessTokenResponseClient
|
||||
|
@ -1754,6 +1760,12 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-redirect-strategy-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the authorization RedirectStrategy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-request-resolver-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Reference to the OAuth2AuthorizationRequestResolver
|
||||
|
|
|
@ -59,6 +59,8 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
@ -69,6 +71,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -96,6 +99,8 @@ public class OAuth2ClientConfigurerTests {
|
|||
|
||||
private static OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
private static RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
private static OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private static RequestCache requestCache;
|
||||
|
@ -131,6 +136,7 @@ public class OAuth2ClientConfigurerTests {
|
|||
authorizedClientService);
|
||||
authorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
|
||||
"/oauth2/authorization");
|
||||
authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234")
|
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER).expiresIn(300).build();
|
||||
accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
|
||||
|
@ -262,6 +268,19 @@ public class OAuth2ClientConfigurerTests {
|
|||
verify(authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenCustomAuthorizationRedirectStrategySetThenAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
authorizationRedirectStrategy = mock(RedirectStrategy.class);
|
||||
this.spring.register(OAuth2ClientConfig.class).autowire();
|
||||
// @formatter:off
|
||||
this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
// @formatter:on
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
static class OAuth2ClientConfig extends WebSecurityConfigurerAdapter {
|
||||
|
@ -279,6 +298,7 @@ public class OAuth2ClientConfigurerTests {
|
|||
.oauth2Client()
|
||||
.authorizationCodeGrant()
|
||||
.authorizationRequestResolver(authorizationRequestResolver)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy)
|
||||
.accessTokenResponseClient(accessTokenResponseClient);
|
||||
// @formatter:on
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoder;
|
|||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
|
@ -101,7 +102,9 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -382,6 +385,32 @@ public class OAuth2LoginConfigurerTests {
|
|||
"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oauth2LoginWithAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
loadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class);
|
||||
RedirectStrategy redirectStrategy = this.context
|
||||
.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class).redirectStrategy;
|
||||
String requestUri = "/oauth2/authorization/google";
|
||||
this.request = new MockHttpServletRequest("GET", requestUri);
|
||||
this.request.setServletPath(requestUri);
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
|
||||
then(redirectStrategy).should().sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenOauth2LoginWithCustomAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
loadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class);
|
||||
RedirectStrategy redirectStrategy = this.context
|
||||
.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class).redirectStrategy;
|
||||
String requestUri = "/oauth2/authorization/google";
|
||||
this.request = new MockHttpServletRequest("GET", requestUri);
|
||||
this.request.setServletPath(requestUri);
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
|
||||
then(redirectStrategy).should().sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() throws Exception {
|
||||
|
@ -883,6 +912,59 @@ public class OAuth2LoginConfigurerTests {
|
|||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigCustomAuthorizationRedirectStrategy extends CommonWebSecurityConfigurerAdapter {
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(
|
||||
GOOGLE_CLIENT_REGISTRATION);
|
||||
|
||||
RedirectStrategy redirectStrategy = mock(RedirectStrategy.class);
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.oauth2Login((oauth2Login) ->
|
||||
oauth2Login
|
||||
.clientRegistrationRepository(this.clientRegistrationRepository)
|
||||
.authorizationEndpoint((authorizationEndpoint) ->
|
||||
authorizationEndpoint
|
||||
.authorizationRedirectStrategy(this.redirectStrategy)
|
||||
)
|
||||
);
|
||||
// @formatter:on
|
||||
super.configure(http);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda
|
||||
extends CommonLambdaWebSecurityConfigurerAdapter {
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(
|
||||
GOOGLE_CLIENT_REGISTRATION);
|
||||
|
||||
RedirectStrategy redirectStrategy = mock(RedirectStrategy.class);
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.oauth2Login((oauth2Login) ->
|
||||
oauth2Login
|
||||
.clientRegistrationRepository(this.clientRegistrationRepository)
|
||||
.authorizationEndpoint((authorizationEndpoint) ->
|
||||
authorizationEndpoint
|
||||
.authorizationRedirectStrategy(this.redirectStrategy)
|
||||
)
|
||||
);
|
||||
// @formatter:on
|
||||
super.configure(http);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OAuth2LoginConfigMultipleClients extends CommonWebSecurityConfigurerAdapter {
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
|||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
@ -55,6 +56,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
@ -90,6 +92,9 @@ public class OAuth2ClientBeanDefinitionParserTests {
|
|||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
|
@ -148,6 +153,16 @@ public class OAuth2ClientBeanDefinitionParserTests {
|
|||
verify(this.authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {
|
||||
this.spring.configLocations(xml("CustomAuthorizationRedirectStrategy")).autowire();
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/oauth2/authorization/google"))
|
||||
.andExpect(status().isOk());
|
||||
// @formatter:on
|
||||
verify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizationResponseMatchThenProcess() throws Exception {
|
||||
this.spring.configLocations(xml("CustomConfiguration")).autowire();
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
|||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
|
@ -78,6 +79,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
@ -118,6 +120,9 @@ public class OAuth2LoginBeanDefinitionParserTests {
|
|||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedirectStrategy authorizationRedirectStrategy;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
|
@ -378,6 +383,17 @@ public class OAuth2LoginBeanDefinitionParserTests {
|
|||
verify(this.authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthorizationRedirectStrategy"))
|
||||
.autowire();
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/oauth2/authorization/google-login"))
|
||||
.andExpect(status().isOk());
|
||||
// @formatter:on
|
||||
verify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() throws Exception {
|
||||
|
|
|
@ -39,14 +39,18 @@ import org.springframework.security.config.annotation.web.reactive.ServerHttpSec
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
|
||||
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
||||
|
@ -76,6 +80,7 @@ import org.springframework.web.server.WebFilterChain;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
@ -531,6 +536,90 @@ public class ServerHttpSecurityTests {
|
|||
verify(authorizationRequestRepository).removeAuthorizationRequest(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Login() {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Login()
|
||||
.clientRegistrationRepository(clientRegistrationRepository).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange().expectStatus().is3xxRedirection();
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isInstanceOf(DefaultServerRedirectStrategy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConfigureAuthorizationRedirectStrategyForOAuth2Login() {
|
||||
ServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
given(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Login()
|
||||
.clientRegistrationRepository(clientRegistrationRepository)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange();
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any());
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isSameAs(authorizationRedirectStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Client() {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Client()
|
||||
.clientRegistrationRepository(clientRegistrationRepository).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange().expectStatus().is3xxRedirection();
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isInstanceOf(DefaultServerRedirectStrategy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConfigureAuthorizationRedirectStrategyForOAuth2Client() {
|
||||
ServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
|
||||
ReactiveClientRegistrationRepository.class);
|
||||
given(clientRegistrationRepository.findByRegistrationId(anyString()))
|
||||
.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));
|
||||
given(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());
|
||||
|
||||
SecurityWebFilterChain securityFilterChain = this.http.oauth2Client()
|
||||
.clientRegistrationRepository(clientRegistrationRepository)
|
||||
.authorizationRedirectStrategy(authorizationRedirectStrategy).and().build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();
|
||||
client.get().uri("/oauth2/authorization/registration-id").exchange();
|
||||
verify(authorizationRedirectStrategy).sendRedirect(any(), any());
|
||||
|
||||
OAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,
|
||||
OAuth2AuthorizationRequestRedirectWebFilter.class).get();
|
||||
assertThat(ReflectionTestUtils.getField(filter, "authorizationRedirectStrategy"))
|
||||
.isSameAs(authorizationRedirectStrategy);
|
||||
}
|
||||
|
||||
private boolean isX509Filter(WebFilter filter) {
|
||||
try {
|
||||
Object converter = ReflectionTestUtils.getField(filter, "authenticationConverter");
|
||||
|
|
|
@ -37,7 +37,9 @@ import org.springframework.security.oauth2.client.web.server.ServerAuthorization
|
|||
import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.web.reactive.config.EnableWebFlux
|
||||
|
@ -128,6 +130,41 @@ class ServerOAuth2ClientDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 client when authorization redirect strategy configured then custom redirect strategy used`() {
|
||||
this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)
|
||||
every {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
} returns Mono.empty()
|
||||
|
||||
this.client.get()
|
||||
.uri("/oauth2/authorization/google")
|
||||
.exchange()
|
||||
|
||||
verify(exactly = 1) {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
open class AuthorizationRedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
return http {
|
||||
oauth2Client {
|
||||
authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 client when authentication converter configured then custom converter used`() {
|
||||
this.spring.register(AuthenticationConverterConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
|
|
@ -34,7 +34,9 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
|||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.server.DefaultServerRedirectStrategy
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
|
@ -139,6 +141,38 @@ class ServerOAuth2LoginDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 login when authorization redirect strategy configured then custom redirect strategy used`() {
|
||||
this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)
|
||||
every {
|
||||
AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())
|
||||
} returns Mono.empty()
|
||||
this.client.get()
|
||||
.uri("/oauth2/authorization/google")
|
||||
.exchange()
|
||||
|
||||
verify(exactly = 1) { AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
open class AuthorizationRedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
return http {
|
||||
oauth2Login {
|
||||
authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `OAuth2 login when authentication matcher configured then custom matcher used`() {
|
||||
this.spring.register(AuthenticationMatcherConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
|
|
@ -43,6 +43,9 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken
|
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames
|
||||
import org.springframework.security.web.DefaultRedirectStrategy
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
|
||||
|
@ -101,6 +104,40 @@ class AuthorizationCodeGrantDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Client when custom authorization redirect strategy then redirect strategy used`() {
|
||||
this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)
|
||||
every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
|
||||
this.mockMvc.get("/oauth2/authorization/registrationId")
|
||||
|
||||
verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class RedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
oauth2Client {
|
||||
authorizationCodeGrant {
|
||||
authorizationRedirectStrategy = REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
authorizeRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Client when custom access token response client then client used`() {
|
||||
this.spring.register(AuthorizedClientConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
|
|
@ -38,6 +38,9 @@ import org.springframework.security.oauth2.client.web.AuthorizationRequestReposi
|
|||
import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
import org.springframework.security.web.DefaultRedirectStrategy
|
||||
import org.springframework.security.web.RedirectStrategy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.get
|
||||
|
||||
|
@ -121,6 +124,37 @@ class AuthorizationEndpointDslTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Login when custom authorization redirect strategy then redirect strategy used`() {
|
||||
this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()
|
||||
mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)
|
||||
every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
|
||||
this.mockMvc.get("/oauth2/authorization/google")
|
||||
|
||||
verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class RedirectStrategyConfig {
|
||||
|
||||
companion object {
|
||||
val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
oauth2Login {
|
||||
authorizationEndpoint {
|
||||
authorizationRedirectStrategy = REDIRECT_STRATEGY
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oauth2Login when custom authorization uri repository then uri used`() {
|
||||
this.spring.register(AuthorizationUriConfig::class.java, ClientConfig::class.java).autowire()
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2022 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.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http auto-config="true">
|
||||
<oauth2-client>
|
||||
<authorization-code-grant
|
||||
authorization-redirect-strategy-ref="authorizationRedirectStrategy"/>
|
||||
</oauth2-client>
|
||||
</http>
|
||||
|
||||
<b:bean id="authorizationRedirectStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<b:constructor-arg value="org.springframework.security.web.RedirectStrategy"/>
|
||||
</b:bean>
|
||||
|
||||
<client-registrations>
|
||||
<client-registration registration-id="google"
|
||||
client-id="google-client-id"
|
||||
client-secret="google-client-secret"
|
||||
redirect-uri="http://localhost/callback/google"
|
||||
scope="scope1,scope2"
|
||||
provider-id="google"/>
|
||||
</client-registrations>
|
||||
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2022 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.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http auto-config="true">
|
||||
<intercept-url pattern="/**" access="authenticated"/>
|
||||
<oauth2-login authorization-redirect-strategy-ref="authorizationRedirectStrategy"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="authorizationRedirectStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<b:constructor-arg value="org.springframework.security.web.RedirectStrategy"/>
|
||||
</b:bean>
|
||||
|
||||
<b:import resource="../oauth2/client/google-registration.xml"/>
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
|
@ -984,6 +984,11 @@ Reference to the `AuthorizationRequestRepository`.
|
|||
Reference to the `OAuth2AuthorizationRequestResolver`.
|
||||
|
||||
|
||||
[[nsa-oauth2-login-authorization-redirect-strategy-ref]]
|
||||
* **authorization-redirect-strategy-ref**
|
||||
Reference to the authorization `RedirectStrategy`.
|
||||
|
||||
|
||||
[[nsa-oauth2-login-access-token-response-client-ref]]
|
||||
* **access-token-response-client-ref**
|
||||
Reference to the `OAuth2AccessTokenResponseClient`.
|
||||
|
@ -1084,6 +1089,11 @@ Configures xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-aut
|
|||
Reference to the `AuthorizationRequestRepository`.
|
||||
|
||||
|
||||
[[nsa-authorization-code-grant-authorization-redirect-strategy-ref]]
|
||||
* **authorization-redirect-strategy-ref**
|
||||
Reference to the authorization `RedirectStrategy`.
|
||||
|
||||
|
||||
[[nsa-authorization-code-grant-authorization-request-resolver-ref]]
|
||||
* **authorization-request-resolver-ref**
|
||||
Reference to the `OAuth2AuthorizationRequestResolver`.
|
||||
|
|
|
@ -95,7 +95,7 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||
|
||||
private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
||||
|
||||
private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
private RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
|
@ -139,6 +139,15 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
|
|||
this.authorizationRequestResolver = authorizationRequestResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
*/
|
||||
public void setAuthorizationRedirectStrategy(RedirectStrategy authorizationRedirectStrategy) {
|
||||
Assert.notNull(authorizationRedirectStrategy, "authorizationRedirectStrategy cannot be null");
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
|
||||
* @param authorizationRequestRepository the repository used for storing
|
||||
|
|
|
@ -75,7 +75,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
|||
*/
|
||||
public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
||||
|
||||
private final ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
private ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
||||
|
||||
private final ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
|
@ -105,6 +105,15 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|||
this.authorizationRequestResolver = authorizationRequestResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redirect strategy for Authorization Endpoint redirect URI.
|
||||
* @param authorizationRedirectStrategy the redirect strategy
|
||||
*/
|
||||
public void setAuthorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
|
||||
Assert.notNull(authorizationRedirectStrategy, "authorizationRedirectStrategy cannot be null");
|
||||
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
|
||||
* @param authorizationRequestRepository the repository used for storing
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.security.oauth2.client.web;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -30,7 +31,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
||||
|
@ -40,6 +43,7 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
|
|||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
@ -116,6 +120,11 @@ public class OAuth2AuthorizationRequestRedirectFilterTests {
|
|||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRequestRepository(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthorizationRedirectStrategyWhenAuthorizationRedirectStrategyIsNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestCacheWhenRequestCacheIsNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestCache(null));
|
||||
|
@ -333,4 +342,31 @@ public class OAuth2AuthorizationRequestRedirectFilterTests {
|
|||
+ "login_hint=user@provider\\.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenCustomAuthorizationRedirectStrategySetThenCustomAuthorizationRedirectStrategyUsed()
|
||||
throws Exception {
|
||||
String requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/"
|
||||
+ this.registration1.getRegistrationId();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
||||
request.setServletPath(requestUri);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain filterChain = mock(FilterChain.class);
|
||||
RedirectStrategy customRedirectStrategy = (httpRequest, httpResponse, url) -> {
|
||||
String redirectUrl = httpResponse.encodeRedirectURL(url);
|
||||
httpResponse.setStatus(HttpStatus.OK.value());
|
||||
httpResponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
|
||||
httpResponse.getWriter().write(redirectUrl);
|
||||
httpResponse.getWriter().flush();
|
||||
};
|
||||
this.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);
|
||||
this.filter.doFilter(request, response, filterChain);
|
||||
verifyZeroInteractions(filterChain);
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_PLAIN_VALUE);
|
||||
assertThat(response.getContentAsString(StandardCharsets.UTF_8))
|
||||
.matches("https://example.com/login/oauth/authorize\\?" + "response_type=code&client_id=client-id&"
|
||||
+ "scope=read:user&state=.{15,}&"
|
||||
+ "redirect_uri=http://localhost/login/oauth2/code/registration-id");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.security.oauth2.client.web.server;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -24,13 +25,20 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.server.ServerRedirectStrategy;
|
||||
import org.springframework.security.web.server.savedrequest.ServerRequestCache;
|
||||
import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
@ -81,6 +89,11 @@ public class OAuth2AuthorizationRequestRedirectWebFilterTests {
|
|||
.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectWebFilter(this.clientRepository));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setterWhenAuthorizationRedirectStrategyNullThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenDoesNotMatchThenClientRegistrationRepositoryNotSubscribed() {
|
||||
// @formatter:off
|
||||
|
@ -195,4 +208,46 @@ public class OAuth2AuthorizationRequestRedirectWebFilterTests {
|
|||
verifyNoInteractions(this.requestCache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenCustomRedirectStrategySetThenRedirectUriInResponseBody() {
|
||||
given(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))
|
||||
.willReturn(Mono.just(this.registration));
|
||||
given(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());
|
||||
ServerRedirectStrategy customRedirectStrategy = (exchange, location) -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory()
|
||||
.wrap(location.toASCIIString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return exchange.getResponse().writeWith(Flux.just(buffer));
|
||||
};
|
||||
this.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);
|
||||
this.filter.setRequestCache(this.requestCache);
|
||||
|
||||
FluxExchangeResult<String> result = this.client.get()
|
||||
.uri("https://example.com/oauth2/authorization/registration-id").exchange().expectHeader()
|
||||
.contentType(MediaType.TEXT_PLAIN).expectStatus().isOk().returnResult(String.class);
|
||||
|
||||
// @formatter:off
|
||||
StepVerifier.create(result.getResponseBody())
|
||||
.assertNext((uri) -> {
|
||||
URI location = URI.create(uri);
|
||||
|
||||
assertThat(location)
|
||||
.hasScheme("https")
|
||||
.hasHost("example.com")
|
||||
.hasPath("/login/oauth/authorize")
|
||||
.hasParameter("response_type", "code")
|
||||
.hasParameter("client_id", "client-id")
|
||||
.hasParameter("scope", "read:user")
|
||||
.hasParameter("state")
|
||||
.hasParameter("redirect_uri", "https://example.com/login/oauth2/code/registration-id");
|
||||
})
|
||||
.verifyComplete();
|
||||
// @formatter:on
|
||||
|
||||
verifyNoInteractions(this.requestCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue