Skip to content

Commit 5228576

Browse files
committed
Pass client_secret_expires_at
Closes gh-2111 Signed-off-by: wheleph <[email protected]>
1 parent a59c93c commit 5228576

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public OidcClientRegistration convert(RegisteredClient registeredClient) {
4949
builder.clientSecret(registeredClient.getClientSecret());
5050
}
5151

52+
if (registeredClient.getClientSecretExpiresAt() != null) {
53+
builder.clientSecretExpiresAt(registeredClient.getClientSecretExpiresAt());
54+
}
55+
5256
builder.redirectUris((redirectUris) ->
5357
redirectUris.addAll(registeredClient.getRedirectUris()));
5458

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
1717

18+
import java.time.Duration;
1819
import java.time.Instant;
1920
import java.time.temporal.ChronoUnit;
2021
import java.util.Collections;
@@ -31,6 +32,7 @@
3132
import jakarta.servlet.http.HttpServletResponse;
3233
import okhttp3.mockwebserver.MockResponse;
3334
import okhttp3.mockwebserver.MockWebServer;
35+
import org.assertj.core.data.TemporalUnitWithinOffset;
3436
import org.junit.jupiter.api.AfterAll;
3537
import org.junit.jupiter.api.AfterEach;
3638
import org.junit.jupiter.api.BeforeAll;
@@ -508,6 +510,46 @@ public void requestWhenClientRegistersWithCustomMetadataThenSavedToRegisteredCli
508510
assertThat(registeredClient.getClientSettings().<String>getSetting("non-registered-custom-metadata")).isNull();
509511
}
510512

513+
/**
514+
* Scenario to validate that if there's a customization that sets client secret expiration date, then the date
515+
* is persisted and returned in the registration response
516+
*/
517+
@Test
518+
public void requestWhenClientRegistersWithSecretExpirationThenClientRegistrationResponse() throws Exception {
519+
this.spring.register(ClientSecretExpirationConfiguration.class).autowire();
520+
521+
// @formatter:off
522+
OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
523+
.clientName("client-name")
524+
.redirectUri("https://client.example.com")
525+
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
526+
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
527+
.scope("scope1")
528+
.scope("scope2")
529+
.build();
530+
// @formatter:on
531+
532+
OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
533+
534+
var expectedSecretExpiryDate = Instant.now().plus(Duration.ofHours(24));
535+
var allowedDelta = new TemporalUnitWithinOffset(1, ChronoUnit.MINUTES);
536+
537+
// Returned response contains expiration date
538+
assertThat(clientRegistrationResponse.getClientSecretExpiresAt())
539+
.isNotNull()
540+
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
541+
542+
RegisteredClient registeredClient = this.registeredClientRepository
543+
.findByClientId(clientRegistrationResponse.getClientId());
544+
545+
// Persisted RegisteredClient contains expiration date
546+
assertThat(registeredClient)
547+
.isNotNull();
548+
assertThat(registeredClient.getClientSecretExpiresAt())
549+
.isNotNull()
550+
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
551+
}
552+
511553
private OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {
512554
// ***** (1) Obtain the "initial" access token used for registering the client
513555

@@ -643,8 +685,40 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
643685

644686
@EnableWebSecurity
645687
@Configuration(proxyBeanMethods = false)
646-
static class CustomClientMetadataConfiguration extends AuthorizationServerConfiguration {
688+
static class CustomClientMetadataConfiguration extends ClientRegistrationConvertersConfiguration {
689+
690+
private static final List<String> supportedCustomClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
691+
692+
@Override
693+
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
694+
return new CustomRegisteredClientConverter(supportedCustomClientMetadata);
695+
}
696+
697+
@Override
698+
protected Converter<RegisteredClient, OidcClientRegistration> oidcClientRegistrationConverter() {
699+
return new CustomClientRegistrationConverter(supportedCustomClientMetadata);
700+
}
701+
702+
}
703+
704+
@EnableWebSecurity
705+
@Configuration(proxyBeanMethods = false)
706+
static class ClientSecretExpirationConfiguration extends ClientRegistrationConvertersConfiguration {
707+
708+
@Override
709+
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
710+
return new ClientSecretExpirationRegisteredClientConverter();
711+
}
712+
713+
}
647714

715+
/**
716+
* This test configuration allows to override {@code RegisteredClient} -> {@code OidcClientRegistration} and
717+
* {@code OidcClientRegistration} -> {@code RegisteredClient} converters
718+
*/
719+
@EnableWebSecurity
720+
@Configuration(proxyBeanMethods = false)
721+
static class ClientRegistrationConvertersConfiguration extends AuthorizationServerConfiguration {
648722
// @formatter:off
649723
@Bean
650724
@Override
@@ -674,15 +748,27 @@ private Consumer<List<AuthenticationProvider>> configureClientRegistrationConver
674748
// @formatter:off
675749
return (authenticationProviders) ->
676750
authenticationProviders.forEach((authenticationProvider) -> {
677-
List<String> supportedCustomClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
678751
if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {
679-
provider.setRegisteredClientConverter(new CustomRegisteredClientConverter(supportedCustomClientMetadata));
680-
provider.setClientRegistrationConverter(new CustomClientRegistrationConverter(supportedCustomClientMetadata));
752+
var registeredClientConverter = registeredClientConverter();
753+
if (registeredClientConverter != null) {
754+
provider.setRegisteredClientConverter(registeredClientConverter);
755+
}
756+
var oidcClientRegistrationConverter = oidcClientRegistrationConverter();
757+
if (oidcClientRegistrationConverter != null) {
758+
provider.setClientRegistrationConverter(oidcClientRegistrationConverter);
759+
}
681760
}
682761
});
683762
// @formatter:on
684763
}
685764

765+
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
766+
return null;
767+
}
768+
769+
protected Converter<RegisteredClient, OidcClientRegistration> oidcClientRegistrationConverter() {
770+
return null;
771+
}
686772
}
687773

688774
@EnableWebSecurity
@@ -814,4 +900,26 @@ public OidcClientRegistration convert(RegisteredClient registeredClient) {
814900

815901
}
816902

903+
/**
904+
* This customization adds client secret expiration time by setting {@code RegisteredClient.clientSecretExpiresAt}
905+
* during {@code OidcClientRegistration} -> {@code RegisteredClient} conversion
906+
*/
907+
private static final class ClientSecretExpirationRegisteredClientConverter
908+
implements Converter<OidcClientRegistration, RegisteredClient> {
909+
910+
private static final OidcClientRegistrationRegisteredClientConverter delegate =
911+
new OidcClientRegistrationRegisteredClientConverter();
912+
913+
@Override
914+
public RegisteredClient convert(OidcClientRegistration clientRegistration) {
915+
RegisteredClient registeredClient = delegate.convert(clientRegistration);
916+
var registeredClientBuilder = RegisteredClient.from(registeredClient);
917+
918+
var clientSecretExpiresAt = Instant.now().plus(Duration.ofHours(24));
919+
registeredClientBuilder.clientSecretExpiresAt(clientSecretExpiresAt);
920+
921+
return registeredClientBuilder.build();
922+
}
923+
}
924+
817925
}

0 commit comments

Comments
 (0)