-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix to save after encoding the secret when registering the client #1056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2020-2022 the original author or authors. | ||
* Copyright 2020-2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -34,8 +34,10 @@ | |
import org.springframework.security.authentication.AuthenticationProvider; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.crypto.factory.PasswordEncoderFactories; | ||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; | ||
import org.springframework.security.crypto.keygen.StringKeyGenerator; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.oauth2.core.AuthorizationGrantType; | ||
import org.springframework.security.oauth2.core.ClaimAccessor; | ||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | ||
|
@@ -79,6 +81,7 @@ | |
* @see OAuth2TokenGenerator | ||
* @see OidcClientRegistrationAuthenticationToken | ||
* @see OidcClientConfigurationAuthenticationProvider | ||
* @see PasswordEncoder | ||
* @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration">3. Client Registration Endpoint</a> | ||
*/ | ||
public final class OidcClientRegistrationAuthenticationProvider implements AuthenticationProvider { | ||
|
@@ -91,6 +94,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe | |
private final Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter; | ||
private Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter; | ||
|
||
private PasswordEncoder passwordEncoder; | ||
|
||
/** | ||
* Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the provided parameters. | ||
* | ||
|
@@ -109,6 +114,21 @@ public OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository r | |
this.tokenGenerator = tokenGenerator; | ||
this.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter(); | ||
this.registeredClientConverter = new OidcClientRegistrationRegisteredClientConverter(); | ||
this.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); | ||
} | ||
|
||
/** | ||
* Sets the {@link PasswordEncoder} used to encode the clientSecret | ||
* the {@link RegisteredClient#getClientSecret() client secret}. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update the javadoc as the passwordencoder is not used to validate the clientSecret but instead used to encode the clientSecret. |
||
* If not set, the client secret will be encoded using | ||
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}. | ||
* | ||
* @param passwordEncoder the {@link PasswordEncoder} used to encode the clientSecret | ||
* @since 1.1.0 | ||
*/ | ||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { | ||
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); | ||
this.passwordEncoder = passwordEncoder; | ||
} | ||
|
||
@Override | ||
|
@@ -183,7 +203,21 @@ private OidcClientRegistrationAuthenticationToken registerClient(OidcClientRegis | |
} | ||
|
||
RegisteredClient registeredClient = this.registeredClientConverter.convert(clientRegistrationAuthentication.getClientRegistration()); | ||
this.registeredClientRepository.save(registeredClient); | ||
|
||
// When secret exists, copy RegisteredClient and encode only secret | ||
String rawClientSecret = registeredClient.getClientSecret(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The extension point for customizing client metadata before it's saved to |
||
String clientSecret = null; | ||
RegisteredClient saveRegisteredClient = null; | ||
if (rawClientSecret != null) { | ||
clientSecret = passwordEncoder.encode(rawClientSecret); | ||
saveRegisteredClient = RegisteredClient.from(registeredClient) | ||
.clientSecret(clientSecret) | ||
.build(); | ||
} else { | ||
saveRegisteredClient = registeredClient; | ||
} | ||
|
||
this.registeredClientRepository.save(saveRegisteredClient); | ||
|
||
if (this.logger.isTraceEnabled()) { | ||
this.logger.trace("Saved registered client"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2020-2022 the original author or authors. | ||
* Copyright 2020-2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -57,7 +57,7 @@ | |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; | ||
import org.springframework.security.crypto.password.NoOpPasswordEncoder; | ||
import org.springframework.security.crypto.factory.PasswordEncoderFactories; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.oauth2.core.AuthorizationGrantType; | ||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | ||
|
@@ -112,6 +112,7 @@ | |
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.verifyNoInteractions; | ||
import static org.mockito.Mockito.when; | ||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; | ||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||
|
@@ -290,7 +291,6 @@ public void requestWhenClientConfigurationRequestAuthorizedThenClientRegistratio | |
|
||
assertThat(clientConfigurationResponse.getClientId()).isEqualTo(clientRegistrationResponse.getClientId()); | ||
assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isEqualTo(clientRegistrationResponse.getClientIdIssuedAt()); | ||
assertThat(clientConfigurationResponse.getClientSecret()).isEqualTo(clientRegistrationResponse.getClientSecret()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isEqualTo(clientRegistrationResponse.getClientSecretExpiresAt()); | ||
assertThat(clientConfigurationResponse.getClientName()).isEqualTo(clientRegistrationResponse.getClientName()); | ||
assertThat(clientConfigurationResponse.getRedirectUris()) | ||
|
@@ -358,6 +358,34 @@ public void requestWhenClientRegistrationEndpointCustomizedThenUsed() throws Exc | |
verifyNoInteractions(authenticationFailureHandler); | ||
} | ||
|
||
// gh-1056 | ||
@Test | ||
public void requestWhenClientRegistersWithSecretThenClientAuthenticationSuccess() throws Exception { | ||
this.spring.register(AuthorizationServerConfiguration.class).autowire(); | ||
|
||
// @formatter:off | ||
OidcClientRegistration clientRegistration = OidcClientRegistration.builder() | ||
.clientName("client-name") | ||
.redirectUri("https://client.example.com") | ||
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) | ||
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) | ||
.scope("scope1") | ||
.scope("scope2") | ||
.build(); | ||
// @formatter:on | ||
|
||
OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration); | ||
|
||
MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) | ||
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) | ||
.param(OAuth2ParameterNames.SCOPE, "scope1") | ||
.with(httpBasic(clientRegistrationResponse.getClientId(), clientRegistrationResponse.getClientSecret()))) | ||
.andExpect(status().isOk()) | ||
.andExpect(jsonPath("$.access_token").isNotEmpty()) | ||
.andExpect(jsonPath("$.scope").value("scope1")) | ||
.andReturn(); | ||
} | ||
|
||
@Test | ||
public void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() throws Exception { | ||
this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); | ||
|
@@ -566,9 +594,8 @@ AuthorizationServerSettings authorizationServerSettings() { | |
|
||
@Bean | ||
PasswordEncoder passwordEncoder() { | ||
return NoOpPasswordEncoder.getInstance(); | ||
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); | ||
} | ||
|
||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update copyright year to 2023