Skip to content

Commit 7facc38

Browse files
committed
Adds ability to inject custom metadata at client registration
Fixes spring-projectsgh-1172
1 parent 3efb4f2 commit 7facc38

File tree

7 files changed

+246
-210
lines changed

7 files changed

+246
-210
lines changed

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,19 +46,21 @@
4646
* @author Ovidiu Popa
4747
* @author Joe Grandja
4848
* @author Rafal Lewczuk
49+
* @author Dmitriy Dubson
4950
* @since 0.4.0
5051
* @see RegisteredClientRepository
5152
* @see OAuth2AuthorizationService
5253
* @see OidcClientRegistrationAuthenticationToken
5354
* @see OidcClientRegistrationAuthenticationProvider
55+
* @see RegisteredClientOidcClientRegistrationConverter
5456
* @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint">4. Client Configuration Endpoint</a>
5557
*/
5658
public final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider {
5759
static final String DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE = "client.read";
5860
private final Log logger = LogFactory.getLog(getClass());
5961
private final RegisteredClientRepository registeredClientRepository;
6062
private final OAuth2AuthorizationService authorizationService;
61-
private final Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
63+
private Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
6264

6365
/**
6466
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters.
@@ -75,6 +77,17 @@ public OidcClientConfigurationAuthenticationProvider(RegisteredClientRepository
7577
this.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter();
7678
}
7779

80+
/**
81+
* Sets the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}.
82+
*
83+
* @param clientRegistrationConverter the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}
84+
* @since 1.2.0
85+
*/
86+
public void setClientRegistrationConverter(Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
87+
Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
88+
this.clientRegistrationConverter = clientRegistrationConverter;
89+
}
90+
7891
@Override
7992
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
8093
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication =

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

Lines changed: 13 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@
1717

1818
import java.net.URI;
1919
import java.net.URISyntaxException;
20-
import java.time.Instant;
21-
import java.util.Base64;
2220
import java.util.Collection;
2321
import java.util.Collections;
2422
import java.util.HashSet;
2523
import java.util.List;
2624
import java.util.Map;
2725
import java.util.Set;
28-
import java.util.UUID;
2926

3027
import org.apache.commons.logging.Log;
3128
import org.apache.commons.logging.LogFactory;
@@ -35,8 +32,6 @@
3532
import org.springframework.security.core.Authentication;
3633
import org.springframework.security.core.AuthenticationException;
3734
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
38-
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
39-
import org.springframework.security.crypto.keygen.StringKeyGenerator;
4035
import org.springframework.security.crypto.password.PasswordEncoder;
4136
import org.springframework.security.oauth2.core.AuthorizationGrantType;
4237
import org.springframework.security.oauth2.core.ClaimAccessor;
@@ -46,7 +41,6 @@
4641
import org.springframework.security.oauth2.core.OAuth2Error;
4742
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
4843
import org.springframework.security.oauth2.core.OAuth2Token;
49-
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
5044
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
5145
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
5246
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
@@ -59,8 +53,6 @@
5953
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
6054
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientMetadataClaimNames;
6155
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;
62-
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
63-
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
6456
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
6557
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
6658
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
@@ -75,6 +67,7 @@
7567
* @author Ovidiu Popa
7668
* @author Joe Grandja
7769
* @author Rafal Lewczuk
70+
* @author Dmitriy Dubson
7871
* @since 0.1.1
7972
* @see RegisteredClientRepository
8073
* @see OAuth2AuthorizationService
@@ -91,7 +84,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
9184
private final RegisteredClientRepository registeredClientRepository;
9285
private final OAuth2AuthorizationService authorizationService;
9386
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
94-
private final Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
87+
private Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
9588
private Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter;
9689
private PasswordEncoder passwordEncoder;
9790

@@ -172,6 +165,17 @@ public void setRegisteredClientConverter(Converter<OidcClientRegistration, Regis
172165
this.registeredClientConverter = registeredClientConverter;
173166
}
174167

168+
/**
169+
* Sets the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}.
170+
*
171+
* @param clientRegistrationConverter the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}
172+
* @since 1.2.0
173+
*/
174+
public void setClientRegistrationConverter(Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
175+
Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
176+
this.clientRegistrationConverter = clientRegistrationConverter;
177+
}
178+
175179
/**
176180
* Sets the {@link PasswordEncoder} used to encode the {@link RegisteredClient#getClientSecret() client secret}.
177181
* If not set, the client secret will be encoded using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.
@@ -368,89 +372,4 @@ private static void throwInvalidClientRegistration(String errorCode, String fiel
368372
throw new OAuth2AuthenticationException(error);
369373
}
370374

371-
private static final class OidcClientRegistrationRegisteredClientConverter implements Converter<OidcClientRegistration, RegisteredClient> {
372-
private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
373-
Base64.getUrlEncoder().withoutPadding(), 32);
374-
private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
375-
Base64.getUrlEncoder().withoutPadding(), 48);
376-
377-
@Override
378-
public RegisteredClient convert(OidcClientRegistration clientRegistration) {
379-
// @formatter:off
380-
RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
381-
.clientId(CLIENT_ID_GENERATOR.generateKey())
382-
.clientIdIssuedAt(Instant.now())
383-
.clientName(clientRegistration.getClientName());
384-
385-
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
386-
builder
387-
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
388-
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
389-
} else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
390-
builder
391-
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
392-
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
393-
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
394-
builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
395-
} else {
396-
builder
397-
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
398-
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
399-
}
400-
401-
builder.redirectUris(redirectUris ->
402-
redirectUris.addAll(clientRegistration.getRedirectUris()));
403-
404-
if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
405-
builder.postLogoutRedirectUris(postLogoutRedirectUris ->
406-
postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
407-
}
408-
409-
if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
410-
builder.authorizationGrantTypes(authorizationGrantTypes ->
411-
clientRegistration.getGrantTypes().forEach(grantType ->
412-
authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
413-
} else {
414-
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
415-
}
416-
if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
417-
clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
418-
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
419-
}
420-
421-
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
422-
builder.scopes(scopes ->
423-
scopes.addAll(clientRegistration.getScopes()));
424-
}
425-
426-
ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
427-
.requireProofKey(true)
428-
.requireAuthorizationConsent(true);
429-
430-
if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
431-
MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
432-
if (macAlgorithm == null) {
433-
macAlgorithm = MacAlgorithm.HS256;
434-
}
435-
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
436-
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
437-
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
438-
if (signatureAlgorithm == null) {
439-
signatureAlgorithm = SignatureAlgorithm.RS256;
440-
}
441-
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
442-
clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
443-
}
444-
445-
builder
446-
.clientSettings(clientSettingsBuilder.build())
447-
.tokenSettings(TokenSettings.builder()
448-
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
449-
.build());
450-
451-
return builder.build();
452-
// @formatter:on
453-
}
454-
455-
}
456375
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2020-2023 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 org.springframework.core.convert.converter.Converter;
19+
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
20+
import org.springframework.security.crypto.keygen.StringKeyGenerator;
21+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
22+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
23+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
24+
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
25+
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
26+
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
27+
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;
28+
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
29+
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
30+
import org.springframework.util.CollectionUtils;
31+
32+
import java.time.Instant;
33+
import java.util.Base64;
34+
import java.util.UUID;
35+
36+
/**
37+
* @author Joe Grandja
38+
* @author Dmitriy Dubson
39+
* @since 1.2.0
40+
*/
41+
public final class OidcClientRegistrationRegisteredClientConverter implements Converter<OidcClientRegistration, RegisteredClient> {
42+
private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
43+
Base64.getUrlEncoder().withoutPadding(), 32);
44+
private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
45+
Base64.getUrlEncoder().withoutPadding(), 48);
46+
47+
@Override
48+
public RegisteredClient convert(OidcClientRegistration clientRegistration) {
49+
// @formatter:off
50+
RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
51+
.clientId(CLIENT_ID_GENERATOR.generateKey())
52+
.clientIdIssuedAt(Instant.now())
53+
.clientName(clientRegistration.getClientName());
54+
55+
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
56+
builder
57+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
58+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
59+
} else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
60+
builder
61+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
62+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
63+
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
64+
builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
65+
} else {
66+
builder
67+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
68+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
69+
}
70+
71+
builder.redirectUris(redirectUris ->
72+
redirectUris.addAll(clientRegistration.getRedirectUris()));
73+
74+
if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
75+
builder.postLogoutRedirectUris(postLogoutRedirectUris ->
76+
postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
77+
}
78+
79+
if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
80+
builder.authorizationGrantTypes(authorizationGrantTypes ->
81+
clientRegistration.getGrantTypes().forEach(grantType ->
82+
authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
83+
} else {
84+
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
85+
}
86+
if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
87+
clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
88+
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
89+
}
90+
91+
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
92+
builder.scopes(scopes ->
93+
scopes.addAll(clientRegistration.getScopes()));
94+
}
95+
96+
ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
97+
.requireProofKey(true)
98+
.requireAuthorizationConsent(true);
99+
100+
if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
101+
MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
102+
if (macAlgorithm == null) {
103+
macAlgorithm = MacAlgorithm.HS256;
104+
}
105+
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
106+
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
107+
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
108+
if (signatureAlgorithm == null) {
109+
signatureAlgorithm = SignatureAlgorithm.RS256;
110+
}
111+
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
112+
clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
113+
}
114+
115+
builder
116+
.clientSettings(clientSettingsBuilder.build())
117+
.tokenSettings(TokenSettings.builder()
118+
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
119+
.build());
120+
121+
return builder.build();
122+
// @formatter:on
123+
}
124+
125+
}
126+

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929

3030
/**
3131
* @author Joe Grandja
32-
* @since 0.4.0
32+
* @since 1.2.0
3333
*/
34-
final class RegisteredClientOidcClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
34+
public final class RegisteredClientOidcClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
3535

3636
@Override
3737
public OidcClientRegistration convert(RegisteredClient registeredClient) {

0 commit comments

Comments
 (0)