Allow custom token settings for OAuth 2.0 dynamic client registration

Closes gh-18870
This commit is contained in:
Joe Grandja 2026-03-10 07:22:35 -04:00
parent 16cc1dd8d6
commit c7235ec0a3
2 changed files with 86 additions and 0 deletions

View File

@ -411,6 +411,30 @@ public class OAuth2ClientRegistrationTests {
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
}
@Test
public void requestWhenClientRegistersWithCustomTokenSettingsThenSavedToRegisteredClient() throws Exception {
this.spring.register(CustomTokenSettingsConfiguration.class).autowire();
// @formatter:off
OAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.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
OAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
RegisteredClient registeredClient = this.registeredClientRepository
.findByClientId(clientRegistrationResponse.getClientId());
assertThat(registeredClient).isNotNull();
assertThat(registeredClient.getTokenSettings().getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
}
private OAuth2ClientRegistration registerClient(OAuth2ClientRegistration clientRegistration) throws Exception {
// ***** (1) Obtain the "initial" access token used for registering the client
@ -600,6 +624,44 @@ public class OAuth2ClientRegistrationTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class CustomTokenSettingsConfiguration extends AuthorizationServerConfiguration {
// @formatter:off
@Bean
@Override
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(configureClientRegistrationConverters())
)
)
.authorizeHttpRequests((authorize) ->
authorize.anyRequest().authenticated()
);
return http.build();
}
// @formatter:on
private Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {
// @formatter:off
return (authenticationProviders) ->
authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof OAuth2ClientRegistrationAuthenticationProvider provider) {
OAuth2ClientRegistrationRegisteredClientConverter clientRegistrationRegisteredClientConverter = new OAuth2ClientRegistrationRegisteredClientConverter();
clientRegistrationRegisteredClientConverter.setTokenSettingsCustomizer((tokenSettings) -> tokenSettings.accessTokenTimeToLive(Duration.ofMinutes(60)));
provider.setRegisteredClientConverter(clientRegistrationRegisteredClientConverter);
}
});
// @formatter:on
}
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class OpenClientRegistrationConfiguration extends AuthorizationServerConfiguration {

View File

@ -19,6 +19,7 @@ package org.springframework.security.oauth2.server.authorization.converter;
import java.time.Instant;
import java.util.Base64;
import java.util.UUID;
import java.util.function.Consumer;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
@ -29,6 +30,8 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResp
import org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
@ -47,6 +50,9 @@ public final class OAuth2ClientRegistrationRegisteredClientConverter
private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
Base64.getUrlEncoder().withoutPadding(), 48);
private Consumer<TokenSettings.Builder> tokenSettingsCustomizer = (tokenSettings) -> {
};
@Override
public RegisteredClient convert(OAuth2ClientRegistration clientRegistration) {
// @formatter:off
@ -103,8 +109,26 @@ public final class OAuth2ClientRegistrationRegisteredClientConverter
builder
.clientSettings(clientSettingsBuilder.build());
TokenSettings.Builder tokenSettingsBuilder = TokenSettings.builder();
this.tokenSettingsCustomizer.accept(tokenSettingsBuilder);
builder
.tokenSettings(tokenSettingsBuilder.build());
return builder.build();
// @formatter:on
}
/**
* Sets the {@code Consumer} providing access to the {@link TokenSettings.Builder}
* allowing the ability to customize the token configuration settings.
* @param tokenSettingsCustomizer the {@code Consumer} providing access to the
* {@link TokenSettings.Builder}
* @since 7.1
*/
public void setTokenSettingsCustomizer(Consumer<TokenSettings.Builder> tokenSettingsCustomizer) {
Assert.notNull(tokenSettingsCustomizer, "tokenSettingsCustomizer cannot be null");
this.tokenSettingsCustomizer = tokenSettingsCustomizer;
}
}