Skip to content

Commit 5c3d5b6

Browse files
author
Dmitriy Dubson
committed
Adds ability to inject custom metadata at client registration
Closes spring-projects#1172
1 parent 3efb4f2 commit 5c3d5b6

File tree

13 files changed

+650
-214
lines changed

13 files changed

+650
-214
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
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
@@ -58,7 +59,7 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
5859
private final Log logger = LogFactory.getLog(getClass());
5960
private final RegisteredClientRepository registeredClientRepository;
6061
private final OAuth2AuthorizationService authorizationService;
61-
private final Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
62+
private Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
6263

6364
/**
6465
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters.
@@ -75,6 +76,17 @@ public OidcClientConfigurationAuthenticationProvider(RegisteredClientRepository
7576
this.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter();
7677
}
7778

79+
/**
80+
* Sets the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}.
81+
*
82+
* @param clientRegistrationConverter the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}
83+
* @since 1.2.0
84+
*/
85+
public void setClientRegistrationConverter(Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
86+
Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
87+
this.clientRegistrationConverter = clientRegistrationConverter;
88+
}
89+
7890
@Override
7991
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
8092
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,121 @@
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+
public class OidcClientRegistrationRegisteredClientConverter implements Converter<OidcClientRegistration, RegisteredClient> {
37+
private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
38+
Base64.getUrlEncoder().withoutPadding(), 32);
39+
private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
40+
Base64.getUrlEncoder().withoutPadding(), 48);
41+
42+
@Override
43+
public RegisteredClient convert(OidcClientRegistration clientRegistration) {
44+
// @formatter:off
45+
RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
46+
.clientId(CLIENT_ID_GENERATOR.generateKey())
47+
.clientIdIssuedAt(Instant.now())
48+
.clientName(clientRegistration.getClientName());
49+
50+
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
51+
builder
52+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
53+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
54+
} else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
55+
builder
56+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
57+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
58+
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
59+
builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
60+
} else {
61+
builder
62+
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
63+
.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
64+
}
65+
66+
builder.redirectUris(redirectUris ->
67+
redirectUris.addAll(clientRegistration.getRedirectUris()));
68+
69+
if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
70+
builder.postLogoutRedirectUris(postLogoutRedirectUris ->
71+
postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
72+
}
73+
74+
if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
75+
builder.authorizationGrantTypes(authorizationGrantTypes ->
76+
clientRegistration.getGrantTypes().forEach(grantType ->
77+
authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
78+
} else {
79+
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
80+
}
81+
if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
82+
clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
83+
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
84+
}
85+
86+
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
87+
builder.scopes(scopes ->
88+
scopes.addAll(clientRegistration.getScopes()));
89+
}
90+
91+
ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
92+
.requireProofKey(true)
93+
.requireAuthorizationConsent(true);
94+
95+
if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
96+
MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
97+
if (macAlgorithm == null) {
98+
macAlgorithm = MacAlgorithm.HS256;
99+
}
100+
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
101+
} else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
102+
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
103+
if (signatureAlgorithm == null) {
104+
signatureAlgorithm = SignatureAlgorithm.RS256;
105+
}
106+
clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
107+
clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
108+
}
109+
110+
builder
111+
.clientSettings(clientSettingsBuilder.build())
112+
.tokenSettings(TokenSettings.builder()
113+
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
114+
.build());
115+
116+
return builder.build();
117+
// @formatter:on
118+
}
119+
120+
}
121+

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @author Joe Grandja
3232
* @since 0.4.0
3333
*/
34-
final class RegisteredClientOidcClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
34+
public class RegisteredClientOidcClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
3535

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

0 commit comments

Comments
 (0)