Polish "Add Spring Authorization Server support"

See gh-34003
pull/34729/head
Madhura Bhave 2 years ago
parent 25d77ee70b
commit e6f602cec0

@ -31,7 +31,6 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat; import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings; import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.util.CollectionUtils;
/** /**
* Adapter class to convert {@link Client} to a {@link RegisteredClient}. * Adapter class to convert {@link Client} to a {@link RegisteredClient}.
@ -58,66 +57,23 @@ public final class OAuth2AuthorizationServerPropertiesRegistrationAdapter {
map.from(registration::getClientId).to(builder::clientId); map.from(registration::getClientId).to(builder::clientId);
map.from(registration::getClientSecret).to(builder::clientSecret); map.from(registration::getClientSecret).to(builder::clientSecret);
map.from(registration::getClientName).to(builder::clientName); map.from(registration::getClientName).to(builder::clientName);
if (!CollectionUtils.isEmpty(registration.getClientAuthenticationMethods())) { registration.getClientAuthenticationMethods()
registration.getClientAuthenticationMethods() .forEach((clientAuthenticationMethod) -> map.from(clientAuthenticationMethod)
.forEach((clientAuthenticationMethod) -> map.from(clientAuthenticationMethod) .as(ClientAuthenticationMethod::new)
.as(OAuth2AuthorizationServerPropertiesRegistrationAdapter::clientAuthenticationMethod) .to(builder::clientAuthenticationMethod));
.to(builder::clientAuthenticationMethod)); registration.getAuthorizationGrantTypes()
} .forEach((authorizationGrantType) -> map.from(authorizationGrantType)
if (!CollectionUtils.isEmpty(registration.getAuthorizationGrantTypes())) { .as(AuthorizationGrantType::new)
registration.getAuthorizationGrantTypes() .to(builder::authorizationGrantType));
.forEach((authorizationGrantType) -> map.from(authorizationGrantType) registration.getRedirectUris().forEach((redirectUri) -> map.from(redirectUri).to(builder::redirectUri));
.as(OAuth2AuthorizationServerPropertiesRegistrationAdapter::authorizationGrantType) registration.getPostLogoutRedirectUris()
.to(builder::authorizationGrantType)); .forEach((redirectUri) -> map.from(redirectUri).to(builder::postLogoutRedirectUri));
} registration.getScopes().forEach((scope) -> map.from(scope).to(builder::scope));
if (!CollectionUtils.isEmpty(registration.getRedirectUris())) {
registration.getRedirectUris().forEach((redirectUri) -> map.from(redirectUri).to(builder::redirectUri));
}
if (!CollectionUtils.isEmpty(registration.getPostLogoutRedirectUris())) {
registration.getPostLogoutRedirectUris()
.forEach((redirectUri) -> map.from(redirectUri).to(builder::postLogoutRedirectUri));
}
if (!CollectionUtils.isEmpty(registration.getScopes())) {
registration.getScopes().forEach((scope) -> map.from(scope).to(builder::scope));
}
builder.clientSettings(getClientSettings(client, map)); builder.clientSettings(getClientSettings(client, map));
builder.tokenSettings(getTokenSettings(client, map)); builder.tokenSettings(getTokenSettings(client, map));
return builder.build(); return builder.build();
} }
private static ClientAuthenticationMethod clientAuthenticationMethod(String clientAuthenticationMethod) {
if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) {
return ClientAuthenticationMethod.CLIENT_SECRET_BASIC;
}
else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {
return ClientAuthenticationMethod.CLIENT_SECRET_POST;
}
else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientAuthenticationMethod)) {
return ClientAuthenticationMethod.CLIENT_SECRET_JWT;
}
else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientAuthenticationMethod)) {
return ClientAuthenticationMethod.PRIVATE_KEY_JWT;
}
else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {
return ClientAuthenticationMethod.NONE;
}
else {
return new ClientAuthenticationMethod(clientAuthenticationMethod);
}
}
private static AuthorizationGrantType authorizationGrantType(String authorizationGrantType) {
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {
return AuthorizationGrantType.AUTHORIZATION_CODE;
}
else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {
return AuthorizationGrantType.CLIENT_CREDENTIALS;
}
else {
return new AuthorizationGrantType(authorizationGrantType);
}
}
private static ClientSettings getClientSettings(Client client, PropertyMapper map) { private static ClientSettings getClientSettings(Client client, PropertyMapper map) {
ClientSettings.Builder builder = ClientSettings.builder(); ClientSettings.Builder builder = ClientSettings.builder();
map.from(client::isRequireProofKey).to(builder::requireProofKey); map.from(client::isRequireProofKey).to(builder::requireProofKey);

@ -21,10 +21,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
/** /**
@ -43,11 +42,11 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
* @since 3.1.0 * @since 3.1.0
* @see OAuth2AuthorizationServerJwtAutoConfiguration * @see OAuth2AuthorizationServerJwtAutoConfiguration
*/ */
@AutoConfiguration(before = { OAuth2ResourceServerAutoConfiguration.class, SecurityFilterAutoConfiguration.class }) @AutoConfiguration(before = { OAuth2ResourceServerAutoConfiguration.class, SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class })
@ConditionalOnClass(OAuth2Authorization.class) @ConditionalOnClass(OAuth2Authorization.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Import({ OAuth2AuthorizationServerPropertiesConfiguration.class, @Import({ OAuth2AuthorizationServerConfiguration.class, OAuth2AuthorizationServerWebSecurityConfiguration.class })
OAuth2AuthorizationServerWebSecurityConfiguration.class, ObjectPostProcessorConfiguration.class })
public class OAuth2AuthorizationServerAutoConfiguration { public class OAuth2AuthorizationServerAutoConfiguration {
} }

@ -40,7 +40,7 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(OAuth2AuthorizationServerProperties.class) @EnableConfigurationProperties(OAuth2AuthorizationServerProperties.class)
class OAuth2AuthorizationServerPropertiesConfiguration { class OAuth2AuthorizationServerConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean

@ -16,13 +16,30 @@
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet; package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for JWT support for endpoints of the * {@link EnableAutoConfiguration Auto-configuration} for JWT support for endpoints of the
@ -30,12 +47,52 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
* *
* @author Steve Riesenberg * @author Steve Riesenberg
* @since 3.1.0 * @since 3.1.0
* @see OAuth2AuthorizationServerAutoConfiguration
*/ */
@AutoConfiguration(after = { UserDetailsServiceAutoConfiguration.class }) @AutoConfiguration(after = UserDetailsServiceAutoConfiguration.class)
@ConditionalOnClass(OAuth2Authorization.class) @ConditionalOnClass(OAuth2Authorization.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Import(OAuth2AuthorizationServerJwtConfiguration.class)
public class OAuth2AuthorizationServerJwtAutoConfiguration { public class OAuth2AuthorizationServerJwtAutoConfiguration {
@Bean
@ConditionalOnClass(JwtDecoder.class)
@ConditionalOnMissingBean
JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean
JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = getRsaKey();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static RSAKey getRsaKey() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// @formatter:off
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
return rsaKey;
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
} }

@ -1,87 +0,0 @@
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
/**
* {@link Configuration @Configuration} for JWT support for endpoints of the OAuth2
* authorization server that require it (e.g. User Info, Client Registration).
*
* @author Steve Riesenberg
*/
@Configuration(proxyBeanMethods = false)
class OAuth2AuthorizationServerJwtConfiguration {
@Bean
@ConditionalOnClass(JwtDecoder.class)
@ConditionalOnMissingBean
JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnClass(JWKSource.class)
@ConditionalOnMissingBean
JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// @formatter:off
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet; package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.security.ConditionalOnDefaultWebSecurity; import org.springframework.boot.autoconfigure.security.ConditionalOnDefaultWebSecurity;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -25,8 +26,10 @@ import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@ -37,6 +40,7 @@ import org.springframework.security.web.authentication.LoginUrlAuthenticationEnt
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity @ConditionalOnDefaultWebSecurity
@ConditionalOnBean({ RegisteredClientRepository.class, AuthorizationServerSettings.class })
class OAuth2AuthorizationServerWebSecurityConfiguration { class OAuth2AuthorizationServerWebSecurityConfiguration {
@Bean @Bean

@ -37,7 +37,7 @@ import org.springframework.security.authentication.DefaultAuthenticationEventPub
* @author Madhura Bhave * @author Madhura Bhave
* @since 1.0.0 * @since 1.0.0
*/ */
@AutoConfiguration @AutoConfiguration(before = UserDetailsServiceAutoConfiguration.class)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class) @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class }) @Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })

@ -34,18 +34,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Steve Riesenberg * @author Steve Riesenberg
*/ */
public class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests { class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
@Test @Test
void getRegisteredClientsWhenValidParametersShouldAdapt() { void getRegisteredClientsWhenValidParametersShouldAdapt() {
OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties(); OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();
OAuth2AuthorizationServerProperties.Client client = createClient(); OAuth2AuthorizationServerProperties.Client client = createClient();
properties.getClient().put("foo", client); properties.getClient().put("foo", client);
List<RegisteredClient> registeredClients = OAuth2AuthorizationServerPropertiesRegistrationAdapter List<RegisteredClient> registeredClients = OAuth2AuthorizationServerPropertiesRegistrationAdapter
.getRegisteredClients(properties); .getRegisteredClients(properties);
assertThat(registeredClients).hasSize(1); assertThat(registeredClients).hasSize(1);
RegisteredClient registeredClient = registeredClients.get(0); RegisteredClient registeredClient = registeredClients.get(0);
assertThat(registeredClient.getClientId()).isEqualTo("foo"); assertThat(registeredClient.getClientId()).isEqualTo("foo");
assertThat(registeredClient.getClientSecret()).isEqualTo("secret"); assertThat(registeredClient.getClientSecret()).isEqualTo("secret");
@ -56,13 +54,11 @@ public class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
assertThat(registeredClient.getRedirectUris()).containsExactly("https://example.com/redirect"); assertThat(registeredClient.getRedirectUris()).containsExactly("https://example.com/redirect");
assertThat(registeredClient.getPostLogoutRedirectUris()).containsExactly("https://example.com/logout"); assertThat(registeredClient.getPostLogoutRedirectUris()).containsExactly("https://example.com/logout");
assertThat(registeredClient.getScopes()).containsExactly("user.read"); assertThat(registeredClient.getScopes()).containsExactly("user.read");
assertThat(registeredClient.getClientSettings().isRequireProofKey()).isTrue(); assertThat(registeredClient.getClientSettings().isRequireProofKey()).isTrue();
assertThat(registeredClient.getClientSettings().isRequireAuthorizationConsent()).isTrue(); assertThat(registeredClient.getClientSettings().isRequireAuthorizationConsent()).isTrue();
assertThat(registeredClient.getClientSettings().getJwkSetUrl()).isEqualTo("https://example.com/jwks"); assertThat(registeredClient.getClientSettings().getJwkSetUrl()).isEqualTo("https://example.com/jwks");
assertThat(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm()) assertThat(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm())
.isEqualTo(SignatureAlgorithm.RS256); .isEqualTo(SignatureAlgorithm.RS256);
assertThat(registeredClient.getTokenSettings().getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.REFERENCE); assertThat(registeredClient.getTokenSettings().getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.REFERENCE);
assertThat(registeredClient.getTokenSettings().getAccessTokenTimeToLive()).isEqualTo(Duration.ofSeconds(300)); assertThat(registeredClient.getTokenSettings().getAccessTokenTimeToLive()).isEqualTo(Duration.ofSeconds(300));
assertThat(registeredClient.getTokenSettings().getRefreshTokenTimeToLive()).isEqualTo(Duration.ofHours(24)); assertThat(registeredClient.getTokenSettings().getRefreshTokenTimeToLive()).isEqualTo(Duration.ofHours(24));
@ -77,7 +73,6 @@ public class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
client.setRequireAuthorizationConsent(true); client.setRequireAuthorizationConsent(true);
client.setJwkSetUri("https://example.com/jwks"); client.setJwkSetUri("https://example.com/jwks");
client.setTokenEndpointAuthenticationSigningAlgorithm("rs256"); client.setTokenEndpointAuthenticationSigningAlgorithm("rs256");
OAuth2AuthorizationServerProperties.Registration registration = client.getRegistration(); OAuth2AuthorizationServerProperties.Registration registration = client.getRegistration();
registration.setClientId("foo"); registration.setClientId("foo");
registration.setClientSecret("secret"); registration.setClientSecret("secret");
@ -86,14 +81,12 @@ public class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
registration.getRedirectUris().add("https://example.com/redirect"); registration.getRedirectUris().add("https://example.com/redirect");
registration.getPostLogoutRedirectUris().add("https://example.com/logout"); registration.getPostLogoutRedirectUris().add("https://example.com/logout");
registration.getScopes().add("user.read"); registration.getScopes().add("user.read");
OAuth2AuthorizationServerProperties.Token token = client.getToken(); OAuth2AuthorizationServerProperties.Token token = client.getToken();
token.setAccessTokenFormat("reference"); token.setAccessTokenFormat("reference");
token.setAccessTokenTimeToLive(Duration.ofSeconds(300)); token.setAccessTokenTimeToLive(Duration.ofSeconds(300));
token.setRefreshTokenTimeToLive(Duration.ofHours(24)); token.setRefreshTokenTimeToLive(Duration.ofHours(24));
token.setReuseRefreshTokens(true); token.setReuseRefreshTokens(true);
token.setIdTokenSignatureAlgorithm("rs512"); token.setIdTokenSignatureAlgorithm("rs512");
return client; return client;
} }

@ -27,12 +27,11 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Steve Riesenberg * @author Steve Riesenberg
*/ */
public class OAuth2AuthorizationServerPropertiesSettingsAdapterTests { class OAuth2AuthorizationServerPropertiesSettingsAdapterTests {
@Test @Test
void getAuthorizationServerSettingsWhenValidParametersShouldAdapt() { void getAuthorizationServerSettingsWhenValidParametersShouldAdapt() {
OAuth2AuthorizationServerProperties properties = createAuthorizationServerProperties(); OAuth2AuthorizationServerProperties properties = createAuthorizationServerProperties();
AuthorizationServerSettings settings = OAuth2AuthorizationServerPropertiesSettingsAdapter AuthorizationServerSettings settings = OAuth2AuthorizationServerPropertiesSettingsAdapter
.getAuthorizationServerSettings(properties); .getAuthorizationServerSettings(properties);
assertThat(settings.getIssuer()).isEqualTo("https://example.com"); assertThat(settings.getIssuer()).isEqualTo("https://example.com");
@ -49,14 +48,12 @@ public class OAuth2AuthorizationServerPropertiesSettingsAdapterTests {
private OAuth2AuthorizationServerProperties createAuthorizationServerProperties() { private OAuth2AuthorizationServerProperties createAuthorizationServerProperties() {
OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties(); OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();
properties.setIssuer("https://example.com"); properties.setIssuer("https://example.com");
OAuth2AuthorizationServerProperties.Endpoint endpoints = properties.getEndpoint(); OAuth2AuthorizationServerProperties.Endpoint endpoints = properties.getEndpoint();
endpoints.setAuthorizationUri("/authorize"); endpoints.setAuthorizationUri("/authorize");
endpoints.setTokenUri("/token"); endpoints.setTokenUri("/token");
endpoints.setJwkSetUri("/jwks"); endpoints.setJwkSetUri("/jwks");
endpoints.setTokenRevocationUri("/revoke"); endpoints.setTokenRevocationUri("/revoke");
endpoints.setTokenIntrospectionUri("/introspect"); endpoints.setTokenIntrospectionUri("/introspect");
OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoints.getOidc(); OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoints.getOidc();
oidc.setLogoutUri("/logout"); oidc.setLogoutUri("/logout");
oidc.setClientRegistrationUri("/register"); oidc.setClientRegistrationUri("/register");

@ -25,7 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* *
* @author Steve Riesenberg * @author Steve Riesenberg
*/ */
public class OAuth2AuthorizationServerPropertiesTests { class OAuth2AuthorizationServerPropertiesTests {
private final OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties(); private final OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();

@ -0,0 +1,184 @@
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link OAuth2AuthorizationServerAutoConfiguration}.
*
* @author Steve Riesenberg
* @author Madhura Bhave
*/
class OAuth2AuthorizationServerAutoConfigurationTests {
private static final String PROPERTIES_PREFIX = "spring.security.oauth2.authorizationserver";
private static final String CLIENT_PREFIX = PROPERTIES_PREFIX + ".client";
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(OAuth2AuthorizationServerAutoConfiguration.class,
OAuth2AuthorizationServerJwtAutoConfiguration.class, SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class));
@Test
void autoConfigurationConditionalOnClassOauth2Authorization() {
this.contextRunner.withClassLoader(new FilteredClassLoader(OAuth2Authorization.class))
.run((context) -> assertThat(context).doesNotHaveBean(OAuth2AuthorizationServerAutoConfiguration.class));
}
@Test
void autoConfigurationDoesNotCauseUserDetailsServiceToBackOff() {
this.contextRunner.run((context) -> assertThat(context).hasBean("inMemoryUserDetailsManager"));
}
@Test
void registeredClientRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(RegisteredClientRepository.class));
}
@Test
void registeredClientRepositoryBeanShouldBeCreatedWhenPropertiesPresent() {
this.contextRunner
.withPropertyValues(CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.scopes=test")
.run((context) -> {
RegisteredClientRepository registeredClientRepository = context
.getBean(RegisteredClientRepository.class);
RegisteredClient registeredClient = registeredClientRepository.findById("foo");
assertThat(registeredClient).isNotNull();
assertThat(registeredClient.getClientId()).isEqualTo("abcd");
assertThat(registeredClient.getClientSecret()).isEqualTo("secret");
assertThat(registeredClient.getClientAuthenticationMethods())
.containsOnly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
assertThat(registeredClient.getAuthorizationGrantTypes())
.containsOnly(AuthorizationGrantType.CLIENT_CREDENTIALS);
assertThat(registeredClient.getScopes()).containsOnly("test");
});
}
@Test
void registeredClientRepositoryBacksOffWhenRegisteredClientRepositoryBeanPresent() {
this.contextRunner.withUserConfiguration(TestRegisteredClientRepositoryConfiguration.class)
.withPropertyValues(CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.scope=test")
.run((context) -> {
RegisteredClientRepository registeredClientRepository = context
.getBean(RegisteredClientRepository.class);
RegisteredClient registeredClient = registeredClientRepository.findById("test");
assertThat(registeredClient).isNotNull();
assertThat(registeredClient.getClientId()).isEqualTo("abcd");
assertThat(registeredClient.getClientSecret()).isEqualTo("secret");
assertThat(registeredClient.getClientAuthenticationMethods())
.containsOnly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
assertThat(registeredClient.getAuthorizationGrantTypes())
.containsOnly(AuthorizationGrantType.CLIENT_CREDENTIALS);
assertThat(registeredClient.getScopes()).containsOnly("test");
});
}
@Test
void authorizationServerSettingsBeanShouldBeCreatedWhenPropertiesAbsent() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(AuthorizationServerSettings.class));
}
@Test
void authorizationServerSettingsBeanShouldBeCreatedWhenPropertiesPresent() {
this.contextRunner
.withPropertyValues(PROPERTIES_PREFIX + ".issuer=https://example.com",
PROPERTIES_PREFIX + ".endpoint.authorization-uri=/authorize",
PROPERTIES_PREFIX + ".endpoint.token-uri=/token", PROPERTIES_PREFIX + ".endpoint.jwk-set-uri=/jwks",
PROPERTIES_PREFIX + ".endpoint.token-revocation-uri=/revoke",
PROPERTIES_PREFIX + ".endpoint.token-introspection-uri=/introspect",
PROPERTIES_PREFIX + ".endpoint.oidc.logout-uri=/logout",
PROPERTIES_PREFIX + ".endpoint.oidc.client-registration-uri=/register",
PROPERTIES_PREFIX + ".endpoint.oidc.user-info-uri=/user")
.run((context) -> {
AuthorizationServerSettings settings = context.getBean(AuthorizationServerSettings.class);
assertThat(settings.getIssuer()).isEqualTo("https://example.com");
assertThat(settings.getAuthorizationEndpoint()).isEqualTo("/authorize");
assertThat(settings.getTokenEndpoint()).isEqualTo("/token");
assertThat(settings.getJwkSetEndpoint()).isEqualTo("/jwks");
assertThat(settings.getTokenRevocationEndpoint()).isEqualTo("/revoke");
assertThat(settings.getTokenIntrospectionEndpoint()).isEqualTo("/introspect");
assertThat(settings.getOidcLogoutEndpoint()).isEqualTo("/logout");
assertThat(settings.getOidcClientRegistrationEndpoint()).isEqualTo("/register");
assertThat(settings.getOidcUserInfoEndpoint()).isEqualTo("/user");
});
}
@Test
void authorizationServerSettingsBacksOffWhenAuthorizationServerSettingsBeanPresent() {
this.contextRunner.withUserConfiguration(TestAuthorizationServerSettingsConfiguration.class)
.withPropertyValues(PROPERTIES_PREFIX + ".issuer=https://test.com")
.run((context) -> {
AuthorizationServerSettings settings = context.getBean(AuthorizationServerSettings.class);
assertThat(settings.getIssuer()).isEqualTo("https://example.com");
});
}
@Configuration
static class TestRegisteredClientRepositoryConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId("test")
.clientId("abcd")
.clientSecret("secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope("test")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
}
@Configuration
static class TestAuthorizationServerSettingsConfiguration {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer("https://example.com").build();
}
}
}

@ -0,0 +1,107 @@
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2AuthorizationServerJwtAutoConfiguration}.
*
* @author Steve Riesenberg
*/
class OAuth2AuthorizationServerJwtAutoConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(OAuth2AuthorizationServerJwtAutoConfiguration.class));
@Test
void autoConfigurationConditionalOnClassOauth2Authorization() {
this.contextRunner.withClassLoader(new FilteredClassLoader(OAuth2Authorization.class))
.run((context) -> assertThat(context).doesNotHaveBean(OAuth2AuthorizationServerJwtAutoConfiguration.class));
}
@Test
void jwtDecoderConditionalOnClassJwtDecoder() {
this.contextRunner.withClassLoader(new FilteredClassLoader(JwtDecoder.class))
.run((context) -> assertThat(context).doesNotHaveBean("jwtDecoder"));
}
@Test
void jwtConfigurationConfiguresJwtDecoderWithGeneratedKey() {
this.contextRunner.run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context.getBean("jwtDecoder")).isInstanceOf(NimbusJwtDecoder.class);
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwkSource")).isInstanceOf(ImmutableJWKSet.class);
});
}
@Test
void jwtDecoderBacksOffWhenBeanPresent() {
this.contextRunner.withUserConfiguration(TestJwtDecoderConfiguration.class).run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context.getBean("jwtDecoder")).isNotInstanceOf(NimbusJwtDecoder.class);
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwkSource")).isInstanceOf(ImmutableJWKSet.class);
});
}
@Test
void jwkSourceBacksOffWhenBeanPresent() {
this.contextRunner.withUserConfiguration(TestJwkSourceConfiguration.class).run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context.getBean("jwtDecoder")).isInstanceOf(NimbusJwtDecoder.class);
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwkSource")).isNotInstanceOf(ImmutableJWKSet.class);
});
}
@Configuration
static class TestJwtDecoderConfiguration {
@Bean
JwtDecoder jwtDecoder() {
return (token) -> null;
}
}
@Configuration
static class TestJwkSourceConfiguration {
@Bean
JWKSource<SecurityContext> jwkSource() {
return (jwkSelector, context) -> null;
}
}
}

@ -1,110 +0,0 @@
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2AuthorizationServerJwtConfiguration}.
*
* @author Steve Riesenberg
*/
public class OAuth2AuthorizationServerJwtConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
void jwtConfigurationConfiguresJwtDecoderWithGeneratedKey() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestJwtConfiguration.class)
.run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwtDecoder")).isInstanceOf(NimbusJwtDecoder.class);
assertThat(context.getBean("jwkSource")).isInstanceOf(ImmutableJWKSet.class);
});
// @formatter:on
}
@Test
void jwtDecoderBacksOffWhenBeanPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestJwtDecoderConfiguration.class, TestJwtConfiguration.class)
.run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwtDecoder")).isNotInstanceOf(NimbusJwtDecoder.class);
assertThat(context.getBean("jwkSource")).isInstanceOf(ImmutableJWKSet.class);
});
// @formatter:on
}
@Test
void jwkSourceBacksOffWhenBeanPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestJwkSourceConfiguration.class, TestJwtConfiguration.class)
.run((context) -> {
assertThat(context).hasBean("jwtDecoder");
assertThat(context).hasBean("jwkSource");
assertThat(context.getBean("jwtDecoder")).isInstanceOf(NimbusJwtDecoder.class);
assertThat(context.getBean("jwkSource")).isNotInstanceOf(ImmutableJWKSet.class);
});
// @formatter:on
}
@Configuration
@Import(OAuth2AuthorizationServerJwtConfiguration.class)
static class TestJwtConfiguration {
}
@Configuration
static class TestJwtDecoderConfiguration {
@Bean
JwtDecoder jwtDecoder() {
return (token) -> null;
}
}
@Configuration
static class TestJwkSourceConfiguration {
@Bean
JWKSource<SecurityContext> jwkSource() {
return (jwkSelector, context) -> null;
}
}
}

@ -1,189 +0,0 @@
/*
* Copyright 2012-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Riesenberg
*/
public class OAuth2AuthorizationServerPropertiesConfigurationTests {
private static final String PROPERTIES_PREFIX = "spring.security.oauth2.authorizationserver";
private static final String CLIENT_PREFIX = PROPERTIES_PREFIX + ".client";
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
void registeredClientRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(RegisteredClientRepository.class));
// @formatter:on
}
@Test
void registeredClientRepositoryBeanShouldBeCreatedWhenPropertiesPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues(
CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.scopes=test")
.run((context) -> {
RegisteredClientRepository registeredClientRepository = context.getBean(RegisteredClientRepository.class);
RegisteredClient registeredClient = registeredClientRepository.findById("foo");
assertThat(registeredClient).isNotNull();
assertThat(registeredClient.getClientId()).isEqualTo("abcd");
assertThat(registeredClient.getClientSecret()).isEqualTo("secret");
assertThat(registeredClient.getClientAuthenticationMethods())
.containsOnly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
assertThat(registeredClient.getAuthorizationGrantTypes())
.containsOnly(AuthorizationGrantType.CLIENT_CREDENTIALS);
assertThat(registeredClient.getScopes()).containsOnly("test");
});
// @formatter:on
}
@Test
void registeredClientRepositoryBacksOffWhenRegisteredClientRepositoryBeanPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestRegisteredClientRepositoryConfiguration.class,
TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues(
CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.scope=test")
.run((context) -> {
RegisteredClientRepository registeredClientRepository = context.getBean(RegisteredClientRepository.class);
RegisteredClient registeredClient = registeredClientRepository.findById("test");
assertThat(registeredClient).isNotNull();
assertThat(registeredClient.getClientId()).isEqualTo("abcd");
assertThat(registeredClient.getClientSecret()).isEqualTo("secret");
assertThat(registeredClient.getClientAuthenticationMethods())
.containsOnly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
assertThat(registeredClient.getAuthorizationGrantTypes())
.containsOnly(AuthorizationGrantType.CLIENT_CREDENTIALS);
assertThat(registeredClient.getScopes()).containsOnly("test");
});
// @formatter:on
}
@Test
void authorizationServerSettingsBeanShouldBeCreatedWhenPropertiesAbsent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(AuthorizationServerSettings.class));
// @formatter:on
}
@Test
void authorizationServerSettingsBeanShouldBeCreatedWhenPropertiesPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues(
PROPERTIES_PREFIX + ".issuer=https://example.com",
PROPERTIES_PREFIX + ".endpoint.authorization-uri=/authorize",
PROPERTIES_PREFIX + ".endpoint.token-uri=/token",
PROPERTIES_PREFIX + ".endpoint.jwk-set-uri=/jwks",
PROPERTIES_PREFIX + ".endpoint.token-revocation-uri=/revoke",
PROPERTIES_PREFIX + ".endpoint.token-introspection-uri=/introspect",
PROPERTIES_PREFIX + ".endpoint.oidc.logout-uri=/logout",
PROPERTIES_PREFIX + ".endpoint.oidc.client-registration-uri=/register",
PROPERTIES_PREFIX + ".endpoint.oidc.user-info-uri=/user")
.run((context) -> {
AuthorizationServerSettings settings = context.getBean(AuthorizationServerSettings.class);
assertThat(settings.getIssuer()).isEqualTo("https://example.com");
assertThat(settings.getAuthorizationEndpoint()).isEqualTo("/authorize");
assertThat(settings.getTokenEndpoint()).isEqualTo("/token");
assertThat(settings.getJwkSetEndpoint()).isEqualTo("/jwks");
assertThat(settings.getTokenRevocationEndpoint()).isEqualTo("/revoke");
assertThat(settings.getTokenIntrospectionEndpoint()).isEqualTo("/introspect");
assertThat(settings.getOidcLogoutEndpoint()).isEqualTo("/logout");
assertThat(settings.getOidcClientRegistrationEndpoint()).isEqualTo("/register");
assertThat(settings.getOidcUserInfoEndpoint()).isEqualTo("/user");
});
// @formatter:on
}
@Test
void authorizationServerSettingsBacksOffWhenAuthorizationServerSettingsBeanPresent() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestAuthorizationServerSettingsConfiguration.class,
TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues(PROPERTIES_PREFIX + ".issuer=https://test.com")
.run((context) -> {
AuthorizationServerSettings settings = context.getBean(AuthorizationServerSettings.class);
assertThat(settings.getIssuer()).isEqualTo("https://example.com");
});
// @formatter:on
}
@Configuration
@EnableWebSecurity
@Import({ OAuth2AuthorizationServerPropertiesConfiguration.class })
static class TestOAuth2AuthorizationServerConfiguration {
}
@Configuration
static class TestRegisteredClientRepositoryConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId("test")
.clientId("abcd")
.clientSecret("secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope("test")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
}
@Configuration
static class TestAuthorizationServerSettingsConfiguration {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer("https://example.com").build();
}
}
}

@ -21,8 +21,8 @@ import java.util.List;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -31,10 +31,16 @@ import org.springframework.security.config.BeanIds;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter; import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter; import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter; import org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
@ -49,74 +55,69 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link OAuth2AuthorizationServerWebSecurityConfiguration}.
*
* @author Steve Riesenberg * @author Steve Riesenberg
*/ */
public class OAuth2AuthorizationServerWebSecurityConfigurationTests { class OAuth2AuthorizationServerWebSecurityConfigurationTests {
private static final String PROPERTIES_PREFIX = "spring.security.oauth2.authorizationserver"; private static final String PROPERTIES_PREFIX = "spring.security.oauth2.authorizationserver";
private static final String CLIENT_PREFIX = PROPERTIES_PREFIX + ".client"; private static final String CLIENT_PREFIX = PROPERTIES_PREFIX + ".client";
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner();
@Test @Test
void webSecurityConfigurationConfiguresAuthorizationServerWithFormLogin() { void webSecurityConfigurationConfiguresAuthorizationServerWithFormLogin() {
// @formatter:off
this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class) this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues( .withPropertyValues(CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-id=abcd", CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-secret=secret", CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic", CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials", CLIENT_PREFIX + ".foo.registration.scopes=test")
CLIENT_PREFIX + ".foo.registration.scopes=test") .run((context) -> {
.run((context) -> { assertThat(context).hasBean("authorizationServerSecurityFilterChain");
assertThat(context).hasBean("authorizationServerSecurityFilterChain"); assertThat(context).hasBean("defaultSecurityFilterChain");
assertThat(context).hasBean("defaultSecurityFilterChain"); assertThat(context).hasBean("registeredClientRepository");
assertThat(context).hasBean("registeredClientRepository"); assertThat(context).hasBean("authorizationServerSettings");
assertThat(context).hasBean("authorizationServerSettings"); assertThat(findFilter(context, OAuth2AuthorizationEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2TokenEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2AuthorizationEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OAuth2TokenIntrospectionEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2TokenEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OAuth2TokenRevocationEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2TokenIntrospectionEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OAuth2AuthorizationServerMetadataEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2TokenRevocationEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OidcProviderConfigurationEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OAuth2AuthorizationServerMetadataEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OidcUserInfoEndpointFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OidcProviderConfigurationEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, BearerTokenAuthenticationFilter.class, 0)).isNotNull();
assertThat(findFilter(context, OidcUserInfoEndpointFilter.class, 0)).isNotNull(); assertThat(findFilter(context, OidcClientRegistrationEndpointFilter.class, 0)).isNull();
assertThat(findFilter(context, BearerTokenAuthenticationFilter.class, 0)).isNotNull(); assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 0)).isNull();
assertThat(findFilter(context, OidcClientRegistrationEndpointFilter.class, 0)).isNull(); assertThat(findFilter(context, DefaultLoginPageGeneratingFilter.class, 1)).isNotNull();
assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 0)).isNull(); assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 1)).isNotNull();
assertThat(findFilter(context, DefaultLoginPageGeneratingFilter.class, 1)).isNotNull(); });
assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 1)).isNotNull();
});
// @formatter:on
} }
@Test @Test
void securityFilterChainsBackOffWhenSecurityFilterChainBeanPresent() { void securityFilterChainsBackOffWhenSecurityFilterChainBeanPresent() {
// @formatter:off this.contextRunner
this.contextRunner.withUserConfiguration(TestSecurityFilterChainConfiguration.class, .withUserConfiguration(TestSecurityFilterChainConfiguration.class,
TestOAuth2AuthorizationServerConfiguration.class) TestOAuth2AuthorizationServerConfiguration.class)
.withPropertyValues( .withPropertyValues(CLIENT_PREFIX + ".foo.registration.client-id=abcd",
CLIENT_PREFIX + ".foo.registration.client-id=abcd", CLIENT_PREFIX + ".foo.registration.client-secret=secret",
CLIENT_PREFIX + ".foo.registration.client-secret=secret", CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic",
CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic", CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials",
CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials", CLIENT_PREFIX + ".foo.registration.scopes=test")
CLIENT_PREFIX + ".foo.registration.scopes=test") .run((context) -> {
.run((context) -> { assertThat(context).hasBean("authServerSecurityFilterChain");
assertThat(context).hasBean("authServerSecurityFilterChain"); assertThat(context).doesNotHaveBean("authorizationServerSecurityFilterChain");
assertThat(context).doesNotHaveBean("authorizationServerSecurityFilterChain"); assertThat(context).hasBean("securityFilterChain");
assertThat(context).hasBean("securityFilterChain"); assertThat(context).doesNotHaveBean("defaultSecurityFilterChain");
assertThat(context).doesNotHaveBean("defaultSecurityFilterChain"); assertThat(context).hasBean("registeredClientRepository");
assertThat(context).hasBean("registeredClientRepository"); assertThat(context).hasBean("authorizationServerSettings");
assertThat(context).hasBean("authorizationServerSettings"); assertThat(findFilter(context, BearerTokenAuthenticationFilter.class, 0)).isNull();
assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 1)).isNull();
assertThat(findFilter(context, BearerTokenAuthenticationFilter.class, 0)).isNull(); });
assertThat(findFilter(context, UsernamePasswordAuthenticationFilter.class, 1)).isNull();
});
// @formatter:on
} }
private Filter findFilter(AssertableApplicationContext context, Class<? extends Filter> filter, private Filter findFilter(AssertableWebApplicationContext context, Class<? extends Filter> filter,
int filterChainIndex) { int filterChainIndex) {
FilterChainProxy filterChain = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); FilterChainProxy filterChain = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
List<SecurityFilterChain> filterChains = filterChain.getFilterChains(); List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
@ -126,12 +127,35 @@ public class OAuth2AuthorizationServerWebSecurityConfigurationTests {
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@Import({ OAuth2AuthorizationServerPropertiesConfiguration.class, @Import({ TestRegisteredClientRepositoryConfiguration.class,
OAuth2AuthorizationServerWebSecurityConfiguration.class, OAuth2AuthorizationServerJwtConfiguration.class }) OAuth2AuthorizationServerWebSecurityConfiguration.class,
OAuth2AuthorizationServerJwtAutoConfiguration.class })
static class TestOAuth2AuthorizationServerConfiguration { static class TestOAuth2AuthorizationServerConfiguration {
} }
@Configuration
static class TestRegisteredClientRepositoryConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId("test")
.clientId("abcd")
.clientSecret("secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope("test")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer("https://example.com").build();
}
}
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
static class TestSecurityFilterChainConfiguration { static class TestSecurityFilterChainConfiguration {

@ -1336,7 +1336,7 @@ bom {
] ]
} }
} }
library("Spring Authorization Server", "1.1.0-M1") { library("Spring Authorization Server", "1.1.0-M2") {
group("org.springframework.security") { group("org.springframework.security") {
modules = [ modules = [
"spring-security-oauth2-authorization-server" "spring-security-oauth2-authorization-server"

@ -102,18 +102,14 @@ class SampleOAuth2AuthorizationServerApplicationTests {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("messaging-client", "secret"); headers.setBasicAuth("messaging-client", "secret");
HttpEntity<Object> request = new HttpEntity<>(headers); HttpEntity<Object> request = new HttpEntity<>(headers);
// @formatter:off
String requestUri = UriComponentsBuilder.fromUriString("/token") String requestUri = UriComponentsBuilder.fromUriString("/token")
.queryParam(OAuth2ParameterNames.CLIENT_ID, "messaging-client") .queryParam(OAuth2ParameterNames.CLIENT_ID, "messaging-client")
.queryParam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) .queryParam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.queryParam(OAuth2ParameterNames.SCOPE, "message.read+message.write") .queryParam(OAuth2ParameterNames.SCOPE, "message.read+message.write")
.toUriString(); .toUriString();
// @formatter:on
ResponseEntity<Map<String, Object>> entity = this.restTemplate.exchange(requestUri, HttpMethod.POST, request, ResponseEntity<Map<String, Object>> entity = this.restTemplate.exchange(requestUri, HttpMethod.POST, request,
MAP_TYPE_REFERENCE); MAP_TYPE_REFERENCE);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
Map<String, Object> tokenResponse = Objects.requireNonNull(entity.getBody()); Map<String, Object> tokenResponse = Objects.requireNonNull(entity.getBody());
assertThat(tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN)).isNotNull(); assertThat(tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN)).isNotNull();
assertThat(tokenResponse.get(OAuth2ParameterNames.EXPIRES_IN)).isNotNull(); assertThat(tokenResponse.get(OAuth2ParameterNames.EXPIRES_IN)).isNotNull();

Loading…
Cancel
Save