Polish Authorization Server auto-configuration

pull/34729/head
Andy Wilkinson 2 years ago
parent 94b091d311
commit f06536f642

@ -1,52 +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;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
/**
* Adapter class to convert {@link OAuth2AuthorizationServerProperties.Endpoint} to a
* {@link AuthorizationServerSettings}.
*
* @author Steve Riesenberg
* @since 3.1.0
*/
public final class OAuth2AuthorizationServerPropertiesSettingsAdapter {
private OAuth2AuthorizationServerPropertiesSettingsAdapter() {
}
public static AuthorizationServerSettings getAuthorizationServerSettings(
OAuth2AuthorizationServerProperties properties) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
OAuth2AuthorizationServerProperties.Endpoint endpoint = properties.getEndpoint();
OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoint.getOidc();
AuthorizationServerSettings.Builder builder = AuthorizationServerSettings.builder();
map.from(properties::getIssuer).to(builder::issuer);
map.from(endpoint::getAuthorizationUri).to(builder::authorizationEndpoint);
map.from(endpoint::getTokenUri).to(builder::tokenEndpoint);
map.from(endpoint::getJwkSetUri).to(builder::jwkSetEndpoint);
map.from(endpoint::getTokenRevocationUri).to(builder::tokenRevocationEndpoint);
map.from(endpoint::getTokenIntrospectionUri).to(builder::tokenIntrospectionEndpoint);
map.from(oidc::getLogoutUri).to(builder::oidcLogoutEndpoint);
map.from(oidc::getClientRegistrationUri).to(builder::oidcClientRegistrationEndpoint);
map.from(oidc::getUserInfoUri).to(builder::oidcUserInfoEndpoint);
return builder.build();
}
}

@ -1,20 +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.
*/
/**
* Support for Spring Security's OAuth2 authorization server.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server;

@ -16,19 +16,12 @@
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.oauth2.server.OAuth2AuthorizationServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.server.OAuth2AuthorizationServerPropertiesRegistrationAdapter;
import org.springframework.boot.autoconfigure.security.oauth2.server.OAuth2AuthorizationServerPropertiesSettingsAdapter;
import org.springframework.boot.autoconfigure.security.oauth2.server.RegisteredClientsConfiguredCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
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;
@ -42,19 +35,23 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
@EnableConfigurationProperties(OAuth2AuthorizationServerProperties.class)
class OAuth2AuthorizationServerConfiguration {
private final OAuth2AuthorizationServerPropertiesMapper propertiesMapper;
OAuth2AuthorizationServerConfiguration(OAuth2AuthorizationServerProperties properties) {
this.propertiesMapper = new OAuth2AuthorizationServerPropertiesMapper(properties);
}
@Bean
@ConditionalOnMissingBean
@Conditional(RegisteredClientsConfiguredCondition.class)
RegisteredClientRepository registeredClientRepository(OAuth2AuthorizationServerProperties properties) {
List<RegisteredClient> registeredClients = OAuth2AuthorizationServerPropertiesRegistrationAdapter
.getRegisteredClients(properties);
return new InMemoryRegisteredClientRepository(registeredClients);
RegisteredClientRepository registeredClientRepository() {
return new InMemoryRegisteredClientRepository(this.propertiesMapper.asRegisteredClients());
}
@Bean
@ConditionalOnMissingBean
AuthorizationServerSettings authorizationServerSettings(OAuth2AuthorizationServerProperties properties) {
return OAuth2AuthorizationServerPropertiesSettingsAdapter.getAuthorizationServerSettings(properties);
AuthorizationServerSettings authorizationServerSettings() {
return this.propertiesMapper.asAuthorizationServerSettings();
}
}

@ -73,12 +73,9 @@ public class OAuth2AuthorizationServerJwtAutoConfiguration {
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
RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
return rsaKey;
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server;
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.time.Duration;
import java.util.HashMap;

@ -14,13 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server;
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.autoconfigure.security.oauth2.server.OAuth2AuthorizationServerProperties.Client;
import org.springframework.boot.autoconfigure.security.oauth2.server.OAuth2AuthorizationServerProperties.Registration;
import org.springframework.boot.autoconfigure.security.oauth2.server.servlet.OAuth2AuthorizationServerProperties.Client;
import org.springframework.boot.autoconfigure.security.oauth2.server.servlet.OAuth2AuthorizationServerProperties.Registration;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
@ -28,29 +28,49 @@ import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
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.TokenSettings;
/**
* Adapter class to convert {@link Client} to a {@link RegisteredClient}.
* Maps {@OAuth2AuthorizationServerProperties} to Authorization Server types.
*
* @author Steve Riesenberg
* @since 3.1.0
*/
public final class OAuth2AuthorizationServerPropertiesRegistrationAdapter {
final class OAuth2AuthorizationServerPropertiesMapper {
private OAuth2AuthorizationServerPropertiesRegistrationAdapter() {
private final OAuth2AuthorizationServerProperties properties;
OAuth2AuthorizationServerPropertiesMapper(OAuth2AuthorizationServerProperties properties) {
this.properties = properties;
}
AuthorizationServerSettings asAuthorizationServerSettings() {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
OAuth2AuthorizationServerProperties.Endpoint endpoint = this.properties.getEndpoint();
OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoint.getOidc();
AuthorizationServerSettings.Builder builder = AuthorizationServerSettings.builder();
map.from(this.properties::getIssuer).to(builder::issuer);
map.from(endpoint::getAuthorizationUri).to(builder::authorizationEndpoint);
map.from(endpoint::getTokenUri).to(builder::tokenEndpoint);
map.from(endpoint::getJwkSetUri).to(builder::jwkSetEndpoint);
map.from(endpoint::getTokenRevocationUri).to(builder::tokenRevocationEndpoint);
map.from(endpoint::getTokenIntrospectionUri).to(builder::tokenIntrospectionEndpoint);
map.from(oidc::getLogoutUri).to(builder::oidcLogoutEndpoint);
map.from(oidc::getClientRegistrationUri).to(builder::oidcClientRegistrationEndpoint);
map.from(oidc::getUserInfoUri).to(builder::oidcUserInfoEndpoint);
return builder.build();
}
public static List<RegisteredClient> getRegisteredClients(OAuth2AuthorizationServerProperties properties) {
List<RegisteredClient> asRegisteredClients() {
List<RegisteredClient> registeredClients = new ArrayList<>();
properties.getClient()
this.properties.getClient()
.forEach((registrationId, client) -> registeredClients.add(getRegisteredClient(registrationId, client)));
return registeredClients;
}
private static RegisteredClient getRegisteredClient(String registrationId, Client client) {
private RegisteredClient getRegisteredClient(String registrationId, Client client) {
Registration registration = client.getRegistration();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
RegisteredClient.Builder builder = RegisteredClient.withId(registrationId);
@ -74,18 +94,18 @@ public final class OAuth2AuthorizationServerPropertiesRegistrationAdapter {
return builder.build();
}
private static ClientSettings getClientSettings(Client client, PropertyMapper map) {
private ClientSettings getClientSettings(Client client, PropertyMapper map) {
ClientSettings.Builder builder = ClientSettings.builder();
map.from(client::isRequireProofKey).to(builder::requireProofKey);
map.from(client::isRequireAuthorizationConsent).to(builder::requireAuthorizationConsent);
map.from(client::getJwkSetUri).to(builder::jwkSetUrl);
map.from(client::getTokenEndpointAuthenticationSigningAlgorithm)
.as(OAuth2AuthorizationServerPropertiesRegistrationAdapter::jwsAlgorithm)
.as(this::jwsAlgorithm)
.to(builder::tokenEndpointAuthenticationSigningAlgorithm);
return builder.build();
}
private static TokenSettings getTokenSettings(Client client, PropertyMapper map) {
private TokenSettings getTokenSettings(Client client, PropertyMapper map) {
OAuth2AuthorizationServerProperties.Token token = client.getToken();
TokenSettings.Builder builder = TokenSettings.builder();
map.from(token::getAuthorizationCodeTimeToLive).to(builder::authorizationCodeTimeToLive);
@ -94,12 +114,12 @@ public final class OAuth2AuthorizationServerPropertiesRegistrationAdapter {
map.from(token::isReuseRefreshTokens).to(builder::reuseRefreshTokens);
map.from(token::getRefreshTokenTimeToLive).to(builder::refreshTokenTimeToLive);
map.from(token::getIdTokenSignatureAlgorithm)
.as(OAuth2AuthorizationServerPropertiesRegistrationAdapter::signatureAlgorithm)
.as(this::signatureAlgorithm)
.to(builder::idTokenSignatureAlgorithm);
return builder.build();
}
private static JwsAlgorithm jwsAlgorithm(String signingAlgorithm) {
private JwsAlgorithm jwsAlgorithm(String signingAlgorithm) {
String name = signingAlgorithm.toUpperCase();
JwsAlgorithm jwsAlgorithm = SignatureAlgorithm.from(name);
if (jwsAlgorithm == null) {
@ -108,7 +128,7 @@ public final class OAuth2AuthorizationServerPropertiesRegistrationAdapter {
return jwsAlgorithm;
}
private static SignatureAlgorithm signatureAlgorithm(String signatureAlgorithm) {
private SignatureAlgorithm signatureAlgorithm(String signatureAlgorithm) {
return SignatureAlgorithm.from(signatureAlgorithm.toUpperCase());
}

@ -48,26 +48,17 @@ class OAuth2AuthorizationServerWebSecurityConfiguration {
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
// @formatter:off
http
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
);
// @formatter:on
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
// @formatter:on
return http.build();
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server;
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.util.Collections;
import java.util.Map;
@ -34,9 +34,8 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
* properties are defined.
*
* @author Steve Riesenberg
* @since 3.1.0
*/
public class RegisteredClientsConfiguredCondition extends SpringBootCondition {
class RegisteredClientsConfiguredCondition extends SpringBootCondition {
private static final Bindable<Map<String, OAuth2AuthorizationServerProperties.Client>> STRING_CLIENT_MAP = Bindable
.mapOf(String.class, OAuth2AuthorizationServerProperties.Client.class);

@ -1,64 +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;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2AuthorizationServerPropertiesRegistrationAdapter}.
*
* @author Steve Riesenberg
*/
class OAuth2AuthorizationServerPropertiesSettingsAdapterTests {
@Test
void getAuthorizationServerSettingsWhenValidParametersShouldAdapt() {
OAuth2AuthorizationServerProperties properties = createAuthorizationServerProperties();
AuthorizationServerSettings settings = OAuth2AuthorizationServerPropertiesSettingsAdapter
.getAuthorizationServerSettings(properties);
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");
}
private OAuth2AuthorizationServerProperties createAuthorizationServerProperties() {
OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();
properties.setIssuer("https://example.com");
OAuth2AuthorizationServerProperties.Endpoint endpoints = properties.getEndpoint();
endpoints.setAuthorizationUri("/authorize");
endpoints.setTokenUri("/token");
endpoints.setJwkSetUri("/jwks");
endpoints.setTokenRevocationUri("/revoke");
endpoints.setTokenIntrospectionUri("/introspect");
OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoints.getOidc();
oidc.setLogoutUri("/logout");
oidc.setClientRegistrationUri("/register");
oidc.setUserInfoUri("/user");
return properties;
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.server;
package org.springframework.boot.autoconfigure.security.oauth2.server.servlet;
import java.time.Duration;
import java.util.List;
@ -25,24 +25,28 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2AuthorizationServerPropertiesRegistrationAdapter}.
* Tests for {@link OAuth2AuthorizationServerPropertiesMapper}.
*
* @author Steve Riesenberg
*/
class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
class OAuth2AuthorizationServerPropertiesMapperTests {
private final OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();
private final OAuth2AuthorizationServerPropertiesMapper mapper = new OAuth2AuthorizationServerPropertiesMapper(
this.properties);
@Test
void getRegisteredClientsWhenValidParametersShouldAdapt() {
OAuth2AuthorizationServerProperties properties = new OAuth2AuthorizationServerProperties();
OAuth2AuthorizationServerProperties.Client client = createClient();
properties.getClient().put("foo", client);
List<RegisteredClient> registeredClients = OAuth2AuthorizationServerPropertiesRegistrationAdapter
.getRegisteredClients(properties);
this.properties.getClient().put("foo", client);
List<RegisteredClient> registeredClients = this.mapper.asRegisteredClients();
assertThat(registeredClients).hasSize(1);
RegisteredClient registeredClient = registeredClients.get(0);
assertThat(registeredClient.getClientId()).isEqualTo("foo");
@ -90,4 +94,29 @@ class OAuth2AuthorizationServerPropertiesRegistrationAdapterTests {
return client;
}
@Test
void getAuthorizationServerSettingsWhenValidParametersShouldAdapt() {
this.properties.setIssuer("https://example.com");
OAuth2AuthorizationServerProperties.Endpoint endpoints = this.properties.getEndpoint();
endpoints.setAuthorizationUri("/authorize");
endpoints.setTokenUri("/token");
endpoints.setJwkSetUri("/jwks");
endpoints.setTokenRevocationUri("/revoke");
endpoints.setTokenIntrospectionUri("/introspect");
OAuth2AuthorizationServerProperties.OidcEndpoint oidc = endpoints.getOidc();
oidc.setLogoutUri("/logout");
oidc.setClientRegistrationUri("/register");
oidc.setUserInfoUri("/user");
AuthorizationServerSettings settings = this.mapper.asAuthorizationServerSettings();
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");
}
}

@ -227,9 +227,7 @@ Alternatively, you can define your own `OpaqueTokenIntrospector` bean for servle
[[web.security.oauth2.authorization-server]]
==== Authorization Server
If you have `spring-security-oauth2-authorization-server` on your classpath, you can take advantage of some auto-configuration to set up an OAuth2 Authorization Server.
This configuration makes use of the properties under `OAuth2AuthorizationServerProperties`.
The properties are only applicable for servlet applications.
If you have `spring-security-oauth2-authorization-server` on your classpath, you can take advantage of some auto-configuration to set up a Servlet-based OAuth2 Authorization Server.
You can register multiple OAuth2 clients under the `spring.security.oauth2.authorizationserver.client` prefix, as shown in the following example:

@ -5,6 +5,6 @@ plugins {
description = "Starter for using Spring Authorization Server features"
dependencies {
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
api("org.springframework.security:spring-security-oauth2-authorization-server")
}

@ -7,7 +7,6 @@ description = "Spring Boot OAuth2 Authorization Server smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-oauth2-authorization-server"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
testImplementation("org.apache.httpcomponents.client5:httpclient5")

Loading…
Cancel
Save