Browse Source

Pass client_secret_expires_at

Closes gh-2111

Signed-off-by: wheleph <wheleph@gmail.com>
pull/2134/head
wheleph 6 months ago
parent
commit
5228576d07
  1. 4
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/RegisteredClientOidcClientRegistrationConverter.java
  2. 116
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java

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

@ -49,6 +49,10 @@ public final class RegisteredClientOidcClientRegistrationConverter @@ -49,6 +49,10 @@ public final class RegisteredClientOidcClientRegistrationConverter
builder.clientSecret(registeredClient.getClientSecret());
}
if (registeredClient.getClientSecretExpiresAt() != null) {
builder.clientSecretExpiresAt(registeredClient.getClientSecretExpiresAt());
}
builder.redirectUris((redirectUris) ->
redirectUris.addAll(registeredClient.getRedirectUris()));

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

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
@ -31,6 +32,7 @@ import com.nimbusds.jose.proc.SecurityContext; @@ -31,6 +32,7 @@ import com.nimbusds.jose.proc.SecurityContext;
import jakarta.servlet.http.HttpServletResponse;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.assertj.core.data.TemporalUnitWithinOffset;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
@ -508,6 +510,46 @@ public class OidcClientRegistrationTests { @@ -508,6 +510,46 @@ public class OidcClientRegistrationTests {
assertThat(registeredClient.getClientSettings().<String>getSetting("non-registered-custom-metadata")).isNull();
}
/**
* Scenario to validate that if there's a customization that sets client secret expiration date, then the date
* is persisted and returned in the registration response
*/
@Test
public void requestWhenClientRegistersWithSecretExpirationThenClientRegistrationResponse() throws Exception {
this.spring.register(ClientSecretExpirationConfiguration.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);
var expectedSecretExpiryDate = Instant.now().plus(Duration.ofHours(24));
var allowedDelta = new TemporalUnitWithinOffset(1, ChronoUnit.MINUTES);
// Returned response contains expiration date
assertThat(clientRegistrationResponse.getClientSecretExpiresAt())
.isNotNull()
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
RegisteredClient registeredClient = this.registeredClientRepository
.findByClientId(clientRegistrationResponse.getClientId());
// Persisted RegisteredClient contains expiration date
assertThat(registeredClient)
.isNotNull();
assertThat(registeredClient.getClientSecretExpiresAt())
.isNotNull()
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
}
private OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {
// ***** (1) Obtain the "initial" access token used for registering the client
@ -643,8 +685,40 @@ public class OidcClientRegistrationTests { @@ -643,8 +685,40 @@ public class OidcClientRegistrationTests {
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class CustomClientMetadataConfiguration extends AuthorizationServerConfiguration {
static class CustomClientMetadataConfiguration extends ClientRegistrationConvertersConfiguration {
private static final List<String> supportedCustomClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
@Override
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
return new CustomRegisteredClientConverter(supportedCustomClientMetadata);
}
@Override
protected Converter<RegisteredClient, OidcClientRegistration> oidcClientRegistrationConverter() {
return new CustomClientRegistrationConverter(supportedCustomClientMetadata);
}
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class ClientSecretExpirationConfiguration extends ClientRegistrationConvertersConfiguration {
@Override
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
return new ClientSecretExpirationRegisteredClientConverter();
}
}
/**
* This test configuration allows to override {@code RegisteredClient} -> {@code OidcClientRegistration} and
* {@code OidcClientRegistration} -> {@code RegisteredClient} converters
*/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class ClientRegistrationConvertersConfiguration extends AuthorizationServerConfiguration {
// @formatter:off
@Bean
@Override
@ -674,15 +748,27 @@ public class OidcClientRegistrationTests { @@ -674,15 +748,27 @@ public class OidcClientRegistrationTests {
// @formatter:off
return (authenticationProviders) ->
authenticationProviders.forEach((authenticationProvider) -> {
List<String> supportedCustomClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {
provider.setRegisteredClientConverter(new CustomRegisteredClientConverter(supportedCustomClientMetadata));
provider.setClientRegistrationConverter(new CustomClientRegistrationConverter(supportedCustomClientMetadata));
var registeredClientConverter = registeredClientConverter();
if (registeredClientConverter != null) {
provider.setRegisteredClientConverter(registeredClientConverter);
}
var oidcClientRegistrationConverter = oidcClientRegistrationConverter();
if (oidcClientRegistrationConverter != null) {
provider.setClientRegistrationConverter(oidcClientRegistrationConverter);
}
}
});
// @formatter:on
}
protected Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter() {
return null;
}
protected Converter<RegisteredClient, OidcClientRegistration> oidcClientRegistrationConverter() {
return null;
}
}
@EnableWebSecurity
@ -814,4 +900,26 @@ public class OidcClientRegistrationTests { @@ -814,4 +900,26 @@ public class OidcClientRegistrationTests {
}
/**
* This customization adds client secret expiration time by setting {@code RegisteredClient.clientSecretExpiresAt}
* during {@code OidcClientRegistration} -> {@code RegisteredClient} conversion
*/
private static final class ClientSecretExpirationRegisteredClientConverter
implements Converter<OidcClientRegistration, RegisteredClient> {
private static final OidcClientRegistrationRegisteredClientConverter delegate =
new OidcClientRegistrationRegisteredClientConverter();
@Override
public RegisteredClient convert(OidcClientRegistration clientRegistration) {
RegisteredClient registeredClient = delegate.convert(clientRegistration);
var registeredClientBuilder = RegisteredClient.from(registeredClient);
var clientSecretExpiresAt = Instant.now().plus(Duration.ofHours(24));
registeredClientBuilder.clientSecretExpiresAt(clientSecretExpiresAt);
return registeredClientBuilder.build();
}
}
}

Loading…
Cancel
Save