Skip to content

Commit cbdd5cc

Browse files
committed
1 parent 8e8979a commit cbdd5cc

File tree

9 files changed

+586
-35
lines changed

9 files changed

+586
-35
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcClientRegistrationEndpointConfigurer.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import org.springframework.security.config.annotation.ObjectPostProcessor;
2121
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2222
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
23+
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider;
2324
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider;
25+
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientConfigurationEndpointFilter;
2426
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter;
2527
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
2628
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
29+
import org.springframework.security.web.util.matcher.OrRequestMatcher;
2730
import org.springframework.security.web.util.matcher.RequestMatcher;
2831

2932
/**
@@ -47,14 +50,21 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
4750
@Override
4851
<B extends HttpSecurityBuilder<B>> void init(B builder) {
4952
ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);
50-
this.requestMatcher = new AntPathRequestMatcher(
51-
providerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.POST.name());
53+
this.requestMatcher = new OrRequestMatcher(
54+
new AntPathRequestMatcher(providerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.POST.name()),
55+
new AntPathRequestMatcher(providerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.GET.name())
56+
);
5257

5358
OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider =
5459
new OidcClientRegistrationAuthenticationProvider(
5560
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder),
5661
OAuth2ConfigurerUtils.getAuthorizationService(builder));
62+
OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider =
63+
new OidcClientConfigurationAuthenticationProvider(
64+
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder),
65+
OAuth2ConfigurerUtils.getAuthorizationService(builder));
5766
builder.authenticationProvider(postProcess(oidcClientRegistrationAuthenticationProvider));
67+
builder.authenticationProvider(postProcess(oidcClientConfigurationAuthenticationProvider));
5868
}
5969

6070
@Override
@@ -66,7 +76,12 @@ <B extends HttpSecurityBuilder<B>> void configure(B builder) {
6676
new OidcClientRegistrationEndpointFilter(
6777
authenticationManager,
6878
providerSettings.getOidcClientRegistrationEndpoint());
79+
OidcClientConfigurationEndpointFilter oidcClientConfigurationEndpointFilter =
80+
new OidcClientConfigurationEndpointFilter(
81+
authenticationManager,
82+
providerSettings.getOidcClientRegistrationEndpoint());
6983
builder.addFilterAfter(postProcess(oidcClientRegistrationEndpointFilter), FilterSecurityInterceptor.class);
84+
builder.addFilterAfter(postProcess(oidcClientConfigurationEndpointFilter), FilterSecurityInterceptor.class);
7085
}
7186

7287
@Override

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java

+37
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@
1717

1818
import org.springframework.security.authentication.AuthenticationProvider;
1919
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
20+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
2021
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
22+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
23+
import org.springframework.security.oauth2.core.oidc.OidcClientRegistration;
2124
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
2225
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
26+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
27+
import org.springframework.util.CollectionUtils;
2328

2429
/**
2530
* Utility methods for the OpenID Connect 1.0 {@link AuthenticationProvider}'s.
@@ -60,4 +65,36 @@ static <T extends AbstractOAuth2Token> OAuth2Authorization invalidate(
6065

6166
return authorizationBuilder.build();
6267
}
68+
69+
static OidcClientRegistration convert(RegisteredClient registeredClient) {
70+
// @formatter:off
71+
OidcClientRegistration.Builder builder = OidcClientRegistration.builder()
72+
.clientId(registeredClient.getClientId())
73+
.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())
74+
.clientSecret(registeredClient.getClientSecret())
75+
.clientName(registeredClient.getClientName());
76+
77+
builder.redirectUris(redirectUris ->
78+
redirectUris.addAll(registeredClient.getRedirectUris()));
79+
80+
builder.grantTypes(grantTypes ->
81+
registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType ->
82+
grantTypes.add(authorizationGrantType.getValue())));
83+
84+
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
85+
builder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());
86+
}
87+
88+
if (!CollectionUtils.isEmpty(registeredClient.getScopes())) {
89+
builder.scopes(scopes ->
90+
scopes.addAll(registeredClient.getScopes()));
91+
}
92+
93+
builder
94+
.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue())
95+
.idTokenSignedResponseAlgorithm(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName());
96+
97+
return builder.build();
98+
// @formatter:on
99+
}
63100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2020-2021 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+
* https://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.server.authorization.oidc.authentication;
17+
18+
import java.util.Collection;
19+
20+
import org.springframework.security.authentication.AuthenticationProvider;
21+
import org.springframework.security.core.Authentication;
22+
import org.springframework.security.core.AuthenticationException;
23+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
24+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
25+
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
26+
import org.springframework.security.oauth2.core.OAuth2TokenType;
27+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
28+
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
29+
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
30+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
31+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
32+
import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
33+
import org.springframework.util.Assert;
34+
35+
/**
36+
* An {@link AuthenticationProvider} implementation for OpenID Connect Client Configuration 1.0.
37+
*
38+
* @author Ovidiu Popa
39+
* @since 0.2.1
40+
* @see RegisteredClientRepository
41+
* @see OAuth2AuthorizationService
42+
* @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint">4. Client Configuration Endpoint</a>
43+
*/
44+
public final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider {
45+
46+
private static final String DEFAULT_AUTHORIZED_SCOPE = "client.read";
47+
48+
private final RegisteredClientRepository registeredClientRepository;
49+
private final OAuth2AuthorizationService authorizationService;
50+
51+
/**
52+
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters.
53+
*
54+
* @param registeredClientRepository the repository of registered clients
55+
* @param authorizationService the authorization service
56+
*/
57+
public OidcClientConfigurationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
58+
OAuth2AuthorizationService authorizationService) {
59+
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
60+
Assert.notNull(authorizationService, "authorizationService cannot be null");
61+
this.registeredClientRepository = registeredClientRepository;
62+
this.authorizationService = authorizationService;
63+
}
64+
65+
@Override
66+
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
67+
OidcClientConfigurationAuthenticationToken clientConfigurationAuthentication =
68+
(OidcClientConfigurationAuthenticationToken) authentication;
69+
70+
// Validate the "initial" access token
71+
AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;
72+
if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientConfigurationAuthentication.getPrincipal().getClass())) {
73+
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientConfigurationAuthentication.getPrincipal();
74+
}
75+
if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {
76+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
77+
}
78+
79+
String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();
80+
81+
OAuth2Authorization authorization = this.authorizationService.findByToken(
82+
accessTokenValue, OAuth2TokenType.ACCESS_TOKEN);
83+
if (authorization == null) {
84+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
85+
}
86+
87+
OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();
88+
if (!authorizedAccessToken.isActive()) {
89+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
90+
}
91+
92+
if (!isAuthorized(authorizedAccessToken)) {
93+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
94+
}
95+
96+
RegisteredClient registeredClient = this.registeredClientRepository
97+
.findByClientId(clientConfigurationAuthentication.getClientId());
98+
99+
if (registeredClient == null) {
100+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
101+
}
102+
103+
// Invalidate the "initial" access token as it can only be used once
104+
authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorizedAccessToken.getToken());
105+
if (authorization.getRefreshToken() != null) {
106+
authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getRefreshToken().getToken());
107+
}
108+
109+
this.authorizationService.save(authorization);
110+
111+
return new OidcClientConfigurationAuthenticationToken(
112+
accessTokenAuthentication, OidcAuthenticationProviderUtils.convert(registeredClient));
113+
}
114+
115+
@Override
116+
public boolean supports(Class<?> authentication) {
117+
return OidcClientConfigurationAuthenticationToken.class.isAssignableFrom(authentication);
118+
}
119+
120+
@SuppressWarnings("unchecked")
121+
private static boolean isAuthorized(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken) {
122+
Object scope = authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);
123+
return scope != null && ((Collection<String>) scope).contains(DEFAULT_AUTHORIZED_SCOPE);
124+
}
125+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2020-2021 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+
* https://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.server.authorization.oidc.authentication;
17+
18+
import java.util.Collections;
19+
20+
import org.springframework.lang.Nullable;
21+
import org.springframework.security.authentication.AbstractAuthenticationToken;
22+
import org.springframework.security.core.Authentication;
23+
import org.springframework.security.oauth2.core.Version;
24+
import org.springframework.security.oauth2.core.oidc.OidcClientRegistration;
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* An {@link Authentication} implementation used for OpenID Connect Client Configuration 1.0.
29+
*
30+
* @author Ovidiu Popa
31+
* @since 0.2.1
32+
* @see AbstractAuthenticationToken
33+
* @see OidcClientRegistration
34+
* @see OidcClientConfigurationAuthenticationProvider
35+
*/
36+
public class OidcClientConfigurationAuthenticationToken extends AbstractAuthenticationToken {
37+
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
38+
private final Authentication principal;
39+
private final String clientId;
40+
private final OidcClientRegistration clientRegistration;
41+
42+
/**
43+
* Constructs an {@code OidcClientConfigurationAuthenticationToken} using the provided parameters.
44+
*
45+
* @param principal the authenticated principal
46+
* @param clientId the client identifier
47+
*/
48+
public OidcClientConfigurationAuthenticationToken(Authentication principal, @Nullable String clientId) {
49+
super(Collections.emptyList());
50+
Assert.notNull(principal, "principal cannot be null");
51+
Assert.hasText(clientId, "clientId cannot be empty");
52+
this.principal = principal;
53+
this.clientId = clientId;
54+
this.clientRegistration = null;
55+
setAuthenticated(principal.isAuthenticated());
56+
}
57+
58+
/**
59+
* Constructs an {@code OidcClientConfigurationAuthenticationToken} using the provided parameters.
60+
*
61+
* @param principal the authenticated principal
62+
* @param clientRegistration the client registration
63+
*/
64+
public OidcClientConfigurationAuthenticationToken(Authentication principal,
65+
OidcClientRegistration clientRegistration) {
66+
super(Collections.emptyList());
67+
Assert.notNull(principal, "principal cannot be null");
68+
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
69+
this.principal = principal;
70+
this.clientId = clientRegistration.getClientId();
71+
this.clientRegistration = clientRegistration;
72+
setAuthenticated(principal.isAuthenticated());
73+
}
74+
75+
@Override
76+
public Object getPrincipal() {
77+
return this.principal;
78+
}
79+
80+
@Override
81+
public Object getCredentials() {
82+
return "";
83+
}
84+
85+
/**
86+
* Returns the client registration.
87+
*
88+
* @return the client registration
89+
*/
90+
public OidcClientRegistration getClientRegistration() {
91+
return this.clientRegistration;
92+
}
93+
94+
/**
95+
* Returns the client identifier.
96+
*
97+
* @return the client identifier
98+
*/
99+
public String getClientId() {
100+
return this.clientId;
101+
}
102+
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java

+1-33
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
128128
this.authorizationService.save(authorization);
129129

130130
return new OidcClientRegistrationAuthenticationToken(
131-
accessTokenAuthentication, convert(registeredClient));
131+
accessTokenAuthentication, OidcAuthenticationProviderUtils.convert(registeredClient));
132132
}
133133

134134
@Override
@@ -208,36 +208,4 @@ private static RegisteredClient create(OidcClientRegistration clientRegistration
208208
// @formatter:on
209209
}
210210

211-
private static OidcClientRegistration convert(RegisteredClient registeredClient) {
212-
// @formatter:off
213-
OidcClientRegistration.Builder builder = OidcClientRegistration.builder()
214-
.clientId(registeredClient.getClientId())
215-
.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())
216-
.clientSecret(registeredClient.getClientSecret())
217-
.clientName(registeredClient.getClientName());
218-
219-
builder.redirectUris(redirectUris ->
220-
redirectUris.addAll(registeredClient.getRedirectUris()));
221-
222-
builder.grantTypes(grantTypes ->
223-
registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType ->
224-
grantTypes.add(authorizationGrantType.getValue())));
225-
226-
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
227-
builder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());
228-
}
229-
230-
if (!CollectionUtils.isEmpty(registeredClient.getScopes())) {
231-
builder.scopes(scopes ->
232-
scopes.addAll(registeredClient.getScopes()));
233-
}
234-
235-
builder
236-
.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue())
237-
.idTokenSignedResponseAlgorithm(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName());
238-
239-
return builder.build();
240-
// @formatter:on
241-
}
242-
243211
}

0 commit comments

Comments
 (0)