Support authorization_code grant for OAuth2 client
This commit also refactors OAuth2 client properties. With the added support for authorization_code clients, client registrations are now divided into `login` and `authorization_code`. An environment post processor is used for backward compatibility with old Open ID Connect login clients. Closes gh-13812
This commit is contained in:
parent
5af7835e83
commit
f5deebf0cb
|
@ -16,6 +16,7 @@
|
|||
package org.springframework.boot.autoconfigure.security.oauth2.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -37,29 +38,44 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
|||
*/
|
||||
public class ClientsConfiguredCondition extends SpringBootCondition {
|
||||
|
||||
private static final Bindable<Map<String, OAuth2ClientProperties.Registration>> STRING_REGISTRATION_MAP = Bindable
|
||||
.mapOf(String.class, OAuth2ClientProperties.Registration.class);
|
||||
private static final Bindable<Map<String, OAuth2ClientProperties.LoginClientRegistration>> STRING_LOGIN_REGISTRATION_MAP = Bindable
|
||||
.mapOf(String.class, OAuth2ClientProperties.LoginClientRegistration.class);
|
||||
|
||||
private static final Bindable<Map<String, OAuth2ClientProperties.AuthorizationCodeClientRegistration>> STRING_AUTHORIZATIONCODE_REGISTRATION_MAP = Bindable
|
||||
.mapOf(String.class,
|
||||
OAuth2ClientProperties.AuthorizationCodeClientRegistration.class);
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth2 Clients Configured Condition");
|
||||
Map<String, OAuth2ClientProperties.Registration> registrations = getRegistrations(
|
||||
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = getRegistrations(
|
||||
context.getEnvironment());
|
||||
if (!registrations.isEmpty()) {
|
||||
return ConditionOutcome.match(message
|
||||
.foundExactly("registered clients " + registrations.values().stream()
|
||||
.map(OAuth2ClientProperties.Registration::getClientId)
|
||||
return ConditionOutcome.match(message.foundExactly(
|
||||
"registered clients " + registrations.values().stream().map(
|
||||
OAuth2ClientProperties.BaseClientRegistration::getClientId)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.notAvailable("registered clients"));
|
||||
}
|
||||
|
||||
private Map<String, OAuth2ClientProperties.Registration> getRegistrations(
|
||||
private Map<String, OAuth2ClientProperties.BaseClientRegistration> getRegistrations(
|
||||
Environment environment) {
|
||||
return Binder.get(environment).bind("spring.security.oauth2.client.registration",
|
||||
STRING_REGISTRATION_MAP).orElse(Collections.emptyMap());
|
||||
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = new HashMap();
|
||||
Map<String, OAuth2ClientProperties.LoginClientRegistration> loginClientRegistrations = Binder
|
||||
.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_AUTHORIZATIONCODE_REGISTRATION_MAP)
|
||||
.orElse(Collections.emptyMap());
|
||||
registrations.putAll(loginClientRegistrations);
|
||||
registrations.putAll(authCodeClientRegistrations);
|
||||
return registrations;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,32 +44,106 @@ public class OAuth2ClientProperties {
|
|||
/**
|
||||
* OAuth client registrations.
|
||||
*/
|
||||
private final Map<String, Registration> registration = new HashMap<>();
|
||||
private final Registration registration = new Registration();
|
||||
|
||||
public Map<String, Provider> getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
public Map<String, Registration> getRegistration() {
|
||||
public Registration getRegistration() {
|
||||
return this.registration;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void validate() {
|
||||
this.getRegistration().values().forEach(this::validateRegistration);
|
||||
this.getRegistration().getLogin().values().forEach(this::validateRegistration);
|
||||
this.getRegistration().getAuthorizationCode().values()
|
||||
.forEach(this::validateRegistration);
|
||||
}
|
||||
|
||||
private void validateRegistration(Registration registration) {
|
||||
private void validateRegistration(BaseClientRegistration registration) {
|
||||
if (!StringUtils.hasText(registration.getClientId())) {
|
||||
throw new IllegalStateException("Client id must not be empty.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single client registration.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static class LoginClientRegistration extends BaseClientRegistration {
|
||||
|
||||
/**
|
||||
* Redirect URI. May be left blank when using a pre-defined provider.
|
||||
*/
|
||||
private String redirectUriTemplate;
|
||||
|
||||
public String getRedirectUriTemplate() {
|
||||
return this.redirectUriTemplate;
|
||||
}
|
||||
|
||||
public void setRedirectUriTemplate(String redirectUriTemplate) {
|
||||
this.redirectUriTemplate = redirectUriTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 'provider' property or used one of the commonly used providers (google, github,
|
||||
|
@ -98,11 +172,6 @@ public class OAuth2ClientProperties {
|
|||
*/
|
||||
private String authorizationGrantType;
|
||||
|
||||
/**
|
||||
* Redirect URI. May be left blank when using a pre-defined provider.
|
||||
*/
|
||||
private String redirectUriTemplate;
|
||||
|
||||
/**
|
||||
* Authorization scopes. May be left blank when using a pre-defined provider.
|
||||
*/
|
||||
|
@ -153,14 +222,6 @@ public class OAuth2ClientProperties {
|
|||
this.authorizationGrantType = authorizationGrantType;
|
||||
}
|
||||
|
||||
public String getRedirectUriTemplate() {
|
||||
return this.redirectUriTemplate;
|
||||
}
|
||||
|
||||
public void setRedirectUriTemplate(String redirectUriTemplate) {
|
||||
this.redirectUriTemplate = redirectUriTemplate;
|
||||
}
|
||||
|
||||
public Set<String> getScope() {
|
||||
return this.scope;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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::getRedirectUriTemplate, 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,6 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Registration;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
|
@ -51,19 +50,35 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
|
|||
public static Map<String, ClientRegistration> getClientRegistrations(
|
||||
OAuth2ClientProperties properties) {
|
||||
Map<String, ClientRegistration> clientRegistrations = new HashMap<>();
|
||||
properties.getRegistration().forEach((key, value) -> clientRegistrations.put(key,
|
||||
getClientRegistration(key, value, properties.getProvider())));
|
||||
properties.getRegistration().getLogin()
|
||||
.forEach((key, value) -> clientRegistrations.put(key,
|
||||
getLoginClientRegistration(key, value,
|
||||
properties.getProvider())));
|
||||
properties.getRegistration().getAuthorizationCode()
|
||||
.forEach((key, value) -> clientRegistrations.put(key,
|
||||
getAuthorizationCodeClientRegistration(key, value,
|
||||
properties.getProvider())));
|
||||
return clientRegistrations;
|
||||
}
|
||||
|
||||
private static ClientRegistration getClientRegistration(String registrationId,
|
||||
Registration properties, Map<String, Provider> providers) {
|
||||
private static ClientRegistration getAuthorizationCodeClientRegistration(
|
||||
String registrationId,
|
||||
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) {
|
||||
Builder builder = getBuilderFromIssuerIfPossible(registrationId,
|
||||
properties.getProvider(), providers);
|
||||
if (builder == null) {
|
||||
builder = getBuilder(registrationId, properties.getProvider(), providers);
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::getClientId).to(builder::clientId);
|
||||
map.from(properties::getClientSecret).to(builder::clientSecret);
|
||||
map.from(properties::getClientAuthenticationMethod)
|
||||
|
@ -71,10 +86,18 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
|
|||
.to(builder::clientAuthenticationMethod);
|
||||
map.from(properties::getAuthorizationGrantType).as(AuthorizationGrantType::new)
|
||||
.to(builder::authorizationGrantType);
|
||||
map.from(properties::getRedirectUriTemplate).to(builder::redirectUriTemplate);
|
||||
map.from(properties::getScope).as((scope) -> StringUtils.toStringArray(scope))
|
||||
.to(builder::scope);
|
||||
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::getRedirectUriTemplate).to(builder::redirectUriTemplate);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,8 @@ class OAuth2WebSecurityConfiguration {
|
|||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login();
|
||||
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login()
|
||||
.and().oauth2Client().authorizationCodeGrant();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -546,6 +546,15 @@
|
|||
"name": "spring.session.hazelcast.flush-mode",
|
||||
"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",
|
||||
"defaultValue": [
|
||||
|
|
|
@ -7,6 +7,10 @@ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingL
|
|||
org.springframework.context.ApplicationListener=\
|
||||
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
|
||||
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
|
||||
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
|
@ -28,8 +28,8 @@ import org.junit.Test;
|
|||
import org.junit.rules.ExpectedException;
|
||||
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.Registration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -72,17 +72,17 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
provider.setUserInfoAuthenticationMethod("form");
|
||||
provider.setUserNameAttribute("sub");
|
||||
provider.setJwkSetUri("http://example.com/jwk");
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("provider");
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
registration.setClientAuthenticationMethod("post");
|
||||
registration.setAuthorizationGrantType("authorization_code");
|
||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
||||
registration.setScope(Collections.singleton("scope"));
|
||||
registration.setClientName("clientName");
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setProvider("provider");
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
login.setClientAuthenticationMethod("post");
|
||||
login.setAuthorizationGrantType("authorization_code");
|
||||
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||
login.setScope(Collections.singleton("scope"));
|
||||
login.setClientName("clientName");
|
||||
properties.getRegistration().getLogin().put("registration", login);
|
||||
properties.getProvider().put("provider", provider);
|
||||
properties.getRegistration().put("registration", registration);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("registration");
|
||||
|
@ -114,11 +114,11 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
@Test
|
||||
public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("google");
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
properties.getRegistration().put("registration", registration);
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setProvider("google");
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
properties.getRegistration().getLogin().put("registration", login);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("registration");
|
||||
|
@ -149,16 +149,16 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
@Test
|
||||
public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("google");
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
registration.setClientAuthenticationMethod("post");
|
||||
registration.setAuthorizationGrantType("authorization_code");
|
||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
||||
registration.setScope(Collections.singleton("scope"));
|
||||
registration.setClientName("clientName");
|
||||
properties.getRegistration().put("registration", registration);
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setProvider("google");
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
login.setClientAuthenticationMethod("post");
|
||||
login.setAuthorizationGrantType("authorization_code");
|
||||
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||
login.setScope(Collections.singleton("scope"));
|
||||
login.setClientName("clientName");
|
||||
properties.getRegistration().getLogin().put("registration", login);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("registration");
|
||||
|
@ -192,9 +192,9 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
@Test
|
||||
public void getClientRegistrationsWhenUnknownProviderShouldThrowException() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("missing");
|
||||
properties.getRegistration().put("registration", registration);
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setProvider("missing");
|
||||
properties.getRegistration().getLogin().put("registration", login);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Unknown provider ID 'missing'");
|
||||
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties);
|
||||
|
@ -203,10 +203,10 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
@Test
|
||||
public void getClientRegistrationsWhenProviderNotSpecifiedShouldUseRegistrationId() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Registration registration = new Registration();
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
properties.getRegistration().put("google", registration);
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
properties.getRegistration().getLogin().put("google", login);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("google");
|
||||
|
@ -235,11 +235,47 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
assertThat(adapted.getClientName()).isEqualTo("Google");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientRegistrationsWhenAuhtorizationCodeClientShouldAdapt() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
OAuth2ClientProperties.AuthorizationCodeClientRegistration registration = new OAuth2ClientProperties.AuthorizationCodeClientRegistration();
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
registration.setRedirectUri("http://my-redirect-uri.com");
|
||||
properties.getRegistration().getAuthorizationCode().put("google", registration);
|
||||
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.getRedirectUriTemplate())
|
||||
.isEqualTo("http://my-redirect-uri.com");
|
||||
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.getScopes()).containsExactly("openid", "profile", "email");
|
||||
assertThat(adapted.getClientName()).isEqualTo("Google");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientRegistrationsWhenProviderNotSpecifiedAndUnknownProviderShouldThrowException() {
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Registration registration = new Registration();
|
||||
properties.getRegistration().put("missing", registration);
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
properties.getRegistration().getLogin().put("missing", login);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage(
|
||||
"Provider ID must be specified for client registration 'missing'");
|
||||
|
@ -249,20 +285,20 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
@Test
|
||||
public void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration()
|
||||
throws Exception {
|
||||
Registration registration = new Registration();
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
testOidcConfiguration(registration, "okta");
|
||||
LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
testOidcConfiguration(login, "okta");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration()
|
||||
throws Exception {
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("okta-oidc");
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
testOidcConfiguration(registration, "okta-oidc");
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new LoginClientRegistration();
|
||||
login.setProvider("okta-oidc");
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
testOidcConfiguration(login, "okta-oidc");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -273,13 +309,13 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
String issuer = this.server.url("").toString();
|
||||
String cleanIssuerPath = cleanIssuerPath(issuer);
|
||||
setupMockResponse(cleanIssuerPath);
|
||||
Registration registration = new Registration();
|
||||
registration.setProvider("okta-oidc");
|
||||
registration.setClientId("clientId");
|
||||
registration.setClientSecret("clientSecret");
|
||||
registration.setClientAuthenticationMethod("post");
|
||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
||||
registration.setScope(Collections.singleton("user"));
|
||||
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||
login.setProvider("okta-oidc");
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
login.setClientAuthenticationMethod("post");
|
||||
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||
login.setScope(Collections.singleton("user"));
|
||||
Provider provider = new Provider();
|
||||
provider.setIssuerUri(issuer);
|
||||
provider.setAuthorizationUri("http://example.com/auth");
|
||||
|
@ -289,7 +325,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
provider.setJwkSetUri("http://example.com/jwk");
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
properties.getProvider().put("okta-oidc", provider);
|
||||
properties.getRegistration().put("okta", registration);
|
||||
properties.getRegistration().getLogin().put("okta", login);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("okta");
|
||||
|
@ -313,8 +349,9 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
.isEqualTo("sub");
|
||||
}
|
||||
|
||||
private void testOidcConfiguration(Registration registration, String providerId)
|
||||
throws Exception {
|
||||
private void testOidcConfiguration(
|
||||
OAuth2ClientProperties.LoginClientRegistration registration,
|
||||
String providerId) throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
String issuer = this.server.url("").toString();
|
||||
|
@ -324,7 +361,7 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
|||
Provider provider = new Provider();
|
||||
provider.setIssuerUri(issuer);
|
||||
properties.getProvider().put(providerId, provider);
|
||||
properties.getRegistration().put("okta", registration);
|
||||
properties.getRegistration().getLogin().put("okta", registration);
|
||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||
.getClientRegistrations(properties);
|
||||
ClientRegistration adapted = registrations.get("okta");
|
||||
|
|
|
@ -20,6 +20,9 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.AuthorizationCodeClientRegistration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.LoginClientRegistration;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2ClientProperties}.
|
||||
*
|
||||
|
@ -34,11 +37,11 @@ public class OAuth2ClientPropertiesTests {
|
|||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void clientIdAbsentThrowsException() {
|
||||
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
|
||||
public void clientIdAbsentForLoginClientsThrowsException() {
|
||||
LoginClientRegistration registration = new LoginClientRegistration();
|
||||
registration.setClientSecret("secret");
|
||||
registration.setProvider("google");
|
||||
this.properties.getRegistration().put("foo", registration);
|
||||
this.properties.getRegistration().getLogin().put("foo", registration);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Client id must not be empty.");
|
||||
this.properties.validate();
|
||||
|
@ -46,10 +49,30 @@ public class OAuth2ClientPropertiesTests {
|
|||
|
||||
@Test
|
||||
public void clientSecretAbsentShouldNotThrowException() {
|
||||
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
|
||||
LoginClientRegistration registration = new LoginClientRegistration();
|
||||
registration.setClientId("foo");
|
||||
registration.setProvider("google");
|
||||
this.properties.getRegistration().put("foo", registration);
|
||||
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);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Client id must not be empty.");
|
||||
this.properties.validate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientSecretAbsentForAuthorizationCodeClientDoesNotThrowException() {
|
||||
AuthorizationCodeClientRegistration registration = new AuthorizationCodeClientRegistration();
|
||||
registration.setClientId("foo");
|
||||
registration.setProvider("google");
|
||||
this.properties.getRegistration().getAuthorizationCode().put("foo", registration);
|
||||
this.properties.validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public class ReactiveOAuth2ClientAutoConfigurationTests {
|
|||
.withConfiguration(
|
||||
AutoConfigurations.of(ReactiveOAuth2ClientAutoConfiguration.class));
|
||||
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login";
|
||||
|
||||
@Test
|
||||
public void autoConfigurationShouldImportConfigurations() {
|
||||
|
|
|
@ -33,7 +33,7 @@ public class ReactiveOAuth2ClientRegistrationRepositoryConfigurationTests {
|
|||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login";
|
||||
|
||||
@Test
|
||||
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
|
||||
|
|
|
@ -33,7 +33,7 @@ public class OAuth2ClientRegistrationRepositoryConfigurationTests {
|
|||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
|
||||
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login";
|
||||
|
||||
@Test
|
||||
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
|
||||
|
|
|
@ -39,7 +39,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
|
|||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
|
@ -68,7 +68,28 @@ public class OAuth2WebSecurityConfigurationTests {
|
|||
ClientRegistrationRepository expected = context
|
||||
.getBean(ClientRegistrationRepository.class);
|
||||
ClientRegistrationRepository actual = (ClientRegistrationRepository) ReflectionTestUtils
|
||||
.getField(getAuthCodeFilters(context).get(0),
|
||||
.getField(
|
||||
getFilters(context,
|
||||
OAuth2LoginAuthenticationFilter.class).get(0),
|
||||
"clientRegistrationRepository");
|
||||
assertThat(isEqual(expected.findByRegistrationId("first"),
|
||||
actual.findByRegistrationId("first"))).isTrue();
|
||||
assertThat(isEqual(expected.findByRegistrationId("second"),
|
||||
actual.findByRegistrationId("second"))).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityConfigurerConfiguresAuthorizationCode() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(ClientRegistrationRepositoryConfiguration.class,
|
||||
OAuth2WebSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
ClientRegistrationRepository expected = context
|
||||
.getBean(ClientRegistrationRepository.class);
|
||||
ClientRegistrationRepository actual = (ClientRegistrationRepository) ReflectionTestUtils
|
||||
.getField(getFilters(context,
|
||||
OAuth2AuthorizationCodeGrantFilter.class).get(0),
|
||||
"clientRegistrationRepository");
|
||||
assertThat(isEqual(expected.findByRegistrationId("first"),
|
||||
actual.findByRegistrationId("first"))).isTrue();
|
||||
|
@ -79,10 +100,14 @@ public class OAuth2WebSecurityConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void securityConfigurerBacksOffWhenClientRegistrationBeanAbsent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(TestConfig.class,
|
||||
OAuth2WebSecurityConfiguration.class)
|
||||
.run((context) -> assertThat(getAuthCodeFilters(context)).isEmpty());
|
||||
this.contextRunner.withUserConfiguration(TestConfig.class,
|
||||
OAuth2WebSecurityConfiguration.class).run((context) -> {
|
||||
assertThat(getFilters(context, OAuth2LoginAuthenticationFilter.class))
|
||||
.isEmpty();
|
||||
assertThat(
|
||||
getFilters(context, OAuth2AuthorizationCodeGrantFilter.class))
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -107,7 +132,11 @@ public class OAuth2WebSecurityConfigurationTests {
|
|||
public void securityConfigurerBacksOffWhenOtherWebSecurityAdapterPresent() {
|
||||
this.contextRunner.withUserConfiguration(TestWebSecurityConfigurerConfig.class,
|
||||
OAuth2WebSecurityConfiguration.class).run((context) -> {
|
||||
assertThat(getAuthCodeFilters(context)).isEmpty();
|
||||
assertThat(getFilters(context, OAuth2LoginAuthenticationFilter.class))
|
||||
.isEmpty();
|
||||
assertThat(
|
||||
getFilters(context, OAuth2AuthorizationCodeGrantFilter.class))
|
||||
.isEmpty();
|
||||
assertThat(context).getBean(OAuth2AuthorizedClientService.class)
|
||||
.isNotNull();
|
||||
});
|
||||
|
@ -137,19 +166,14 @@ public class OAuth2WebSecurityConfigurationTests {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Filter> getAuthCodeFilters(AssertableApplicationContext context) {
|
||||
private List<Filter> getFilters(AssertableApplicationContext context,
|
||||
Class<? extends Filter> filter) {
|
||||
FilterChainProxy filterChain = (FilterChainProxy) context
|
||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
|
||||
List<Filter> filters = (List<Filter>) ReflectionTestUtils
|
||||
.getField(filterChains.get(0), "filters");
|
||||
List<Filter> oauth2Filters = filters.stream()
|
||||
.filter((f) -> f instanceof OAuth2LoginAuthenticationFilter
|
||||
|| f instanceof OAuth2AuthorizationRequestRedirectFilter)
|
||||
.collect(Collectors.toList());
|
||||
return oauth2Filters.stream()
|
||||
.filter((f) -> f instanceof OAuth2LoginAuthenticationFilter)
|
||||
.collect(Collectors.toList());
|
||||
return filters.stream().filter(filter::isInstance).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean isEqual(ClientRegistration reg1, ClientRegistration reg2) {
|
||||
|
|
|
@ -3219,36 +3219,18 @@ https://oauth.net/2/[OAuth2] is a widely used authorization framework that is su
|
|||
Spring.
|
||||
|
||||
|
||||
|
||||
[[boot-features-security-oauth2-client]]
|
||||
==== Client
|
||||
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 Client. This configuration
|
||||
makes use of the properties under `OAuth2ClientProperties`. The same properties are applicable
|
||||
for both servlet and reactive applications.
|
||||
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`.
|
||||
|
||||
You can register multiple OAuth2 clients and providers under the
|
||||
`spring.security.oauth2.client` prefix, as shown in the following example:
|
||||
You can register multiple OAuth2/OpenID Connect providers under the `spring.security.oauth2.client.provider`
|
||||
prefix, as shown in the following example:
|
||||
|
||||
[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.token-uri=http://my-auth-server/oauth/token
|
||||
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=http://my-auth-server/userinfo
|
||||
|
@ -3257,6 +3239,48 @@ You can register multiple OAuth2 clients and providers under the
|
|||
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name
|
||||
----
|
||||
|
||||
For OpenID Connect providers that support https://openid.net/specs/openid-connect-discovery-1_0.html[OpenID Connect discovery],
|
||||
the configuration can be further simplified. The provider needs to be configured with an `issuer-uri` which is the
|
||||
URI that the it asserts as its Issuer Identifier. For example, if the
|
||||
`issuer-uri` provided is "https://example.com", then an `OpenID Provider Configuration Request`
|
||||
will be made to "https://example.com/.well-known/openid-configuration". The result is expected
|
||||
to be an `OpenID Provider Configuration Response`. The following example shows how an OpenID Connect
|
||||
Provider can be configured with the `issuer-uri`:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[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-template=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-template=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
|
||||
matching `/login/oauth2/code/*`. If you want to customize the `redirect-uri-template` to
|
||||
use a different pattern, you need to provide configuration to process that custom pattern.
|
||||
|
@ -3280,6 +3304,40 @@ 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]]
|
||||
===== OAuth2 client registration for common providers
|
||||
For common OAuth2 and OpenID providers, including Google, Github, Facebook, and Okta,
|
||||
we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta`,
|
||||
respectively).
|
||||
|
@ -3292,27 +3350,12 @@ In other words, the two configurations in the following example use the Google p
|
|||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.security.oauth2.client.registration.my-client.client-id=abcd
|
||||
spring.security.oauth2.client.registration.my-client.client-secret=password
|
||||
spring.security.oauth2.client.registration.my-client.provider=google
|
||||
spring.security.oauth2.client.registration.login.my-client.client-id=abcd
|
||||
spring.security.oauth2.client.registration.login.my-client.client-secret=password
|
||||
spring.security.oauth2.client.registration.login.my-client.provider=google
|
||||
|
||||
spring.security.oauth2.client.registration.google.client-id=abcd
|
||||
spring.security.oauth2.client.registration.google.client-secret=password
|
||||
----
|
||||
|
||||
For OpenID Connect providers that support https://openid.net/specs/openid-connect-discovery-1_0.html[OpenID Connect discovery],
|
||||
the configuration can be further simplified. The provider needs to be configured with an `issuer-uri` which is the
|
||||
URI that the it asserts as its Issuer Identifier. For example, if the
|
||||
`issuer-uri` provided is "https://example.com", then an `OpenID Provider Configuration Request`
|
||||
will be made to "https://example.com/.well-known/openid-configuration". The result is expected
|
||||
to be an `OpenID Provider Configuration Response`. The following example shows how an OpenID Connect
|
||||
Provider can be configured with the `issuer-uri`:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.security.oauth2.client.registration.oidc-provider.client-id=abcd
|
||||
spring.security.oauth2.client.registration.oidc-provider.client-secret=password
|
||||
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
|
||||
spring.security.oauth2.client.registration.login.google.client-id=abcd
|
||||
spring.security.oauth2.client.registration.login.google.client-secret=password
|
||||
----
|
||||
|
||||
|
||||
|
|
|
@ -3,23 +3,32 @@ spring:
|
|||
oauth2:
|
||||
client:
|
||||
registration:
|
||||
github-client-1:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github user
|
||||
provider: github
|
||||
scope: user
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
github-client-2:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github email
|
||||
provider: github
|
||||
scope: user:email
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
yahoo-oidc:
|
||||
client-id: ${YAHOO-CLIENT-ID}
|
||||
client-secret: ${YAHOO-CLIENT-SECRET}
|
||||
login:
|
||||
github-client-1:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github user
|
||||
provider: github
|
||||
scope: user
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
github-client-2:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github email
|
||||
provider: github
|
||||
scope: user:email
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
yahoo-oidc:
|
||||
client-id: a
|
||||
client-secret: b
|
||||
authorization_code:
|
||||
github-repos:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
scope: public_repo
|
||||
redirect-uri: "{baseUrl}/github-repos"
|
||||
provider: github
|
||||
client-name: GitHub Repositories
|
||||
provider:
|
||||
yahoo-oidc:
|
||||
issuer-uri: https://api.login.yahoo.com/
|
|
@ -53,7 +53,7 @@ public class SampleOAuth2ClientApplicationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldHaveBothOAuthClientsToChooseFrom() {
|
||||
public void loginShouldHaveBothOAuth2LoginClientsToChooseFrom() {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/login",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
@ -62,4 +62,12 @@ public class SampleOAuth2ClientApplicationTests {
|
|||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,23 +3,24 @@ spring:
|
|||
oauth2:
|
||||
client:
|
||||
registration:
|
||||
github-client-1:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github user
|
||||
provider: github
|
||||
scope: user
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
github-client-2:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github email
|
||||
provider: github
|
||||
scope: user:email
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
yahoo-oidc:
|
||||
client-id: ${YAHOO-CLIENT-ID}
|
||||
client-secret: ${YAHOO-CLIENT-SECRET}
|
||||
login:
|
||||
github-client-1:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github user
|
||||
provider: github
|
||||
scope: user
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
github-client-2:
|
||||
client-id: ${APP-CLIENT-ID}
|
||||
client-secret: ${APP-CLIENT-SECRET}
|
||||
client-name: Github email
|
||||
provider: github
|
||||
scope: user:email
|
||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||
yahoo-oidc:
|
||||
client-id: ${YAHOO-CLIENT-ID}
|
||||
client-secret: ${YAHOO-CLIENT-SECRET}
|
||||
provider:
|
||||
yahoo-oidc:
|
||||
issuer-uri: https://api.login.yahoo.com/
|
Loading…
Reference in New Issue