Skip to content

Commit efaee4e

Browse files
igorbolicrwinch
authored andcommitted
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
1 parent c9f8d2b commit efaee4e

File tree

27 files changed

+712
-2
lines changed

27 files changed

+712
-2
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
3535
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
3636
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
37+
import org.springframework.security.web.RedirectStrategy;
3738
import org.springframework.security.web.savedrequest.RequestCache;
3839
import org.springframework.util.Assert;
3940

@@ -171,6 +172,8 @@ public final class AuthorizationCodeGrantConfigurer {
171172

172173
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
173174

175+
private RedirectStrategy authorizationRedirectStrategy;
176+
174177
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
175178

176179
private AuthorizationCodeGrantConfigurer() {
@@ -202,6 +205,17 @@ public AuthorizationCodeGrantConfigurer authorizationRequestRepository(
202205
return this;
203206
}
204207

208+
/**
209+
* Sets the redirect strategy for Authorization Endpoint redirect URI.
210+
* @param authorizationRedirectStrategy the redirect strategy
211+
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
212+
*/
213+
public AuthorizationCodeGrantConfigurer authorizationRedirectStrategy(
214+
RedirectStrategy authorizationRedirectStrategy) {
215+
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
216+
return this;
217+
}
218+
205219
/**
206220
* Sets the client used for requesting the access token credential from the Token
207221
* Endpoint.
@@ -247,6 +261,9 @@ private OAuth2AuthorizationRequestRedirectFilter createAuthorizationRequestRedir
247261
authorizationRequestRedirectFilter
248262
.setAuthorizationRequestRepository(this.authorizationRequestRepository);
249263
}
264+
if (this.authorizationRedirectStrategy != null) {
265+
authorizationRequestRedirectFilter.setAuthorizationRedirectStrategy(this.authorizationRedirectStrategy);
266+
}
250267
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
251268
if (requestCache != null) {
252269
authorizationRequestRedirectFilter.setRequestCache(requestCache);

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import org.springframework.security.oauth2.core.user.OAuth2User;
6969
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
7070
import org.springframework.security.web.AuthenticationEntryPoint;
71+
import org.springframework.security.web.RedirectStrategy;
7172
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
7273
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
7374
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
@@ -368,6 +369,10 @@ public void configure(B http) throws Exception {
368369
authorizationRequestFilter
369370
.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);
370371
}
372+
if (this.authorizationEndpointConfig.authorizationRedirectStrategy != null) {
373+
authorizationRequestFilter
374+
.setAuthorizationRedirectStrategy(this.authorizationEndpointConfig.authorizationRedirectStrategy);
375+
}
371376
RequestCache requestCache = http.getSharedObject(RequestCache.class);
372377
if (requestCache != null) {
373378
authorizationRequestFilter.setRequestCache(requestCache);
@@ -540,6 +545,8 @@ public final class AuthorizationEndpointConfig {
540545

541546
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
542547

548+
private RedirectStrategy authorizationRedirectStrategy;
549+
543550
private AuthorizationEndpointConfig() {
544551
}
545552

@@ -582,6 +589,17 @@ public AuthorizationEndpointConfig authorizationRequestRepository(
582589
return this;
583590
}
584591

592+
/**
593+
* Sets the redirect strategy for Authorization Endpoint redirect URI.
594+
* @param authorizationRedirectStrategy the redirect strategy
595+
* @return the {@link AuthorizationEndpointConfig} for further configuration
596+
*/
597+
public AuthorizationEndpointConfig authorizationRedirectStrategy(
598+
RedirectStrategy authorizationRedirectStrategy) {
599+
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
600+
return this;
601+
}
602+
585603
/**
586604
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
587605
* @return the {@link OAuth2LoginConfigurer}

config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
4444

4545
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
4646

47+
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
48+
4749
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
4850

4951
private final BeanReference requestCache;
@@ -87,6 +89,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
8789
}
8890
BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(
8991
authorizationCodeGrantElt);
92+
BeanMetadataElement authorizationRedirectStrategy = getAuthorizationRedirectStrategy(authorizationCodeGrantElt);
9093
BeanDefinitionBuilder authorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder
9194
.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);
9295
String authorizationRequestResolverRef = (authorizationCodeGrantElt != null)
@@ -99,6 +102,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
99102
}
100103
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
101104
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
105+
.addPropertyValue("authorizationRedirectStrategy", authorizationRedirectStrategy)
102106
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
103107
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
104108
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
@@ -130,6 +134,16 @@ private BeanMetadataElement getAuthorizationRequestRepository(Element element) {
130134
.getBeanDefinition();
131135
}
132136

137+
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
138+
String authorizationRedirectStrategyRef = (element != null)
139+
? element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF) : null;
140+
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
141+
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
142+
}
143+
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
144+
.getBeanDefinition();
145+
}
146+
133147
private BeanMetadataElement getAccessTokenResponseClient(Element element) {
134148
String accessTokenResponseClientRef = (element != null)
135149
? element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null;

config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
8787

8888
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
8989

90+
private static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = "authorization-redirect-strategy-ref";
91+
9092
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
9193

9294
private static final String ATT_USER_AUTHORITIES_MAPPER_REF = "user-authorities-mapper-ref";
@@ -203,6 +205,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
203205
}
204206
oauth2AuthorizationRequestRedirectFilterBuilder
205207
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
208+
.addPropertyValue("authorizationRedirectStrategy", getAuthorizationRedirectStrategy(element))
206209
.addPropertyValue("requestCache", this.requestCache);
207210
this.oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder
208211
.getBeanDefinition();
@@ -267,6 +270,15 @@ private BeanMetadataElement getAuthorizationRequestRepository(Element element) {
267270
.getBeanDefinition();
268271
}
269272

273+
private BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {
274+
String authorizationRedirectStrategyRef = element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF);
275+
if (StringUtils.hasText(authorizationRedirectStrategyRef)) {
276+
return new RuntimeBeanReference(authorizationRedirectStrategyRef);
277+
}
278+
return BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.web.DefaultRedirectStrategy")
279+
.getBeanDefinition();
280+
}
281+
270282
private BeanDefinition getOidcAuthProvider(Element element, BeanMetadataElement accessTokenResponseClient,
271283
String userAuthoritiesMapperRef) {
272284
boolean oidcAuthenticationProviderEnabled = ClassUtils

config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,14 @@
102102
import org.springframework.security.web.PortMapper;
103103
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
104104
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
105+
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
105106
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;
106107
import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
107108
import org.springframework.security.web.server.ExchangeMatcherRedirectWebFilter;
108109
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
109110
import org.springframework.security.web.server.SecurityWebFilterChain;
110111
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
112+
import org.springframework.security.web.server.ServerRedirectStrategy;
111113
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter;
112114
import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;
113115
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
@@ -3375,6 +3377,8 @@ public final class OAuth2LoginSpec {
33753377

33763378
private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
33773379

3380+
private ServerRedirectStrategy authorizationRedirectStrategy;
3381+
33783382
private ServerWebExchangeMatcher authenticationMatcher;
33793383

33803384
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
@@ -3547,6 +3551,16 @@ public OAuth2LoginSpec authorizationRequestResolver(
35473551
return this;
35483552
}
35493553

3554+
/**
3555+
* Sets the redirect strategy for Authorization Endpoint redirect URI.
3556+
* @param authorizationRedirectStrategy the redirect strategy
3557+
* @return the {@link OAuth2LoginSpec} for further configuration
3558+
*/
3559+
public OAuth2LoginSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
3560+
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
3561+
return this;
3562+
}
3563+
35503564
/**
35513565
* Sets the {@link ServerWebExchangeMatcher matcher} used for determining if the
35523566
* request is an authentication request.
@@ -3581,7 +3595,9 @@ protected void configure(ServerHttpSecurity http) {
35813595
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();
35823596
ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = getAuthorizationRequestRepository();
35833597
oauthRedirectFilter.setAuthorizationRequestRepository(authorizationRequestRepository);
3598+
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
35843599
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
3600+
35853601
ReactiveAuthenticationManager manager = getAuthenticationManager();
35863602
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager,
35873603
authorizedClientRepository);
@@ -3591,6 +3607,7 @@ protected void configure(ServerHttpSecurity http) {
35913607
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
35923608
authenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());
35933609
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
3610+
35943611
setDefaultEntryPoints(http);
35953612
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
35963613
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
@@ -3737,6 +3754,13 @@ private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> getAuth
37373754
return this.authorizationRequestRepository;
37383755
}
37393756

3757+
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
3758+
if (this.authorizationRedirectStrategy == null) {
3759+
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
3760+
}
3761+
return this.authorizationRedirectStrategy;
3762+
}
3763+
37403764
private ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {
37413765
ReactiveOAuth2AuthorizedClientService bean = getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);
37423766
if (bean != null) {
@@ -3759,6 +3783,8 @@ public final class OAuth2ClientSpec {
37593783

37603784
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
37613785

3786+
private ServerRedirectStrategy authorizationRedirectStrategy;
3787+
37623788
private OAuth2ClientSpec() {
37633789
}
37643790

@@ -3851,6 +3877,23 @@ private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> getAuth
38513877
return this.authorizationRequestRepository;
38523878
}
38533879

3880+
/**
3881+
* Sets the redirect strategy for Authorization Endpoint redirect URI.
3882+
* @param authorizationRedirectStrategy the redirect strategy
3883+
* @return the {@link OAuth2ClientSpec} for further configuration
3884+
*/
3885+
public OAuth2ClientSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {
3886+
this.authorizationRedirectStrategy = authorizationRedirectStrategy;
3887+
return this;
3888+
}
3889+
3890+
private ServerRedirectStrategy getAuthorizationRedirectStrategy() {
3891+
if (this.authorizationRedirectStrategy == null) {
3892+
this.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
3893+
}
3894+
return this.authorizationRedirectStrategy;
3895+
}
3896+
38543897
/**
38553898
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
38563899
* @return the {@link ServerHttpSecurity} to continue configuring
@@ -3870,12 +3913,15 @@ protected void configure(ServerHttpSecurity http) {
38703913
if (http.requestCache != null) {
38713914
codeGrantWebFilter.setRequestCache(http.requestCache.requestCache);
38723915
}
3916+
38733917
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(
38743918
clientRegistrationRepository);
38753919
oauthRedirectFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());
3920+
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
38763921
if (http.requestCache != null) {
38773922
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
38783923
}
3924+
38793925
http.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.OAUTH2_AUTHORIZATION_CODE);
38803926
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
38813927
}

config/src/main/kotlin/org/springframework/security/config/web/server/ServerOAuth2ClientDsl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
2222
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository
2323
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
2424
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
25+
import org.springframework.security.web.server.ServerRedirectStrategy
2526
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
2627
import org.springframework.web.server.ServerWebExchange
2728

@@ -37,6 +38,7 @@ import org.springframework.web.server.ServerWebExchange
3738
* @property clientRegistrationRepository the repository of client registrations.
3839
* @property authorizedClientRepository the repository for authorized client(s).
3940
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
41+
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
4042
*/
4143
@ServerSecurityMarker
4244
class ServerOAuth2ClientDsl {
@@ -45,6 +47,7 @@ class ServerOAuth2ClientDsl {
4547
var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null
4648
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
4749
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
50+
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
4851

4952
internal fun get(): (ServerHttpSecurity.OAuth2ClientSpec) -> Unit {
5053
return { oauth2Client ->
@@ -53,6 +56,7 @@ class ServerOAuth2ClientDsl {
5356
clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }
5457
authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }
5558
authorizationRequestRepository?.also { oauth2Client.authorizationRequestRepository(authorizationRequestRepository) }
59+
authorizationRedirectStrategy?.also { oauth2Client.authorizationRedirectStrategy(authorizationRedirectStrategy) }
5660
}
5761
}
5862
}

config/src/main/kotlin/org/springframework/security/config/web/server/ServerOAuth2LoginDsl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.springframework.security.oauth2.client.web.server.ServerAuthorization
2424
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver
2525
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
2626
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
27+
import org.springframework.security.web.server.ServerRedirectStrategy
2728
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
2829
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
2930
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
@@ -49,6 +50,7 @@ import org.springframework.web.server.ServerWebExchange
4950
* @property authorizedClientRepository the repository for authorized client(s).
5051
* @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.
5152
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]s.
53+
* @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.
5254
* @property authenticationMatcher the [ServerWebExchangeMatcher] used for determining if the request is an
5355
* authentication request.
5456
*/
@@ -64,6 +66,7 @@ class ServerOAuth2LoginDsl {
6466
var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null
6567
var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
6668
var authorizationRequestResolver: ServerOAuth2AuthorizationRequestResolver? = null
69+
var authorizationRedirectStrategy: ServerRedirectStrategy? = null
6770
var authenticationMatcher: ServerWebExchangeMatcher? = null
6871

6972
internal fun get(): (ServerHttpSecurity.OAuth2LoginSpec) -> Unit {
@@ -78,6 +81,7 @@ class ServerOAuth2LoginDsl {
7881
authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }
7982
authorizationRequestRepository?.also { oauth2Login.authorizationRequestRepository(authorizationRequestRepository) }
8083
authorizationRequestResolver?.also { oauth2Login.authorizationRequestResolver(authorizationRequestResolver) }
84+
authorizationRedirectStrategy?.also { oauth2Login.authorizationRedirectStrategy(authorizationRedirectStrategy) }
8185
authenticationMatcher?.also { oauth2Login.authenticationMatcher(authenticationMatcher) }
8286
}
8387
}

0 commit comments

Comments
 (0)