Skip to content

Commit c6fce40

Browse files
committed
Provide configuration for client authentication
Closes spring-projectsgh-380
1 parent 8ca3917 commit c6fce40

File tree

3 files changed

+278
-39
lines changed

3 files changed

+278
-39
lines changed

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

+17-27
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@
2929
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
3030
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
3131
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
32-
import org.springframework.security.crypto.password.PasswordEncoder;
3332
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
3433
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
35-
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationProvider;
3634
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;
3735
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;
3836
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
@@ -42,7 +40,6 @@
4240
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
4341
import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter;
4442
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter;
45-
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
4643
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;
4744
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
4845
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
@@ -62,6 +59,7 @@
6259
* @author Ovidiu Popa
6360
* @since 0.0.1
6461
* @see AbstractHttpConfigurer
62+
* @see OAuth2ClientAuthenticationConfigurer
6563
* @see OAuth2AuthorizationEndpointConfigurer
6664
* @see OAuth2TokenEndpointConfigurer
6765
* @see RegisteredClientRepository
@@ -72,7 +70,6 @@
7270
* @see NimbusJwkSetEndpointFilter
7371
* @see OidcProviderConfigurationEndpointFilter
7472
* @see OAuth2AuthorizationServerMetadataEndpointFilter
75-
* @see OAuth2ClientAuthenticationFilter
7673
* @see OidcClientRegistrationEndpointFilter
7774
*/
7875
public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBuilder<B>>
@@ -103,7 +100,7 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
103100
*/
104101
public OAuth2AuthorizationServerConfigurer<B> registeredClientRepository(RegisteredClientRepository registeredClientRepository) {
105102
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
106-
this.getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
103+
getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
107104
return this;
108105
}
109106

@@ -115,7 +112,7 @@ public OAuth2AuthorizationServerConfigurer<B> registeredClientRepository(Registe
115112
*/
116113
public OAuth2AuthorizationServerConfigurer<B> authorizationService(OAuth2AuthorizationService authorizationService) {
117114
Assert.notNull(authorizationService, "authorizationService cannot be null");
118-
this.getBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService);
115+
getBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService);
119116
return this;
120117
}
121118

@@ -127,7 +124,7 @@ public OAuth2AuthorizationServerConfigurer<B> authorizationService(OAuth2Authori
127124
*/
128125
public OAuth2AuthorizationServerConfigurer<B> authorizationConsentService(OAuth2AuthorizationConsentService authorizationConsentService) {
129126
Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null");
130-
this.getBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);
127+
getBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);
131128
return this;
132129
}
133130

@@ -139,7 +136,18 @@ public OAuth2AuthorizationServerConfigurer<B> authorizationConsentService(OAuth2
139136
*/
140137
public OAuth2AuthorizationServerConfigurer<B> providerSettings(ProviderSettings providerSettings) {
141138
Assert.notNull(providerSettings, "providerSettings cannot be null");
142-
this.getBuilder().setSharedObject(ProviderSettings.class, providerSettings);
139+
getBuilder().setSharedObject(ProviderSettings.class, providerSettings);
140+
return this;
141+
}
142+
143+
/**
144+
* Configures OAuth 2.0 Client Authentication.
145+
*
146+
* @param clientAuthenticationCustomizer the {@link Customizer} providing access to the {@link OAuth2ClientAuthenticationConfigurer}
147+
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
148+
*/
149+
public OAuth2AuthorizationServerConfigurer<B> clientAuthentication(Customizer<OAuth2ClientAuthenticationConfigurer> clientAuthenticationCustomizer) {
150+
clientAuthenticationCustomizer.customize(getConfigurer(OAuth2ClientAuthenticationConfigurer.class));
143151
return this;
144152
}
145153

@@ -182,16 +190,6 @@ public void init(B builder) {
182190

183191
this.configurers.values().forEach(configurer -> configurer.init(builder));
184192

185-
OAuth2ClientAuthenticationProvider clientAuthenticationProvider =
186-
new OAuth2ClientAuthenticationProvider(
187-
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder),
188-
OAuth2ConfigurerUtils.getAuthorizationService(builder));
189-
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(builder, PasswordEncoder.class);
190-
if (passwordEncoder != null) {
191-
clientAuthenticationProvider.setPasswordEncoder(passwordEncoder);
192-
}
193-
builder.authenticationProvider(postProcess(clientAuthenticationProvider));
194-
195193
OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider =
196194
new OAuth2TokenIntrospectionAuthenticationProvider(
197195
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder),
@@ -245,15 +243,6 @@ public void configure(B builder) {
245243

246244
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
247245

248-
OAuth2ClientAuthenticationFilter clientAuthenticationFilter =
249-
new OAuth2ClientAuthenticationFilter(
250-
authenticationManager,
251-
new OrRequestMatcher(
252-
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
253-
this.tokenIntrospectionEndpointMatcher,
254-
this.tokenRevocationEndpointMatcher));
255-
builder.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class);
256-
257246
OAuth2TokenIntrospectionEndpointFilter tokenIntrospectionEndpointFilter =
258247
new OAuth2TokenIntrospectionEndpointFilter(
259248
authenticationManager,
@@ -276,6 +265,7 @@ public void configure(B builder) {
276265

277266
private Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> createConfigurers() {
278267
Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
268+
configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess));
279269
configurers.put(OAuth2AuthorizationEndpointConfigurer.class, new OAuth2AuthorizationEndpointConfigurer(this::postProcess));
280270
configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));
281271
return configurers;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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.config.annotation.web.configurers.oauth2.server.authorization;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import javax.servlet.http.HttpServletRequest;
22+
23+
import org.springframework.http.HttpMethod;
24+
import org.springframework.security.authentication.AuthenticationManager;
25+
import org.springframework.security.authentication.AuthenticationProvider;
26+
import org.springframework.security.config.annotation.ObjectPostProcessor;
27+
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
28+
import org.springframework.security.core.context.SecurityContext;
29+
import org.springframework.security.crypto.password.PasswordEncoder;
30+
import org.springframework.security.oauth2.core.OAuth2Error;
31+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationProvider;
32+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
33+
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
34+
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
35+
import org.springframework.security.web.authentication.AuthenticationConverter;
36+
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
37+
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
38+
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
39+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
40+
import org.springframework.security.web.util.matcher.OrRequestMatcher;
41+
import org.springframework.security.web.util.matcher.RequestMatcher;
42+
43+
/**
44+
* Configurer for OAuth 2.0 Client Authentication.
45+
*
46+
* @author Joe Grandja
47+
* @since 0.2.0
48+
* @see OAuth2AuthorizationServerConfigurer#clientAuthentication
49+
* @see OAuth2ClientAuthenticationFilter
50+
*/
51+
public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Configurer {
52+
private RequestMatcher requestMatcher;
53+
private AuthenticationConverter authenticationConverter;
54+
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
55+
private AuthenticationSuccessHandler authenticationSuccessHandler;
56+
private AuthenticationFailureHandler errorResponseHandler;
57+
58+
/**
59+
* Restrict for internal use only.
60+
*/
61+
OAuth2ClientAuthenticationConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {
62+
super(objectPostProcessor);
63+
}
64+
65+
/**
66+
* Sets the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
67+
* to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client.
68+
*
69+
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
70+
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
71+
*/
72+
public OAuth2ClientAuthenticationConfigurer authenticationConverter(AuthenticationConverter authenticationConverter) {
73+
this.authenticationConverter = authenticationConverter;
74+
return this;
75+
}
76+
77+
/**
78+
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}.
79+
*
80+
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}
81+
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
82+
*/
83+
public OAuth2ClientAuthenticationConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
84+
this.authenticationProviders.add(authenticationProvider);
85+
return this;
86+
}
87+
88+
/**
89+
* Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication
90+
* and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}.
91+
*
92+
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling a successful client authentication
93+
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
94+
*/
95+
public OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
96+
this.authenticationSuccessHandler = authenticationSuccessHandler;
97+
return this;
98+
}
99+
100+
/**
101+
* Sets the {@link AuthenticationFailureHandler} used for handling a failed client authentication
102+
* and returning the {@link OAuth2Error Error Response}.
103+
*
104+
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling a failed client authentication
105+
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
106+
*/
107+
public OAuth2ClientAuthenticationConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
108+
this.errorResponseHandler = errorResponseHandler;
109+
return this;
110+
}
111+
112+
@Override
113+
<B extends HttpSecurityBuilder<B>> void init(B builder) {
114+
ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);
115+
this.requestMatcher = new OrRequestMatcher(
116+
new AntPathRequestMatcher(
117+
providerSettings.getTokenEndpoint(),
118+
HttpMethod.POST.name()),
119+
new AntPathRequestMatcher(
120+
providerSettings.getTokenIntrospectionEndpoint(),
121+
HttpMethod.POST.name()),
122+
new AntPathRequestMatcher(
123+
providerSettings.getTokenRevocationEndpoint(),
124+
HttpMethod.POST.name()));
125+
126+
List<AuthenticationProvider> authenticationProviders =
127+
!this.authenticationProviders.isEmpty() ?
128+
this.authenticationProviders :
129+
createDefaultAuthenticationProviders(builder);
130+
authenticationProviders.forEach(authenticationProvider ->
131+
builder.authenticationProvider(postProcess(authenticationProvider)));
132+
}
133+
134+
@Override
135+
<B extends HttpSecurityBuilder<B>> void configure(B builder) {
136+
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
137+
OAuth2ClientAuthenticationFilter clientAuthenticationFilter = new OAuth2ClientAuthenticationFilter(
138+
authenticationManager, this.requestMatcher);
139+
if (this.authenticationConverter != null) {
140+
clientAuthenticationFilter.setAuthenticationConverter(this.authenticationConverter);
141+
}
142+
if (this.authenticationSuccessHandler != null) {
143+
clientAuthenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
144+
}
145+
if (this.errorResponseHandler != null) {
146+
clientAuthenticationFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
147+
}
148+
builder.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class);
149+
}
150+
151+
@Override
152+
RequestMatcher getRequestMatcher() {
153+
return this.requestMatcher;
154+
}
155+
156+
private <B extends HttpSecurityBuilder<B>> List<AuthenticationProvider> createDefaultAuthenticationProviders(B builder) {
157+
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
158+
159+
OAuth2ClientAuthenticationProvider clientAuthenticationProvider =
160+
new OAuth2ClientAuthenticationProvider(
161+
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder),
162+
OAuth2ConfigurerUtils.getAuthorizationService(builder));
163+
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(builder, PasswordEncoder.class);
164+
if (passwordEncoder != null) {
165+
clientAuthenticationProvider.setPasswordEncoder(passwordEncoder);
166+
}
167+
authenticationProviders.add(clientAuthenticationProvider);
168+
169+
return authenticationProviders;
170+
}
171+
172+
}

0 commit comments

Comments
 (0)