Skip to content

Commit 779597a

Browse files
jgrandjarwinch
authored andcommitted
Add support for custom authorization request parameters
Fixes gh-4911
1 parent a3210c9 commit 779597a

File tree

12 files changed

+867
-423
lines changed

12 files changed

+867
-423
lines changed

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void configureWhenAuthorizationCodeRequestThenRedirectForAuthorization()
120120
MvcResult mvcResult = this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
121121
.andExpect(status().is3xxRedirection())
122122
.andReturn();
123-
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http://localhost/client-1");
123+
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http%3A%2F%2Flocalhost%2Fclient-1");
124124
}
125125

126126
@Test
@@ -168,7 +168,7 @@ public void configureWhenRequestCacheProvidedAndClientAuthorizationRequiredExcep
168168
MvcResult mvcResult = this.mockMvc.perform(get("/resource1").with(user("user1")))
169169
.andExpect(status().is3xxRedirection())
170170
.andReturn();
171-
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http://localhost/client-1");
171+
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http%3A%2F%2Flocalhost%2Fclient-1");
172172

173173
verify(requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
174174
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.web;
17+
18+
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
19+
import org.springframework.security.crypto.keygen.StringKeyGenerator;
20+
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
21+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
22+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
23+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
24+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
25+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
26+
import org.springframework.security.web.util.UrlUtils;
27+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
28+
import org.springframework.util.Assert;
29+
import org.springframework.web.util.UriComponentsBuilder;
30+
31+
import javax.servlet.http.HttpServletRequest;
32+
import java.util.Base64;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
36+
import static org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.AUTHORIZATION_REQUIRED_EXCEPTION_ATTR_NAME;
37+
38+
/**
39+
* An implementation of an {@link OAuth2AuthorizationRequestResolver} that attempts to
40+
* resolve an {@link OAuth2AuthorizationRequest} from the provided {@code HttpServletRequest}
41+
* using the default request {@code URI} pattern {@code /oauth2/authorization/{registrationId}}.
42+
*
43+
* <p>
44+
* <b>NOTE:</b> The default base {@code URI} {@code /oauth2/authorization} may be overridden
45+
* via it's constructor {@link #DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository, String)}.
46+
*
47+
* @author Joe Grandja
48+
* @since 5.1
49+
* @see OAuth2AuthorizationRequestResolver
50+
* @see OAuth2AuthorizationRequestRedirectFilter
51+
*/
52+
public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
53+
private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
54+
private final ClientRegistrationRepository clientRegistrationRepository;
55+
private final AntPathRequestMatcher authorizationRequestMatcher;
56+
private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
57+
58+
/**
59+
* Constructs a {@code DefaultOAuth2AuthorizationRequestResolver} using the provided parameters.
60+
*
61+
* @param clientRegistrationRepository the repository of client registrations
62+
* @param authorizationRequestBaseUri the base {@code URI} used for resolving authorization requests
63+
*/
64+
public DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository,
65+
String authorizationRequestBaseUri) {
66+
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
67+
Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
68+
this.clientRegistrationRepository = clientRegistrationRepository;
69+
this.authorizationRequestMatcher = new AntPathRequestMatcher(
70+
authorizationRequestBaseUri + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
71+
}
72+
73+
@Override
74+
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
75+
String registrationId = this.resolveRegistrationId(request);
76+
if (registrationId == null) {
77+
return null;
78+
}
79+
80+
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
81+
if (clientRegistration == null) {
82+
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
83+
}
84+
85+
OAuth2AuthorizationRequest.Builder builder;
86+
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
87+
builder = OAuth2AuthorizationRequest.authorizationCode();
88+
} else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
89+
builder = OAuth2AuthorizationRequest.implicit();
90+
} else {
91+
throw new IllegalArgumentException("Invalid Authorization Grant Type (" +
92+
clientRegistration.getAuthorizationGrantType().getValue() +
93+
") for Client Registration with Id: " + clientRegistration.getRegistrationId());
94+
}
95+
96+
String redirectUriAction = this.resolveRedirectUriAction(request, clientRegistration);
97+
String redirectUriStr = this.expandRedirectUri(request, clientRegistration, redirectUriAction);
98+
99+
Map<String, Object> additionalParameters = new HashMap<>();
100+
additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
101+
102+
OAuth2AuthorizationRequest authorizationRequest = builder
103+
.clientId(clientRegistration.getClientId())
104+
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
105+
.redirectUri(redirectUriStr)
106+
.scopes(clientRegistration.getScopes())
107+
.state(this.stateGenerator.generateKey())
108+
.additionalParameters(additionalParameters)
109+
.build();
110+
111+
return authorizationRequest;
112+
}
113+
114+
private String resolveRegistrationId(HttpServletRequest request) {
115+
// Check for ClientAuthorizationRequiredException which may have been set
116+
// in the request by OAuth2AuthorizationRequestRedirectFilter
117+
ClientAuthorizationRequiredException authzEx =
118+
(ClientAuthorizationRequiredException) request.getAttribute(AUTHORIZATION_REQUIRED_EXCEPTION_ATTR_NAME);
119+
if (authzEx != null) {
120+
return authzEx.getClientRegistrationId();
121+
}
122+
if (this.authorizationRequestMatcher.matches(request)) {
123+
return this.authorizationRequestMatcher
124+
.extractUriTemplateVariables(request).get(REGISTRATION_ID_URI_VARIABLE_NAME);
125+
}
126+
return null;
127+
}
128+
129+
private String resolveRedirectUriAction(HttpServletRequest request, ClientRegistration clientRegistration) {
130+
String action = null;
131+
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
132+
String loginAction = "login";
133+
String authorizeAction = "authorize";
134+
String actionParameter = request.getParameter("action");
135+
if (request.getAttribute(AUTHORIZATION_REQUIRED_EXCEPTION_ATTR_NAME) != null) {
136+
// Check for ClientAuthorizationRequiredException which may have been set
137+
// in the request by OAuth2AuthorizationRequestRedirectFilter
138+
action = authorizeAction;
139+
} else if (actionParameter == null) {
140+
action = loginAction; // Default
141+
} else {
142+
if (actionParameter.equalsIgnoreCase(loginAction)) {
143+
action = loginAction;
144+
} else {
145+
action = authorizeAction;
146+
}
147+
}
148+
}
149+
return action;
150+
}
151+
152+
private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
153+
// Supported URI variables -> baseUrl, action, registrationId
154+
// Used in -> CommonOAuth2Provider.DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}"
155+
Map<String, String> uriVariables = new HashMap<>();
156+
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
157+
String baseUrl = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
158+
.replacePath(request.getContextPath())
159+
.build()
160+
.toUriString();
161+
uriVariables.put("baseUrl", baseUrl);
162+
if (action != null) {
163+
uriVariables.put("action", action);
164+
}
165+
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUriTemplate())
166+
.buildAndExpand(uriVariables)
167+
.toUriString();
168+
}
169+
}

0 commit comments

Comments
 (0)