Skip to content

Commit 0994a1e

Browse files
committed
Allow customizing OIDC Provider Configuration Response
Closes gh-616
1 parent 70d433a commit 0994a1e

File tree

6 files changed

+241
-52
lines changed

6 files changed

+241
-52
lines changed

docs/src/docs/asciidoc/protocol-endpoints.adoc

+27-2
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,34 @@ The JWK Set endpoint is configured *only* if a `JWKSource<SecurityContext>` `@Be
198198
[[oidc-provider-configuration-endpoint]]
199199
== OpenID Connect 1.0 Provider Configuration Endpoint
200200

201-
`OidcConfigurer` provides support for the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Connect 1.0 Provider Configuration endpoint].
201+
`OidcProviderConfigurationEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Connect 1.0 Provider Configuration endpoint].
202+
It defines an extension point that lets you customize the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse[OpenID Provider Configuration response].
202203

203-
`OidcConfigurer` configures the `OidcProviderConfigurationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.
204+
`OidcProviderConfigurationEndpointConfigurer` provides the following configuration option:
205+
206+
[source,java]
207+
----
208+
@Bean
209+
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
210+
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
211+
new OAuth2AuthorizationServerConfigurer();
212+
http.apply(authorizationServerConfigurer);
213+
214+
authorizationServerConfigurer
215+
.oidc(oidc ->
216+
oidc
217+
.providerConfigurationEndpoint(providerConfigurationEndpoint ->
218+
providerConfigurationEndpoint
219+
.providerConfigurationCustomizer(providerConfigurationCustomizer) <1>
220+
)
221+
);
222+
223+
return http.build();
224+
}
225+
----
226+
<1> `providerConfigurationCustomizer()`: The `Consumer` providing access to the `OidcProviderConfiguration.Builder` allowing the ability to customize the claims of the OpenID Provider's configuration.
227+
228+
`OidcProviderConfigurationEndpointConfigurer` configures the `OidcProviderConfigurationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.
204229
`OidcProviderConfigurationEndpointFilter` is the `Filter` that returns the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse[OidcProviderConfiguration response].
205230

206231
[[oidc-user-info-endpoint]]

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

+19-32
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@
2020
import java.util.List;
2121
import java.util.Map;
2222

23-
import org.springframework.http.HttpMethod;
2423
import org.springframework.security.config.Customizer;
2524
import org.springframework.security.config.annotation.ObjectPostProcessor;
2625
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27-
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
28-
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
29-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
3026
import org.springframework.security.web.util.matcher.OrRequestMatcher;
3127
import org.springframework.security.web.util.matcher.RequestMatcher;
3228

@@ -36,9 +32,9 @@
3632
* @author Joe Grandja
3733
* @since 0.2.0
3834
* @see OAuth2AuthorizationServerConfigurer#oidc
35+
* @see OidcProviderConfigurationEndpointConfigurer
3936
* @see OidcClientRegistrationEndpointConfigurer
4037
* @see OidcUserInfoEndpointConfigurer
41-
* @see OidcProviderConfigurationEndpointFilter
4238
*/
4339
public final class OidcConfigurer extends AbstractOAuth2Configurer {
4440
private final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
@@ -49,9 +45,22 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
4945
*/
5046
OidcConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {
5147
super(objectPostProcessor);
48+
addConfigurer(OidcProviderConfigurationEndpointConfigurer.class, new OidcProviderConfigurationEndpointConfigurer(objectPostProcessor));
5249
addConfigurer(OidcUserInfoEndpointConfigurer.class, new OidcUserInfoEndpointConfigurer(objectPostProcessor));
5350
}
5451

52+
/**
53+
* Configures the OpenID Connect 1.0 Provider Configuration Endpoint.
54+
*
55+
* @param providerConfigurationEndpointCustomizer the {@link Customizer} providing access to the {@link OidcProviderConfigurationEndpointConfigurer}
56+
* @return the {@link OidcConfigurer} for further configuration
57+
* @since 0.4.0
58+
*/
59+
public OidcConfigurer providerConfigurationEndpoint(Customizer<OidcProviderConfigurationEndpointConfigurer> providerConfigurationEndpointCustomizer) {
60+
providerConfigurationEndpointCustomizer.customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class));
61+
return this;
62+
}
63+
5564
/**
5665
* Configures the OpenID Connect Dynamic Client Registration 1.0 Endpoint.
5766
*
@@ -83,39 +92,17 @@ public OidcConfigurer userInfoEndpoint(Customizer<OidcUserInfoEndpointConfigurer
8392

8493
@Override
8594
void init(HttpSecurity httpSecurity) {
86-
OidcUserInfoEndpointConfigurer userInfoEndpointConfigurer =
87-
getConfigurer(OidcUserInfoEndpointConfigurer.class);
88-
userInfoEndpointConfigurer.init(httpSecurity);
89-
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer =
90-
getConfigurer(OidcClientRegistrationEndpointConfigurer.class);
91-
if (clientRegistrationEndpointConfigurer != null) {
92-
clientRegistrationEndpointConfigurer.init(httpSecurity);
93-
}
94-
9595
List<RequestMatcher> requestMatchers = new ArrayList<>();
96-
requestMatchers.add(new AntPathRequestMatcher(
97-
"/.well-known/openid-configuration", HttpMethod.GET.name()));
98-
requestMatchers.add(userInfoEndpointConfigurer.getRequestMatcher());
99-
if (clientRegistrationEndpointConfigurer != null) {
100-
requestMatchers.add(clientRegistrationEndpointConfigurer.getRequestMatcher());
101-
}
96+
this.configurers.values().forEach(configurer -> {
97+
configurer.init(httpSecurity);
98+
requestMatchers.add(configurer.getRequestMatcher());
99+
});
102100
this.requestMatcher = new OrRequestMatcher(requestMatchers);
103101
}
104102

105103
@Override
106104
void configure(HttpSecurity httpSecurity) {
107-
OidcUserInfoEndpointConfigurer userInfoEndpointConfigurer =
108-
getConfigurer(OidcUserInfoEndpointConfigurer.class);
109-
userInfoEndpointConfigurer.configure(httpSecurity);
110-
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer =
111-
getConfigurer(OidcClientRegistrationEndpointConfigurer.class);
112-
if (clientRegistrationEndpointConfigurer != null) {
113-
clientRegistrationEndpointConfigurer.configure(httpSecurity);
114-
}
115-
116-
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter =
117-
new OidcProviderConfigurationEndpointFilter();
118-
httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
105+
this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
119106
}
120107

121108
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2020-2022 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.config.annotation.web.configurers;
17+
18+
import java.util.function.Consumer;
19+
20+
import org.springframework.http.HttpMethod;
21+
import org.springframework.security.config.annotation.ObjectPostProcessor;
22+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
23+
import org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;
24+
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
25+
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
26+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
27+
import org.springframework.security.web.util.matcher.RequestMatcher;
28+
29+
/**
30+
* Configurer for the OpenID Connect 1.0 Provider Configuration Endpoint.
31+
*
32+
* @author Joe Grandja
33+
* @since 0.4.0
34+
* @see OidcConfigurer#providerConfigurationEndpoint
35+
* @see OidcProviderConfigurationEndpointFilter
36+
*/
37+
public final class OidcProviderConfigurationEndpointConfigurer extends AbstractOAuth2Configurer {
38+
private RequestMatcher requestMatcher;
39+
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer;
40+
private Consumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer;
41+
42+
/**
43+
* Restrict for internal use only.
44+
*/
45+
OidcProviderConfigurationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {
46+
super(objectPostProcessor);
47+
}
48+
49+
/**
50+
* Sets the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
51+
* allowing the ability to customize the claims of the OpenID Provider's configuration.
52+
*
53+
* @param providerConfigurationCustomizer the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
54+
* @return the {@link OidcProviderConfigurationEndpointConfigurer} for further configuration
55+
*/
56+
public OidcProviderConfigurationEndpointConfigurer providerConfigurationCustomizer(
57+
Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer) {
58+
this.providerConfigurationCustomizer = providerConfigurationCustomizer;
59+
return this;
60+
}
61+
62+
void addDefaultProviderConfigurationCustomizer(
63+
Consumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer) {
64+
this.defaultProviderConfigurationCustomizer =
65+
this.defaultProviderConfigurationCustomizer == null ?
66+
defaultProviderConfigurationCustomizer :
67+
this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer);
68+
}
69+
70+
@Override
71+
void init(HttpSecurity httpSecurity) {
72+
this.requestMatcher = new AntPathRequestMatcher(
73+
"/.well-known/openid-configuration", HttpMethod.GET.name());
74+
}
75+
76+
@Override
77+
void configure(HttpSecurity httpSecurity) {
78+
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter =
79+
new OidcProviderConfigurationEndpointFilter();
80+
Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = getProviderConfigurationCustomizer();
81+
if (providerConfigurationCustomizer != null) {
82+
oidcProviderConfigurationEndpointFilter.setProviderConfigurationCustomizer(providerConfigurationCustomizer);
83+
}
84+
httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
85+
}
86+
87+
private Consumer<OidcProviderConfiguration.Builder> getProviderConfigurationCustomizer() {
88+
Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = null;
89+
if (this.defaultProviderConfigurationCustomizer != null || this.providerConfigurationCustomizer != null) {
90+
if (this.defaultProviderConfigurationCustomizer != null) {
91+
providerConfigurationCustomizer = this.defaultProviderConfigurationCustomizer;
92+
}
93+
if (this.providerConfigurationCustomizer != null) {
94+
providerConfigurationCustomizer =
95+
providerConfigurationCustomizer == null ?
96+
this.providerConfigurationCustomizer :
97+
providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer);
98+
}
99+
}
100+
return providerConfigurationCustomizer;
101+
}
102+
103+
@Override
104+
RequestMatcher getRequestMatcher() {
105+
return this.requestMatcher;
106+
}
107+
108+
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java

+20-4
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
4040
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4141
import org.springframework.security.web.util.matcher.RequestMatcher;
42+
import org.springframework.util.Assert;
4243
import org.springframework.web.filter.OncePerRequestFilter;
4344
import org.springframework.web.util.UriComponentsBuilder;
4445

4546
/**
4647
* A {@code Filter} that processes OpenID Provider Configuration Requests.
4748
*
4849
* @author Daniel Garnier-Moiroux
50+
* @author Joe Grandja
4951
* @since 0.1.0
5052
* @see OidcProviderConfiguration
5153
* @see AuthorizationServerSettings
@@ -62,6 +64,19 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
6264
HttpMethod.GET.name());
6365
private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter =
6466
new OidcProviderConfigurationHttpMessageConverter();
67+
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = (providerConfiguration) -> {};
68+
69+
/**
70+
* Sets the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
71+
* allowing the ability to customize the claims of the OpenID Provider's configuration.
72+
*
73+
* @param providerConfigurationCustomizer the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
74+
* @since 0.4.0
75+
*/
76+
public void setProviderConfigurationCustomizer(Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer) {
77+
Assert.notNull(providerConfigurationCustomizer, "providerConfigurationCustomizer cannot be null");
78+
this.providerConfigurationCustomizer = providerConfigurationCustomizer;
79+
}
6580

6681
@Override
6782
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
@@ -76,7 +91,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
7691
String issuer = authorizationServerContext.getIssuer();
7792
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings();
7893

79-
OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder()
94+
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
8095
.issuer(issuer)
8196
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
8297
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
@@ -93,12 +108,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
93108
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
94109
.subjectType("public")
95110
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
96-
.scope(OidcScopes.OPENID)
97-
.build();
111+
.scope(OidcScopes.OPENID);
112+
113+
this.providerConfigurationCustomizer.accept(providerConfiguration);
98114

99115
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
100116
this.providerConfigurationHttpMessageConverter.write(
101-
providerConfiguration, MediaType.APPLICATION_JSON, httpResponse);
117+
providerConfiguration.build(), MediaType.APPLICATION_JSON, httpResponse);
102118
}
103119

104120
private static Consumer<List<String>> clientAuthenticationMethods() {

0 commit comments

Comments
 (0)