Revert OAuth2 Client Registration Grant Type Hierarchy

Closes gh-14554
pull/14688/head
Madhura Bhave 6 years ago
parent 546bd89f2e
commit daa3d457b7

@ -16,7 +16,6 @@
package org.springframework.boot.autoconfigure.security.oauth2.client; package org.springframework.boot.autoconfigure.security.oauth2.client;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -36,46 +35,32 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.1.0 * @since 2.1.0
*/ */
public class ClientsConfiguredCondition extends SpringBootCondition {
private static final Bindable<Map<String, OAuth2ClientProperties.LoginClientRegistration>> STRING_LOGIN_REGISTRATION_MAP = Bindable public class ClientsConfiguredCondition extends SpringBootCondition {
.mapOf(String.class, OAuth2ClientProperties.LoginClientRegistration.class);
private static final Bindable<Map<String, OAuth2ClientProperties.AuthorizationCodeClientRegistration>> STRING_AUTHORIZATION_CODE_REGISTRATION_MAP = Bindable private static final Bindable<Map<String, OAuth2ClientProperties.Registration>> STRING_REGISTRATION_MAP = Bindable
.mapOf(String.class, .mapOf(String.class, OAuth2ClientProperties.Registration.class);
OAuth2ClientProperties.AuthorizationCodeClientRegistration.class);
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth2 Clients Configured Condition"); .forCondition("OAuth2 Clients Configured Condition");
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = getRegistrations( Map<String, OAuth2ClientProperties.Registration> registrations = getRegistrations(
context.getEnvironment()); context.getEnvironment());
if (!registrations.isEmpty()) { if (!registrations.isEmpty()) {
return ConditionOutcome.match(message.foundExactly( return ConditionOutcome.match(message
"registered clients " + registrations.values().stream().map( .foundExactly("registered clients " + registrations.values().stream()
OAuth2ClientProperties.BaseClientRegistration::getClientId) .map(OAuth2ClientProperties.Registration::getClientId)
.collect(Collectors.joining(", ")))); .collect(Collectors.joining(", "))));
} }
return ConditionOutcome.noMatch(message.notAvailable("registered clients")); return ConditionOutcome.noMatch(message.notAvailable("registered clients"));
} }
private Map<String, OAuth2ClientProperties.BaseClientRegistration> getRegistrations( private Map<String, OAuth2ClientProperties.Registration> getRegistrations(
Environment environment) { Environment environment) {
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = new HashMap<>(); return Binder.get(environment).bind("spring.security.oauth2.client.registration",
Map<String, OAuth2ClientProperties.LoginClientRegistration> loginClientRegistrations = Binder STRING_REGISTRATION_MAP).orElse(Collections.emptyMap());
.get(environment).bind("spring.security.oauth2.client.registration.login",
STRING_LOGIN_REGISTRATION_MAP)
.orElse(Collections.emptyMap());
Map<String, OAuth2ClientProperties.AuthorizationCodeClientRegistration> authCodeClientRegistrations = Binder
.get(environment)
.bind("spring.security.oauth2.client.registration.authorizationcode",
STRING_AUTHORIZATION_CODE_REGISTRATION_MAP)
.orElse(Collections.emptyMap());
registrations.putAll(loginClientRegistrations);
registrations.putAll(authCodeClientRegistrations);
return registrations;
} }
} }

@ -44,115 +44,31 @@ public class OAuth2ClientProperties {
/** /**
* OAuth client registrations. * OAuth client registrations.
*/ */
private final Registration registration = new Registration(); private final Map<String, Registration> registration = new HashMap<>();
public Map<String, Provider> getProvider() { public Map<String, Provider> getProvider() {
return this.provider; return this.provider;
} }
public Registration getRegistration() { public Map<String, Registration> getRegistration() {
return this.registration; return this.registration;
} }
@PostConstruct @PostConstruct
public void validate() { public void validate() {
this.getRegistration().getLogin().values().forEach(this::validateRegistration); this.getRegistration().values().forEach(this::validateRegistration);
this.getRegistration().getAuthorizationCode().values()
.forEach(this::validateRegistration);
} }
private void validateRegistration(BaseClientRegistration registration) { private void validateRegistration(Registration registration) {
if (!StringUtils.hasText(registration.getClientId())) { if (!StringUtils.hasText(registration.getClientId())) {
throw new IllegalStateException("Client id must not be empty."); throw new IllegalStateException("Client id must not be empty.");
} }
} }
public static class Registration {
/**
* OpenID Connect client registrations.
*/
private Map<String, LoginClientRegistration> login = new HashMap<>();
/**
* OAuth2 authorization_code client registrations.
*/
private Map<String, AuthorizationCodeClientRegistration> authorizationCode = new HashMap<>();
public Map<String, LoginClientRegistration> getLogin() {
return this.login;
}
public void setLogin(Map<String, LoginClientRegistration> login) {
this.login = login;
}
public Map<String, AuthorizationCodeClientRegistration> getAuthorizationCode() {
return this.authorizationCode;
}
public void setAuthorizationCode(
Map<String, AuthorizationCodeClientRegistration> authorizationCode) {
this.authorizationCode = authorizationCode;
}
}
/** /**
* A single client registration for OpenID Connect login. * A single client registration.
*/
public static class LoginClientRegistration extends BaseClientRegistration {
/**
* Redirect URI. May be left blank when using a pre-defined provider.
*/ */
private String redirectUri; public static class Registration {
public String getRedirectUri() {
return this.redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
@Deprecated
public String getRedirectUriTemplate() {
return getRedirectUri();
}
@Deprecated
public void setRedirectUriTemplate(String redirectUri) {
setRedirectUri(redirectUri);
}
}
/**
* A single client registration for OAuth2 authorization_code flow.
*/
public static class AuthorizationCodeClientRegistration
extends BaseClientRegistration {
/**
* Redirect URI for the registration.
*/
private String redirectUri;
public String getRedirectUri() {
return this.redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
}
/**
* Base class for a single client registration.
*/
public static class BaseClientRegistration {
/** /**
* Reference to the OAuth 2.0 provider to use. May reference an element from the * Reference to the OAuth 2.0 provider to use. May reference an element from the
@ -182,6 +98,11 @@ public class OAuth2ClientProperties {
*/ */
private String authorizationGrantType; private String authorizationGrantType;
/**
* Redirect URI. May be left blank when using a pre-defined provider.
*/
private String redirectUri;
/** /**
* Authorization scopes. May be left blank when using a pre-defined provider. * Authorization scopes. May be left blank when using a pre-defined provider.
*/ */
@ -232,6 +153,24 @@ public class OAuth2ClientProperties {
this.authorizationGrantType = authorizationGrantType; this.authorizationGrantType = authorizationGrantType;
} }
public String getRedirectUri() {
return this.redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
@Deprecated
public String getRedirectUriTemplate() {
return getRedirectUri();
}
@Deprecated
public void setRedirectUriTemplate(String redirectUri) {
setRedirectUri(redirectUri);
}
public Set<String> getScope() { public Set<String> getScope() {
return this.scope; return this.scope;
} }

@ -1,115 +0,0 @@
/*
* Copyright 2012-2018 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
*
* http://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.client;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
/**
* {@link EnvironmentPostProcessor} that migrates legacy OAuth2 login client properties
* under the `spring.security.oauth2.client.login` prefix.
*
* @author Madhura Bhave
* @since 2.1.0
*/
public class OAuth2ClientPropertiesEnvironmentPostProcessor
implements EnvironmentPostProcessor, Ordered {
private static final Bindable<Map<String, OAuth2ClientProperties.LoginClientRegistration>> STRING_LEGACY_REGISTRATION_MAP = Bindable
.mapOf(String.class, OAuth2ClientProperties.LoginClientRegistration.class);
private static final String PREFIX = "spring.security.oauth2.client.registration";
private static final String LOGIN_REGISTRATION_PREFIX = PREFIX + ".login.";
private static final String UPDATED_PROPERTY_SOURCE_SUFFIX = "-updated-oauth-client";
private int order = ConfigFileApplicationListener.DEFAULT_ORDER + 1;
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
environment.getPropertySources().forEach((propertySource) -> {
String name = propertySource.getName();
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources
.from(propertySource);
ConfigurationPropertySource source = sources.iterator().next();
Binder binder = new Binder(sources);
Map<String, Object> map = new LinkedHashMap<>();
MapPropertySource updatedPropertySource = new MapPropertySource(
name + UPDATED_PROPERTY_SOURCE_SUFFIX, map);
Map<String, OAuth2ClientProperties.LoginClientRegistration> registrations = binder
.bind(PREFIX, STRING_LEGACY_REGISTRATION_MAP)
.orElse(Collections.emptyMap());
registrations.entrySet()
.forEach((entry) -> addProperties(entry, source, map));
if (!map.isEmpty()) {
environment.getPropertySources().addBefore(name, updatedPropertySource);
}
});
}
private void addProperties(
Map.Entry<String, OAuth2ClientProperties.LoginClientRegistration> entry,
ConfigurationPropertySource source, Map<String, Object> map) {
OAuth2ClientProperties.LoginClientRegistration registration = entry.getValue();
String registrationId = entry.getKey();
addProperty(registrationId, "client-id", registration::getClientId, map, source);
addProperty(registrationId, "client-secret", registration::getClientSecret, map,
source);
addProperty(registrationId, "client-name", registration::getClientName, map,
source);
addProperty(registrationId, "redirect-uri-template", registration::getRedirectUri,
map, source);
addProperty(registrationId, "authorization-grant-type",
registration::getAuthorizationGrantType, map, source);
addProperty(registrationId, "client-authentication-method",
registration::getClientAuthenticationMethod, map, source);
addProperty(registrationId, "provider", registration::getProvider, map, source);
addProperty(registrationId, "scope", registration::getScope, map, source);
}
private void addProperty(String registrationId, String property,
Supplier<Object> valueSupplier, Map<String, Object> map,
ConfigurationPropertySource source) {
String registrationKey = PREFIX + "." + registrationId + ".";
String loginRegistrationKey = LOGIN_REGISTRATION_PREFIX + registrationId + ".";
if (source.getConfigurationProperty(
ConfigurationPropertyName.of(registrationKey + property)) != null) {
map.put(loginRegistrationKey + property, valueSupplier.get());
}
}
@Override
public int getOrder() {
return this.order;
}
}

@ -50,35 +50,20 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
public static Map<String, ClientRegistration> getClientRegistrations( public static Map<String, ClientRegistration> getClientRegistrations(
OAuth2ClientProperties properties) { OAuth2ClientProperties properties) {
Map<String, ClientRegistration> clientRegistrations = new HashMap<>(); Map<String, ClientRegistration> clientRegistrations = new HashMap<>();
properties.getRegistration().getLogin() properties.getRegistration().forEach((key, value) -> clientRegistrations.put(key,
.forEach((key, value) -> clientRegistrations.put(key, getClientRegistration(key, value, properties.getProvider())));
getLoginClientRegistration(key, value,
properties.getProvider())));
properties.getRegistration().getAuthorizationCode()
.forEach((key, value) -> clientRegistrations.put(key,
getAuthorizationCodeClientRegistration(key, value,
properties.getProvider())));
return clientRegistrations; return clientRegistrations;
} }
private static ClientRegistration getAuthorizationCodeClientRegistration( private static ClientRegistration getClientRegistration(String registrationId,
String registrationId, OAuth2ClientProperties.Registration properties,
OAuth2ClientProperties.AuthorizationCodeClientRegistration properties,
Map<String, Provider> providers) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
Builder builder = getBuilder(map, registrationId, properties, providers);
map.from(properties::getRedirectUri).to(builder::redirectUriTemplate);
return builder.build();
}
private static Builder getBuilder(PropertyMapper map, String registrationId,
OAuth2ClientProperties.BaseClientRegistration properties,
Map<String, Provider> providers) { Map<String, Provider> providers) {
Builder builder = getBuilderFromIssuerIfPossible(registrationId, Builder builder = getBuilderFromIssuerIfPossible(registrationId,
properties.getProvider(), providers); properties.getProvider(), providers);
if (builder == null) { if (builder == null) {
builder = getBuilder(registrationId, properties.getProvider(), providers); builder = getBuilder(registrationId, properties.getProvider(), providers);
} }
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(properties::getClientId).to(builder::clientId); map.from(properties::getClientId).to(builder::clientId);
map.from(properties::getClientSecret).to(builder::clientSecret); map.from(properties::getClientSecret).to(builder::clientSecret);
map.from(properties::getClientAuthenticationMethod) map.from(properties::getClientAuthenticationMethod)
@ -86,18 +71,10 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
.to(builder::clientAuthenticationMethod); .to(builder::clientAuthenticationMethod);
map.from(properties::getAuthorizationGrantType).as(AuthorizationGrantType::new) map.from(properties::getAuthorizationGrantType).as(AuthorizationGrantType::new)
.to(builder::authorizationGrantType); .to(builder::authorizationGrantType);
map.from(properties::getRedirectUri).to(builder::redirectUriTemplate);
map.from(properties::getScope).as((scope) -> StringUtils.toStringArray(scope)) map.from(properties::getScope).as((scope) -> StringUtils.toStringArray(scope))
.to(builder::scope); .to(builder::scope);
map.from(properties::getClientName).to(builder::clientName); map.from(properties::getClientName).to(builder::clientName);
return builder;
}
private static ClientRegistration getLoginClientRegistration(String registrationId,
OAuth2ClientProperties.LoginClientRegistration properties,
Map<String, Provider> providers) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
Builder builder = getBuilder(map, registrationId, properties, providers);
map.from(properties::getRedirectUri).to(builder::redirectUriTemplate);
return builder.build(); return builder.build();
} }

@ -711,15 +711,6 @@
"name": "spring.session.hazelcast.flush-mode", "name": "spring.session.hazelcast.flush-mode",
"defaultValue": "on-save" "defaultValue": "on-save"
}, },
{
"name" : "spring.security.oauth2.client.registration",
"type" : "java.util.Map<java.lang.String,org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.LoginClientRegistration>",
"description" : "Maps client registration-id to a client registration.",
"deprecation" : {
"replacement" : "spring.security.oauth2.client.registration.login",
"level" : "warning"
}
},
{ {
"name": "spring.session.servlet.filter-dispatcher-types", "name": "spring.session.servlet.filter-dispatcher-types",
"defaultValue": [ "defaultValue": [

@ -7,10 +7,6 @@ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingL
org.springframework.context.ApplicationListener=\ org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesEnvironmentPostProcessor
# Auto Configuration Import Listeners # Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

@ -1,158 +0,0 @@
/*
* Copyright 2012-2018 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
*
* http://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.client;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2ClientPropertiesEnvironmentPostProcessor}.
*
* @author Madhura Bhave
*/
public class OAuth2ClientPropertiesEnvironmentPostProcessorTests {
private OAuth2ClientPropertiesEnvironmentPostProcessor postProcessor = new OAuth2ClientPropertiesEnvironmentPostProcessor();
private MockEnvironment environment;
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.github-client.";
private static final String ENVIRONMENT_REGISTRATION_PREFIX = "SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GITHUB-CLIENT_";
private static final String LOGIN_REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login.github-client.";
@Before
public void setup() {
this.environment = new MockEnvironment();
}
@Test
public void postProcessorWhenLegacyPropertiesShouldConvert() {
Map<String, Object> properties = new HashMap<>();
properties.put(REGISTRATION_PREFIX + "client-id", "my-client-id");
properties.put(REGISTRATION_PREFIX + "client-secret", "my-client-secret");
properties.put(REGISTRATION_PREFIX + "redirect-uri-template",
"http://my-redirect-uri.com");
properties.put(REGISTRATION_PREFIX + "provider", "github");
properties.put(REGISTRATION_PREFIX + "scope", "user");
properties.put(REGISTRATION_PREFIX + "client-name", "my-client-name");
properties.put(REGISTRATION_PREFIX + "authorization-grant-type",
"authorization_code");
properties.put(REGISTRATION_PREFIX + "client-authentication-method", "FORM");
MapPropertySource source = new MapPropertySource("test", properties);
this.environment.getPropertySources().addFirst(source);
this.postProcessor.postProcessEnvironment(this.environment, null);
assertPropertyMigration();
}
@Test
public void postProcessorDoesNotCopyMissingProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put(REGISTRATION_PREFIX + "client-id", "my-client-id");
MapPropertySource source = new MapPropertySource("test", properties);
this.environment.getPropertySources().addFirst(source);
this.postProcessor.postProcessEnvironment(this.environment, null);
assertThat(this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "client-id"))
.isEqualTo("my-client-id");
assertThat(
this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "client-secret"))
.isNull();
}
@Test
public void postProcessorWhenLegacyEnvironmentVariablesPropertiesShouldConvert() {
Map<String, Object> properties = new HashMap<>();
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "CLIENTID", "my-client-id");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "CLIENTSECRET",
"my-client-secret");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "REDIRECTURITEMPLATE",
"http://my-redirect-uri.com");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "PROVIDER", "github");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "SCOPE", "user");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "CLIENTNAME", "my-client-name");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "AUTHORIZATIONGRANTTYPE",
"authorization_code");
properties.put(ENVIRONMENT_REGISTRATION_PREFIX + "CLIENTAUTHENTICATIONMETHOD",
"FORM");
SystemEnvironmentPropertySource source = new SystemEnvironmentPropertySource(
"test-" + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
properties);
this.environment.getPropertySources().addFirst(source);
this.postProcessor.postProcessEnvironment(this.environment, null);
assertPropertyMigration();
}
@Test
public void postProcessorWhenNewPropertiesShouldDoNothing() {
Map<String, Object> properties = new HashMap<>();
properties.put(LOGIN_REGISTRATION_PREFIX + "client-id", "my-client-id");
properties.put(LOGIN_REGISTRATION_PREFIX + "client-secret", "my-client-secret");
properties.put(LOGIN_REGISTRATION_PREFIX + "redirect-uri-template",
"http://my-redirect-uri.com");
properties.put(LOGIN_REGISTRATION_PREFIX + "provider", "github");
properties.put(LOGIN_REGISTRATION_PREFIX + "scope", "user");
properties.put(LOGIN_REGISTRATION_PREFIX + "client-name", "my-client-name");
properties.put(LOGIN_REGISTRATION_PREFIX + "authorization-grant-type",
"authorization_code");
properties.put(LOGIN_REGISTRATION_PREFIX + "client-authentication-method",
"FORM");
MapPropertySource source = new MapPropertySource("test", properties);
this.environment.getPropertySources().addFirst(source);
MutablePropertySources propertySources = new MutablePropertySources(
this.environment.getPropertySources());
this.postProcessor.postProcessEnvironment(this.environment, null);
assertPropertyMigration();
assertThat(this.environment.getPropertySources())
.containsExactlyElementsOf(propertySources);
}
private void assertPropertyMigration() {
assertThat(this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "client-id"))
.isEqualTo("my-client-id");
assertThat(
this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "client-secret"))
.isEqualTo("my-client-secret");
assertThat(this.environment
.getProperty(LOGIN_REGISTRATION_PREFIX + "redirect-uri-template"))
.isEqualTo("http://my-redirect-uri.com");
assertThat(this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "provider"))
.isEqualTo("github");
assertThat(this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "scope"))
.isEqualTo("user");
assertThat(
this.environment.getProperty(LOGIN_REGISTRATION_PREFIX + "client-name"))
.isEqualTo("my-client-name");
assertThat(this.environment
.getProperty(LOGIN_REGISTRATION_PREFIX + "authorization-grant-type"))
.isEqualTo("authorization_code");
assertThat(this.environment
.getProperty(LOGIN_REGISTRATION_PREFIX + "client-authentication-method"))
.isEqualTo("FORM");
}
}

@ -26,8 +26,8 @@ import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.LoginClientRegistration;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Registration;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -68,16 +68,16 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
provider.setUserInfoAuthenticationMethod("form"); provider.setUserInfoAuthenticationMethod("form");
provider.setUserNameAttribute("sub"); provider.setUserNameAttribute("sub");
provider.setJwkSetUri("http://example.com/jwk"); provider.setJwkSetUri("http://example.com/jwk");
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setProvider("provider"); registration.setProvider("provider");
login.setClientId("clientId"); registration.setClientId("clientId");
login.setClientSecret("clientSecret"); registration.setClientSecret("clientSecret");
login.setClientAuthenticationMethod("post"); registration.setClientAuthenticationMethod("post");
login.setAuthorizationGrantType("authorization_code"); registration.setAuthorizationGrantType("authorization_code");
login.setRedirectUri("http://example.com/redirect"); registration.setRedirectUri("http://example.com/redirect");
login.setScope(Collections.singleton("scope")); registration.setScope(Collections.singleton("scope"));
login.setClientName("clientName"); registration.setClientName("clientName");
properties.getRegistration().getLogin().put("registration", login); properties.getRegistration().put("registration", registration);
properties.getProvider().put("provider", provider); properties.getProvider().put("provider", provider);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
@ -110,11 +110,11 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt() { public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt() {
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setProvider("google"); registration.setProvider("google");
login.setClientId("clientId"); registration.setClientId("clientId");
login.setClientSecret("clientSecret"); registration.setClientSecret("clientSecret");
properties.getRegistration().getLogin().put("registration", login); properties.getRegistration().put("registration", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("registration"); ClientRegistration adapted = registrations.get("registration");
@ -145,16 +145,16 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt() { public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt() {
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setProvider("google"); registration.setProvider("google");
login.setClientId("clientId"); registration.setClientId("clientId");
login.setClientSecret("clientSecret"); registration.setClientSecret("clientSecret");
login.setClientAuthenticationMethod("post"); registration.setClientAuthenticationMethod("post");
login.setAuthorizationGrantType("authorization_code"); registration.setAuthorizationGrantType("authorization_code");
login.setRedirectUri("http://example.com/redirect"); registration.setRedirectUri("http://example.com/redirect");
login.setScope(Collections.singleton("scope")); registration.setScope(Collections.singleton("scope"));
login.setClientName("clientName"); registration.setClientName("clientName");
properties.getRegistration().getLogin().put("registration", login); properties.getRegistration().put("registration", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("registration"); ClientRegistration adapted = registrations.get("registration");
@ -188,9 +188,9 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void getClientRegistrationsWhenUnknownProviderShouldThrowException() { public void getClientRegistrationsWhenUnknownProviderShouldThrowException() {
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setProvider("missing"); registration.setProvider("missing");
properties.getRegistration().getLogin().put("registration", login); properties.getRegistration().put("registration", registration);
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(() -> OAuth2ClientPropertiesRegistrationAdapter .isThrownBy(() -> OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties)) .getClientRegistrations(properties))
@ -200,46 +200,10 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void getClientRegistrationsWhenProviderNotSpecifiedShouldUseRegistrationId() { public void getClientRegistrationsWhenProviderNotSpecifiedShouldUseRegistrationId() {
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setClientId("clientId");
login.setClientSecret("clientSecret");
properties.getRegistration().getLogin().put("google", login);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("google");
ProviderDetails adaptedProvider = adapted.getProviderDetails();
assertThat(adaptedProvider.getAuthorizationUri())
.isEqualTo("https://accounts.google.com/o/oauth2/v2/auth");
assertThat(adaptedProvider.getTokenUri())
.isEqualTo("https://www.googleapis.com/oauth2/v4/token");
assertThat(adaptedProvider.getUserInfoEndpoint().getUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/userinfo");
assertThat(adaptedProvider.getUserInfoEndpoint().getAuthenticationMethod())
.isEqualTo(
org.springframework.security.oauth2.core.AuthenticationMethod.HEADER);
assertThat(adaptedProvider.getJwkSetUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/certs");
assertThat(adapted.getRegistrationId()).isEqualTo("google");
assertThat(adapted.getClientId()).isEqualTo("clientId");
assertThat(adapted.getClientSecret()).isEqualTo("clientSecret");
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRedirectUriTemplate())
.isEqualTo("{baseUrl}/{action}/oauth2/code/{registrationId}");
assertThat(adapted.getScopes()).containsExactly("openid", "profile", "email");
assertThat(adapted.getClientName()).isEqualTo("Google");
}
@Test
public void getClientRegistrationsWhenAuthorizationCodeClientShouldAdapt() {
OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.AuthorizationCodeClientRegistration registration = new OAuth2ClientProperties.AuthorizationCodeClientRegistration();
registration.setClientId("clientId"); registration.setClientId("clientId");
registration.setClientSecret("clientSecret"); registration.setClientSecret("clientSecret");
registration.setRedirectUri("http://my-redirect-uri.com"); properties.getRegistration().put("google", registration);
properties.getRegistration().getAuthorizationCode().put("google", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("google"); ClientRegistration adapted = registrations.get("google");
@ -258,12 +222,12 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
assertThat(adapted.getRegistrationId()).isEqualTo("google"); assertThat(adapted.getRegistrationId()).isEqualTo("google");
assertThat(adapted.getClientId()).isEqualTo("clientId"); assertThat(adapted.getClientId()).isEqualTo("clientId");
assertThat(adapted.getClientSecret()).isEqualTo("clientSecret"); assertThat(adapted.getClientSecret()).isEqualTo("clientSecret");
assertThat(adapted.getRedirectUriTemplate())
.isEqualTo("http://my-redirect-uri.com");
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo( assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC); org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo( assertThat(adapted.getAuthorizationGrantType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE); org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRedirectUriTemplate())
.isEqualTo("{baseUrl}/{action}/oauth2/code/{registrationId}");
assertThat(adapted.getScopes()).containsExactly("openid", "profile", "email"); assertThat(adapted.getScopes()).containsExactly("openid", "profile", "email");
assertThat(adapted.getClientName()).isEqualTo("Google"); assertThat(adapted.getClientName()).isEqualTo("Google");
} }
@ -271,8 +235,8 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void getClientRegistrationsWhenProviderNotSpecifiedAndUnknownProviderShouldThrowException() { public void getClientRegistrationsWhenProviderNotSpecifiedAndUnknownProviderShouldThrowException() {
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
properties.getRegistration().getLogin().put("missing", login); properties.getRegistration().put("missing", registration);
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(() -> OAuth2ClientPropertiesRegistrationAdapter .isThrownBy(() -> OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties)) .getClientRegistrations(properties))
@ -283,7 +247,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration() public void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration()
throws Exception { throws Exception {
LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); Registration login = new OAuth2ClientProperties.Registration();
login.setClientId("clientId"); login.setClientId("clientId");
login.setClientSecret("clientSecret"); login.setClientSecret("clientSecret");
testOidcConfiguration(login, "okta"); testOidcConfiguration(login, "okta");
@ -292,7 +256,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Test @Test
public void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration() public void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration()
throws Exception { throws Exception {
OAuth2ClientProperties.LoginClientRegistration login = new LoginClientRegistration(); OAuth2ClientProperties.Registration login = new Registration();
login.setProvider("okta-oidc"); login.setProvider("okta-oidc");
login.setClientId("clientId"); login.setClientId("clientId");
login.setClientSecret("clientSecret"); login.setClientSecret("clientSecret");
@ -307,13 +271,13 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
String issuer = this.server.url("").toString(); String issuer = this.server.url("").toString();
String cleanIssuerPath = cleanIssuerPath(issuer); String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponse(cleanIssuerPath); setupMockResponse(cleanIssuerPath);
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
login.setProvider("okta-oidc"); registration.setProvider("okta-oidc");
login.setClientId("clientId"); registration.setClientId("clientId");
login.setClientSecret("clientSecret"); registration.setClientSecret("clientSecret");
login.setClientAuthenticationMethod("post"); registration.setClientAuthenticationMethod("post");
login.setRedirectUri("http://example.com/redirect"); registration.setRedirectUri("http://example.com/redirect");
login.setScope(Collections.singleton("user")); registration.setScope(Collections.singleton("user"));
Provider provider = new Provider(); Provider provider = new Provider();
provider.setIssuerUri(issuer); provider.setIssuerUri(issuer);
provider.setAuthorizationUri("http://example.com/auth"); provider.setAuthorizationUri("http://example.com/auth");
@ -323,7 +287,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
provider.setJwkSetUri("http://example.com/jwk"); provider.setJwkSetUri("http://example.com/jwk");
OAuth2ClientProperties properties = new OAuth2ClientProperties(); OAuth2ClientProperties properties = new OAuth2ClientProperties();
properties.getProvider().put("okta-oidc", provider); properties.getProvider().put("okta-oidc", provider);
properties.getRegistration().getLogin().put("okta", login); properties.getRegistration().put("okta", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("okta"); ClientRegistration adapted = registrations.get("okta");
@ -347,8 +311,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
.isEqualTo("sub"); .isEqualTo("sub");
} }
private void testOidcConfiguration( private void testOidcConfiguration(OAuth2ClientProperties.Registration registration,
OAuth2ClientProperties.LoginClientRegistration registration,
String providerId) throws Exception { String providerId) throws Exception {
this.server = new MockWebServer(); this.server = new MockWebServer();
this.server.start(); this.server.start();
@ -359,7 +322,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
Provider provider = new Provider(); Provider provider = new Provider();
provider.setIssuerUri(issuer); provider.setIssuerUri(issuer);
properties.getProvider().put(providerId, provider); properties.getProvider().put(providerId, provider);
properties.getRegistration().getLogin().put("okta", registration); properties.getRegistration().put("okta", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties); .getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("okta"); ClientRegistration adapted = registrations.get("okta");

@ -18,9 +18,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.AuthorizationCodeClientRegistration;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.LoginClientRegistration;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/** /**
@ -34,40 +31,21 @@ public class OAuth2ClientPropertiesTests {
private OAuth2ClientProperties properties = new OAuth2ClientProperties(); private OAuth2ClientProperties properties = new OAuth2ClientProperties();
@Test @Test
public void clientIdAbsentForLoginClientsThrowsException() { public void clientIdAbsentThrowsException() {
LoginClientRegistration registration = new LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
registration.setClientSecret("secret"); registration.setClientSecret("secret");
registration.setProvider("google"); registration.setProvider("google");
this.properties.getRegistration().getLogin().put("foo", registration); this.properties.getRegistration().put("foo", registration);
assertThatIllegalStateException().isThrownBy(this.properties::validate) assertThatIllegalStateException().isThrownBy(this.properties::validate)
.withMessageContaining("Client id must not be empty."); .withMessageContaining("Client id must not be empty.");
} }
@Test @Test
public void clientSecretAbsentShouldNotThrowException() { public void clientSecretAbsentShouldNotThrowException() {
LoginClientRegistration registration = new LoginClientRegistration(); OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
registration.setClientId("foo");
registration.setProvider("google");
this.properties.getRegistration().getLogin().put("foo", registration);
this.properties.validate();
}
@Test
public void clientIdAbsentForAuthorizationCodeClientsThrowsException() {
AuthorizationCodeClientRegistration registration = new AuthorizationCodeClientRegistration();
registration.setClientSecret("secret");
registration.setProvider("google");
this.properties.getRegistration().getAuthorizationCode().put("foo", registration);
assertThatIllegalStateException().isThrownBy(this.properties::validate)
.withMessageContaining("Client id must not be empty.");
}
@Test
public void clientSecretAbsentForAuthorizationCodeClientDoesNotThrowException() {
AuthorizationCodeClientRegistration registration = new AuthorizationCodeClientRegistration();
registration.setClientId("foo"); registration.setClientId("foo");
registration.setProvider("google"); registration.setProvider("google");
this.properties.getRegistration().getAuthorizationCode().put("foo", registration); this.properties.getRegistration().put("foo", registration);
this.properties.validate(); this.properties.validate();
} }

@ -52,7 +52,7 @@ public class ReactiveOAuth2ClientAutoConfigurationTests {
.withConfiguration( .withConfiguration(
AutoConfigurations.of(ReactiveOAuth2ClientAutoConfiguration.class)); AutoConfigurations.of(ReactiveOAuth2ClientAutoConfiguration.class));
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login"; private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
@Test @Test
public void autoConfigurationShouldBackOffForServletEnvironments() { public void autoConfigurationShouldBackOffForServletEnvironments() {

@ -33,7 +33,7 @@ public class OAuth2ClientRegistrationRepositoryConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login"; private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
@Test @Test
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() { public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {

@ -3264,13 +3264,31 @@ Spring.
==== Client ==== Client
If you have `spring-security-oauth2-client` on your classpath, you can take advantage of If you have `spring-security-oauth2-client` on your classpath, you can take advantage of
some auto-configuration to make it easy to set up an OAuth2/Open ID Connect clients. This configuration some auto-configuration to make it easy to set up an OAuth2/Open ID Connect clients. This configuration
makes use of the properties under `OAuth2ClientProperties`. makes use of the properties under `OAuth2ClientProperties`. The same properties are applicable to both servlet and reactive applications.
You can register multiple OAuth2/OpenID Connect providers under the `spring.security.oauth2.client.provider` You can register multiple OAuth2 clients and providers under the
prefix, as shown in the following example: `spring.security.oauth2.client` prefix, as shown in the following example:
[source,properties,indent=0] [source,properties,indent=0]
---- ----
spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri-template=http://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri-template=http://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=http://my-auth-server/oauth/authorize spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=http://my-auth-server/oauth/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=http://my-auth-server/oauth/token spring.security.oauth2.client.provider.my-oauth-provider.token-uri=http://my-auth-server/oauth/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=http://my-auth-server/userinfo spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=http://my-auth-server/userinfo
@ -3294,33 +3312,6 @@ Provider can be configured with the `issuer-uri`:
[[boot-features-security-oauth2-login-client-registration]]
===== OpenID Connect Login client registration
You can register multiple Open ID Connect clients under the
`spring.security.oauth2.client.registration.login` prefix, as shown in the following example:
[source,properties,indent=0]
----
spring.security.oauth2.client.registration.login.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.login.my-client-1.client-secret=password
spring.security.oauth2.client.registration.login.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.login.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.login.my-client-1.scope=user
spring.security.oauth2.client.registration.login.my-client-1.redirect-uri=http://localhost:8080/login/oauth2/code/my-client-1
spring.security.oauth2.client.registration.login.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.login.my-client-1.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.login.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.login.my-client-2.client-secret=password
spring.security.oauth2.client.registration.login.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.login.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.login.my-client-2.scope=email
spring.security.oauth2.client.registration.login.my-client-2.redirect-uri=http://localhost:8080/login/oauth2/code/my-client-2
spring.security.oauth2.client.registration.login.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.login.my-client-2.authorization-grant-type=authorization_code
----
By default, Spring Security's `OAuth2LoginAuthenticationFilter` only processes URLs By default, Spring Security's `OAuth2LoginAuthenticationFilter` only processes URLs
matching `/login/oauth2/code/*`. If you want to customize the `redirect-uri` to matching `/login/oauth2/code/*`. If you want to customize the `redirect-uri` to
use a different pattern, you need to provide configuration to process that custom pattern. use a different pattern, you need to provide configuration to process that custom pattern.
@ -3344,36 +3335,6 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
} }
---- ----
The same properties are applicable to both servlet and reactive applications.
[[boot-features-security-oauth2-authorization-code-client-registration]]
===== OAuth2 Authorization Code client registration
You can register multiple OAuth2 `authorization_code` clients under the
`spring.security.oauth2.client.registration.authorization-code` prefix, as shown in the following example:
[source,properties,indent=0]
----
spring.security.oauth2.client.registration.authorization-code.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.authorization-code.my-client-1.client-secret=password
spring.security.oauth2.client.registration.authorization-code.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.authorization-code.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.authorization-code.my-client-1.scope=user
spring.security.oauth2.client.registration.authorization-code.my-client-1.redirect-uri=http://my-redirect-uri.com
spring.security.oauth2.client.registration.authorization-code.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.authorization-code.my-client-1.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.authorization-code.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.authorization-code.my-client-2.client-secret=password
spring.security.oauth2.client.registration.authorization-code.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.authorization-code.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.authorization-code.my-client-2.scope=email
spring.security.oauth2.client.registration.authorization-code.my-client-2.redirect-uri=http://my-redirect-uri.com
spring.security.oauth2.client.registration.authorization-code.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.authorization-code.my-client-2.authorization-grant-type=authorization_code
----
[[boot-features-security-oauth2-common-providers]] [[boot-features-security-oauth2-common-providers]]
@ -3383,19 +3344,19 @@ we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta
respectively). respectively).
If you do not need to customize these providers, you can set the `provider` attribute to If you do not need to customize these providers, you can set the `provider` attribute to
the one for which you need to infer defaults. Also, if the ID of your client matches the the one for which you need to infer defaults. Also, if the key for the client registration matches a
default supported provider, Spring Boot infers that as well. default supported provider, Spring Boot infers that as well.
In other words, the two configurations in the following example use the Google provider: In other words, the two configurations in the following example use the Google provider:
[source,properties,indent=0] [source,properties,indent=0]
---- ----
spring.security.oauth2.client.registration.login.my-client.client-id=abcd spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.login.my-client.client-secret=password spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.login.my-client.provider=google spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.login.google.client-id=abcd spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.login.google.client-secret=password spring.security.oauth2.client.registration.google.client-secret=password
---- ----

@ -3,7 +3,6 @@ spring:
oauth2: oauth2:
client: client:
registration: registration:
login:
github-client-1: github-client-1:
client-id: ${APP-CLIENT-ID} client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET} client-secret: ${APP-CLIENT-SECRET}
@ -21,7 +20,6 @@ spring:
yahoo-oidc: yahoo-oidc:
client-id: ${YAHOO-CLIENT-ID} client-id: ${YAHOO-CLIENT-ID}
client-secret: ${YAHOO-CLIENT-SECRET} client-secret: ${YAHOO-CLIENT-SECRET}
authorization_code:
github-repos: github-repos:
client-id: ${APP-CLIENT-ID} client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET} client-secret: ${APP-CLIENT-SECRET}

@ -53,20 +53,13 @@ public class SampleOAuth2ClientApplicationTests {
} }
@Test @Test
public void loginShouldHaveBothOAuth2LoginClientsToChooseFrom() { public void loginShouldHaveAllOAuth2ClientsToChooseFrom() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/login", ResponseEntity<String> entity = this.restTemplate.getForEntity("/login",
String.class); String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("/oauth2/authorization/yahoo"); assertThat(entity.getBody()).contains("/oauth2/authorization/yahoo");
assertThat(entity.getBody()).contains("/oauth2/authorization/github-client-1"); assertThat(entity.getBody()).contains("/oauth2/authorization/github-client-1");
assertThat(entity.getBody()).contains("/oauth2/authorization/github-client-2"); assertThat(entity.getBody()).contains("/oauth2/authorization/github-client-2");
}
@Test
public void authorizationCodeClientIsPresent() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/login",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("/oauth2/authorization/github-repos"); assertThat(entity.getBody()).contains("/oauth2/authorization/github-repos");
} }

@ -3,7 +3,6 @@ spring:
oauth2: oauth2:
client: client:
registration: registration:
login:
github-client-1: github-client-1:
client-id: ${APP-CLIENT-ID} client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET} client-secret: ${APP-CLIENT-SECRET}

Loading…
Cancel
Save