Skip to content

Commit d840090

Browse files
committed
Add support for implicit grant type
Fixes gh-4500
1 parent 6f0821a commit d840090

File tree

15 files changed

+197
-76
lines changed

15 files changed

+197
-76
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
7878
put(LogoutFilter.class, order);
7979
order += STEP;
8080
filterToOrder.put(
81-
"org.springframework.security.oauth2.client.web.AuthorizationCodeRequestRedirectFilter",
81+
"org.springframework.security.oauth2.client.web.AuthorizationRequestRedirectFilter",
8282
order);
8383
order += STEP;
8484
put(X509AuthenticationFilter.class, order);

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import org.springframework.security.oauth2.client.user.DelegatingOAuth2UserService;
3636
import org.springframework.security.oauth2.client.user.OAuth2UserService;
3737
import org.springframework.security.oauth2.client.web.AuthorizationCodeAuthenticationFilter;
38-
import org.springframework.security.oauth2.client.web.AuthorizationCodeRequestRedirectFilter;
38+
import org.springframework.security.oauth2.client.web.AuthorizationRequestRedirectFilter;
3939
import org.springframework.security.oauth2.client.web.AuthorizationGrantTokenExchanger;
4040
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
4141
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
@@ -63,7 +63,7 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
6363
AbstractHttpConfigurer<AuthorizationCodeGrantConfigurer<B>, B> {
6464

6565
// ***** Authorization Request members
66-
private AuthorizationCodeRequestRedirectFilter authorizationRequestFilter;
66+
private AuthorizationRequestRedirectFilter authorizationRequestFilter;
6767
private String authorizationRequestBaseUri;
6868
private AuthorizationRequestUriBuilder authorizationRequestBuilder;
6969
private AuthorizationRequestRepository authorizationRequestRepository;
@@ -180,8 +180,8 @@ public final void init(B http) throws Exception {
180180
// *************************
181181
// ***** Initialize Filter's
182182
//
183-
// -> AuthorizationCodeRequestRedirectFilter
184-
this.authorizationRequestFilter = new AuthorizationCodeRequestRedirectFilter(
183+
// -> AuthorizationRequestRedirectFilter
184+
this.authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
185185
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
186186
if (this.authorizationRequestBuilder != null) {
187187
this.authorizationRequestFilter.setAuthorizationUriBuilder(this.authorizationRequestBuilder);
@@ -210,14 +210,14 @@ public void configure(B http) throws Exception {
210210
http.addFilter(this.postProcess(this.authorizationResponseFilter));
211211
}
212212

213-
AuthorizationCodeRequestRedirectFilter getAuthorizationRequestFilter() {
213+
AuthorizationRequestRedirectFilter getAuthorizationRequestFilter() {
214214
return this.authorizationRequestFilter;
215215
}
216216

217217
String getAuthorizationRequestBaseUri() {
218218
return this.authorizationRequestBaseUri != null ?
219219
this.authorizationRequestBaseUri :
220-
AuthorizationCodeRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
220+
AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
221221
}
222222

223223
String getAuthorizationResponseBaseUri() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2012-2017 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.config.annotation.web.configurers.oauth2.client;
17+
18+
import org.springframework.context.ApplicationContext;
19+
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
20+
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
21+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
22+
import org.springframework.security.oauth2.client.web.AuthorizationRequestRedirectFilter;
23+
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* A security configurer for the Implicit Grant type.
28+
*
29+
* @author Joe Grandja
30+
* @since 5.0
31+
*/
32+
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> extends
33+
AbstractHttpConfigurer<ImplicitGrantConfigurer<B>, B> {
34+
35+
private String authorizationRequestBaseUri;
36+
private AuthorizationRequestUriBuilder authorizationRequestBuilder;
37+
38+
public ImplicitGrantConfigurer<B> authorizationRequestBaseUri(String authorizationRequestBaseUri) {
39+
Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
40+
this.authorizationRequestBaseUri = authorizationRequestBaseUri;
41+
return this;
42+
}
43+
44+
public ImplicitGrantConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
45+
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
46+
this.authorizationRequestBuilder = authorizationRequestBuilder;
47+
return this;
48+
}
49+
50+
public ImplicitGrantConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
51+
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
52+
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
53+
return this;
54+
}
55+
56+
@Override
57+
public void configure(B http) throws Exception {
58+
AuthorizationRequestRedirectFilter authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
59+
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
60+
if (this.authorizationRequestBuilder != null) {
61+
authorizationRequestFilter.setAuthorizationUriBuilder(this.authorizationRequestBuilder);
62+
}
63+
http.addFilter(this.postProcess(authorizationRequestFilter));
64+
}
65+
66+
private String getAuthorizationRequestBaseUri() {
67+
return this.authorizationRequestBaseUri != null ?
68+
this.authorizationRequestBaseUri :
69+
AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
70+
}
71+
72+
private ClientRegistrationRepository getClientRegistrationRepository() {
73+
ClientRegistrationRepository clientRegistrationRepository = this.getBuilder().getSharedObject(ClientRegistrationRepository.class);
74+
if (clientRegistrationRepository == null) {
75+
clientRegistrationRepository = this.getClientRegistrationRepositoryBean();
76+
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
77+
}
78+
return clientRegistrationRepository;
79+
}
80+
81+
private ClientRegistrationRepository getClientRegistrationRepositoryBean() {
82+
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
83+
}
84+
}

config/src/test/java/org/springframework/security/config/oauth2/client/CommonOAuth2ProviderTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,16 @@ public void getBuilderWhenOktaShouldHaveOktaSettings() throws Exception {
109109
ClientRegistration registration = builder(CommonOAuth2Provider.OKTA)
110110
.authorizationUri("http://example.com/auth")
111111
.tokenUri("http://example.com/token")
112-
.userInfoUri("http://example.com/info").build();
112+
.userInfoUri("http://example.com/info")
113+
.jwkSetUri("http://example.com/jwkset").build();
113114
ProviderDetails providerDetails = registration.getProviderDetails();
114115
assertThat(providerDetails.getAuthorizationUri())
115116
.isEqualTo("http://example.com/auth");
116117
assertThat(providerDetails.getTokenUri()).isEqualTo("http://example.com/token");
117118
assertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo("http://example.com/info");
118119
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName())
119120
.isEqualTo(IdTokenClaim.SUB);
120-
assertThat(providerDetails.getJwkSetUri()).isNull();
121+
assertThat(providerDetails.getJwkSetUri()).isEqualTo("http://example.com/jwkset");
121122
assertThat(registration.getClientAuthenticationMethod())
122123
.isEqualTo(ClientAuthenticationMethod.BASIC);
123124
assertThat(registration.getAuthorizationGrantType())

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,18 @@ public Builder clientName(String clientName) {
304304
}
305305

306306
public ClientRegistration build() {
307-
this.validateClientWithAuthorizationCodeGrantType();
308-
ClientRegistration clientRegistration = new ClientRegistration();
309-
this.setProperties(clientRegistration);
310-
return clientRegistration;
307+
Assert.notNull(this.authorizationGrantType, "authorizationGrantType cannot be null");
308+
if (AuthorizationGrantType.IMPLICIT.equals(this.authorizationGrantType)) {
309+
this.validateImplicitGrantType();
310+
} else {
311+
this.validateAuthorizationCodeGrantType();
312+
}
313+
return this.create();
311314
}
312315

313-
protected void setProperties(ClientRegistration clientRegistration) {
316+
protected ClientRegistration create() {
317+
ClientRegistration clientRegistration = new ClientRegistration();
318+
314319
clientRegistration.setRegistrationId(this.registrationId);
315320
clientRegistration.setClientId(this.clientId);
316321
clientRegistration.setClientSecret(this.clientSecret);
@@ -328,9 +333,11 @@ protected void setProperties(ClientRegistration clientRegistration) {
328333
clientRegistration.setProviderDetails(providerDetails);
329334

330335
clientRegistration.setClientName(this.clientName);
336+
337+
return clientRegistration;
331338
}
332339

333-
protected void validateClientWithAuthorizationCodeGrantType() {
340+
protected void validateAuthorizationCodeGrantType() {
334341
Assert.isTrue(AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType),
335342
"authorizationGrantType must be " + AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
336343
Assert.hasText(this.registrationId, "registrationId cannot be empty");
@@ -341,12 +348,22 @@ protected void validateClientWithAuthorizationCodeGrantType() {
341348
Assert.notEmpty(this.scope, "scope cannot be empty");
342349
Assert.hasText(this.authorizationUri, "authorizationUri cannot be empty");
343350
Assert.hasText(this.tokenUri, "tokenUri cannot be empty");
344-
if (!this.scope.contains(OidcScope.OPENID)) {
345-
// userInfoUri is optional for OIDC Clients
346-
Assert.hasText(this.userInfoUri, "userInfoUri cannot be empty");
351+
if (this.scope.contains(OidcScope.OPENID)) {
352+
// OIDC Clients need to verify/validate the ID Token
353+
Assert.hasText(this.jwkSetUri, "jwkSetUri cannot be empty");
347354
}
348355
Assert.hasText(this.clientName, "clientName cannot be empty");
356+
}
357+
358+
protected void validateImplicitGrantType() {
359+
Assert.isTrue(AuthorizationGrantType.IMPLICIT.equals(this.authorizationGrantType),
360+
"authorizationGrantType must be " + AuthorizationGrantType.IMPLICIT.getValue());
349361
Assert.hasText(this.registrationId, "registrationId cannot be empty");
362+
Assert.hasText(this.clientId, "clientId cannot be empty");
363+
Assert.hasText(this.redirectUri, "redirectUri cannot be empty");
364+
Assert.notEmpty(this.scope, "scope cannot be empty");
365+
Assert.hasText(this.authorizationUri, "authorizationUri cannot be empty");
366+
Assert.hasText(this.clientName, "clientName cannot be empty");
350367
}
351368
}
352369
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/AuthorizationCodeAuthenticationFilter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@
7272
* @see AbstractAuthenticationProcessingFilter
7373
* @see AuthorizationCodeAuthenticationToken
7474
* @see AuthorizationCodeAuthenticationProvider
75-
* @see AuthorizationCodeRequestRedirectFilter
75+
* @see AuthorizationRequestRedirectFilter
7676
* @see AuthorizationRequest
7777
* @see AuthorizationRequestRepository
7878
* @see ClientRegistrationRepository
79-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
79+
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
8080
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
8181
*/
8282
public class AuthorizationCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@@ -121,7 +121,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
121121

122122
// The clientRegistration.redirectUri may contain Uri template variables, whether it's configured by
123123
// the user or configured by default. In these cases, the redirectUri will be expanded and ultimately changed
124-
// (by AuthorizationCodeRequestRedirectFilter) before setting it in the authorization request.
124+
// (by AuthorizationRequestRedirectFilter) before setting it in the authorization request.
125125
// The resulting redirectUri used for the authorization request and saved within the AuthorizationRequestRepository
126126
// MUST BE the same one used to complete the authorization code flow.
127127
// Therefore, we'll create a copy of the clientRegistration and override the redirectUri

0 commit comments

Comments
 (0)