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;
|
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;
|
||||||
|
|
||||||
|
@ -37,29 +38,44 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
*/
|
*/
|
||||||
public class ClientsConfiguredCondition extends SpringBootCondition {
|
public class ClientsConfiguredCondition extends SpringBootCondition {
|
||||||
|
|
||||||
private static final Bindable<Map<String, OAuth2ClientProperties.Registration>> STRING_REGISTRATION_MAP = Bindable
|
private static final Bindable<Map<String, OAuth2ClientProperties.LoginClientRegistration>> STRING_LOGIN_REGISTRATION_MAP = Bindable
|
||||||
.mapOf(String.class, OAuth2ClientProperties.Registration.class);
|
.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
|
@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.Registration> registrations = getRegistrations(
|
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = getRegistrations(
|
||||||
context.getEnvironment());
|
context.getEnvironment());
|
||||||
if (!registrations.isEmpty()) {
|
if (!registrations.isEmpty()) {
|
||||||
return ConditionOutcome.match(message
|
return ConditionOutcome.match(message.foundExactly(
|
||||||
.foundExactly("registered clients " + registrations.values().stream()
|
"registered clients " + registrations.values().stream().map(
|
||||||
.map(OAuth2ClientProperties.Registration::getClientId)
|
OAuth2ClientProperties.BaseClientRegistration::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.Registration> getRegistrations(
|
private Map<String, OAuth2ClientProperties.BaseClientRegistration> getRegistrations(
|
||||||
Environment environment) {
|
Environment environment) {
|
||||||
return Binder.get(environment).bind("spring.security.oauth2.client.registration",
|
Map<String, OAuth2ClientProperties.BaseClientRegistration> registrations = new HashMap();
|
||||||
STRING_REGISTRATION_MAP).orElse(Collections.emptyMap());
|
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.
|
* OAuth client registrations.
|
||||||
*/
|
*/
|
||||||
private final Map<String, Registration> registration = new HashMap<>();
|
private final Registration registration = new Registration();
|
||||||
|
|
||||||
public Map<String, Provider> getProvider() {
|
public Map<String, Provider> getProvider() {
|
||||||
return this.provider;
|
return this.provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Registration> getRegistration() {
|
public Registration getRegistration() {
|
||||||
return this.registration;
|
return this.registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void validate() {
|
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())) {
|
if (!StringUtils.hasText(registration.getClientId())) {
|
||||||
throw new IllegalStateException("Client id must not be empty.");
|
throw new IllegalStateException("Client id must not be empty.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A single client registration.
|
|
||||||
*/
|
|
||||||
public static class 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
|
* 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,
|
* 'provider' property or used one of the commonly used providers (google, github,
|
||||||
|
@ -98,11 +172,6 @@ public class OAuth2ClientProperties {
|
||||||
*/
|
*/
|
||||||
private String authorizationGrantType;
|
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.
|
* Authorization scopes. May be left blank when using a pre-defined provider.
|
||||||
*/
|
*/
|
||||||
|
@ -153,14 +222,6 @@ public class OAuth2ClientProperties {
|
||||||
this.authorizationGrantType = authorizationGrantType;
|
this.authorizationGrantType = authorizationGrantType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRedirectUriTemplate() {
|
|
||||||
return this.redirectUriTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRedirectUriTemplate(String redirectUriTemplate) {
|
|
||||||
this.redirectUriTemplate = redirectUriTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getScope() {
|
public Set<String> getScope() {
|
||||||
return this.scope;
|
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 java.util.Map;
|
||||||
|
|
||||||
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.boot.context.properties.PropertyMapper;
|
import org.springframework.boot.context.properties.PropertyMapper;
|
||||||
import org.springframework.boot.convert.ApplicationConversionService;
|
import org.springframework.boot.convert.ApplicationConversionService;
|
||||||
import org.springframework.core.convert.ConversionException;
|
import org.springframework.core.convert.ConversionException;
|
||||||
|
@ -51,19 +50,35 @@ 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().forEach((key, value) -> clientRegistrations.put(key,
|
properties.getRegistration().getLogin()
|
||||||
getClientRegistration(key, value, properties.getProvider())));
|
.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;
|
return clientRegistrations;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClientRegistration getClientRegistration(String registrationId,
|
private static ClientRegistration getAuthorizationCodeClientRegistration(
|
||||||
Registration properties, Map<String, Provider> providers) {
|
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,
|
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)
|
||||||
|
@ -71,10 +86,18 @@ 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::getRedirectUriTemplate).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::getRedirectUriTemplate).to(builder::redirectUriTemplate);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,8 @@ class OAuth2WebSecurityConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
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",
|
"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,6 +7,10 @@ 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
|
||||||
|
|
|
@ -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.junit.rules.ExpectedException;
|
||||||
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;
|
||||||
|
@ -72,17 +72,17 @@ 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");
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setProvider("provider");
|
login.setProvider("provider");
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
registration.setClientAuthenticationMethod("post");
|
login.setClientAuthenticationMethod("post");
|
||||||
registration.setAuthorizationGrantType("authorization_code");
|
login.setAuthorizationGrantType("authorization_code");
|
||||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||||
registration.setScope(Collections.singleton("scope"));
|
login.setScope(Collections.singleton("scope"));
|
||||||
registration.setClientName("clientName");
|
login.setClientName("clientName");
|
||||||
|
properties.getRegistration().getLogin().put("registration", login);
|
||||||
properties.getProvider().put("provider", provider);
|
properties.getProvider().put("provider", provider);
|
||||||
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");
|
||||||
|
@ -114,11 +114,11 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
@Test
|
@Test
|
||||||
public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt() {
|
public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt() {
|
||||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setProvider("google");
|
login.setProvider("google");
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
properties.getRegistration().put("registration", registration);
|
properties.getRegistration().getLogin().put("registration", login);
|
||||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||||
.getClientRegistrations(properties);
|
.getClientRegistrations(properties);
|
||||||
ClientRegistration adapted = registrations.get("registration");
|
ClientRegistration adapted = registrations.get("registration");
|
||||||
|
@ -149,16 +149,16 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
@Test
|
@Test
|
||||||
public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt() {
|
public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt() {
|
||||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setProvider("google");
|
login.setProvider("google");
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
registration.setClientAuthenticationMethod("post");
|
login.setClientAuthenticationMethod("post");
|
||||||
registration.setAuthorizationGrantType("authorization_code");
|
login.setAuthorizationGrantType("authorization_code");
|
||||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||||
registration.setScope(Collections.singleton("scope"));
|
login.setScope(Collections.singleton("scope"));
|
||||||
registration.setClientName("clientName");
|
login.setClientName("clientName");
|
||||||
properties.getRegistration().put("registration", registration);
|
properties.getRegistration().getLogin().put("registration", login);
|
||||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||||
.getClientRegistrations(properties);
|
.getClientRegistrations(properties);
|
||||||
ClientRegistration adapted = registrations.get("registration");
|
ClientRegistration adapted = registrations.get("registration");
|
||||||
|
@ -192,9 +192,9 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
@Test
|
@Test
|
||||||
public void getClientRegistrationsWhenUnknownProviderShouldThrowException() {
|
public void getClientRegistrationsWhenUnknownProviderShouldThrowException() {
|
||||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setProvider("missing");
|
login.setProvider("missing");
|
||||||
properties.getRegistration().put("registration", registration);
|
properties.getRegistration().getLogin().put("registration", login);
|
||||||
this.thrown.expect(IllegalStateException.class);
|
this.thrown.expect(IllegalStateException.class);
|
||||||
this.thrown.expectMessage("Unknown provider ID 'missing'");
|
this.thrown.expectMessage("Unknown provider ID 'missing'");
|
||||||
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties);
|
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties);
|
||||||
|
@ -203,10 +203,10 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
@Test
|
@Test
|
||||||
public void getClientRegistrationsWhenProviderNotSpecifiedShouldUseRegistrationId() {
|
public void getClientRegistrationsWhenProviderNotSpecifiedShouldUseRegistrationId() {
|
||||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
properties.getRegistration().put("google", registration);
|
properties.getRegistration().getLogin().put("google", login);
|
||||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||||
.getClientRegistrations(properties);
|
.getClientRegistrations(properties);
|
||||||
ClientRegistration adapted = registrations.get("google");
|
ClientRegistration adapted = registrations.get("google");
|
||||||
|
@ -235,11 +235,47 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
assertThat(adapted.getClientName()).isEqualTo("Google");
|
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
|
@Test
|
||||||
public void getClientRegistrationsWhenProviderNotSpecifiedAndUnknownProviderShouldThrowException() {
|
public void getClientRegistrationsWhenProviderNotSpecifiedAndUnknownProviderShouldThrowException() {
|
||||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
properties.getRegistration().put("missing", registration);
|
properties.getRegistration().getLogin().put("missing", login);
|
||||||
this.thrown.expect(IllegalStateException.class);
|
this.thrown.expect(IllegalStateException.class);
|
||||||
this.thrown.expectMessage(
|
this.thrown.expectMessage(
|
||||||
"Provider ID must be specified for client registration 'missing'");
|
"Provider ID must be specified for client registration 'missing'");
|
||||||
|
@ -249,20 +285,20 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
@Test
|
@Test
|
||||||
public void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration()
|
public void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Registration registration = new Registration();
|
LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
testOidcConfiguration(registration, "okta");
|
testOidcConfiguration(login, "okta");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration()
|
public void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new LoginClientRegistration();
|
||||||
registration.setProvider("okta-oidc");
|
login.setProvider("okta-oidc");
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
testOidcConfiguration(registration, "okta-oidc");
|
testOidcConfiguration(login, "okta-oidc");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -273,13 +309,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);
|
||||||
Registration registration = new Registration();
|
OAuth2ClientProperties.LoginClientRegistration login = new OAuth2ClientProperties.LoginClientRegistration();
|
||||||
registration.setProvider("okta-oidc");
|
login.setProvider("okta-oidc");
|
||||||
registration.setClientId("clientId");
|
login.setClientId("clientId");
|
||||||
registration.setClientSecret("clientSecret");
|
login.setClientSecret("clientSecret");
|
||||||
registration.setClientAuthenticationMethod("post");
|
login.setClientAuthenticationMethod("post");
|
||||||
registration.setRedirectUriTemplate("http://example.com/redirect");
|
login.setRedirectUriTemplate("http://example.com/redirect");
|
||||||
registration.setScope(Collections.singleton("user"));
|
login.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");
|
||||||
|
@ -289,7 +325,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().put("okta", registration);
|
properties.getRegistration().getLogin().put("okta", login);
|
||||||
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
|
||||||
.getClientRegistrations(properties);
|
.getClientRegistrations(properties);
|
||||||
ClientRegistration adapted = registrations.get("okta");
|
ClientRegistration adapted = registrations.get("okta");
|
||||||
|
@ -313,8 +349,9 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||||
.isEqualTo("sub");
|
.isEqualTo("sub");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testOidcConfiguration(Registration registration, String providerId)
|
private void testOidcConfiguration(
|
||||||
throws Exception {
|
OAuth2ClientProperties.LoginClientRegistration registration,
|
||||||
|
String providerId) throws Exception {
|
||||||
this.server = new MockWebServer();
|
this.server = new MockWebServer();
|
||||||
this.server.start();
|
this.server.start();
|
||||||
String issuer = this.server.url("").toString();
|
String issuer = this.server.url("").toString();
|
||||||
|
@ -324,7 +361,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().put("okta", registration);
|
properties.getRegistration().getLogin().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");
|
||||||
|
|
|
@ -20,6 +20,9 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
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}.
|
* Tests for {@link OAuth2ClientProperties}.
|
||||||
*
|
*
|
||||||
|
@ -34,11 +37,11 @@ public class OAuth2ClientPropertiesTests {
|
||||||
public ExpectedException thrown = ExpectedException.none();
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clientIdAbsentThrowsException() {
|
public void clientIdAbsentForLoginClientsThrowsException() {
|
||||||
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
|
LoginClientRegistration registration = new LoginClientRegistration();
|
||||||
registration.setClientSecret("secret");
|
registration.setClientSecret("secret");
|
||||||
registration.setProvider("google");
|
registration.setProvider("google");
|
||||||
this.properties.getRegistration().put("foo", registration);
|
this.properties.getRegistration().getLogin().put("foo", registration);
|
||||||
this.thrown.expect(IllegalStateException.class);
|
this.thrown.expect(IllegalStateException.class);
|
||||||
this.thrown.expectMessage("Client id must not be empty.");
|
this.thrown.expectMessage("Client id must not be empty.");
|
||||||
this.properties.validate();
|
this.properties.validate();
|
||||||
|
@ -46,10 +49,30 @@ public class OAuth2ClientPropertiesTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clientSecretAbsentShouldNotThrowException() {
|
public void clientSecretAbsentShouldNotThrowException() {
|
||||||
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
|
LoginClientRegistration registration = new LoginClientRegistration();
|
||||||
registration.setClientId("foo");
|
registration.setClientId("foo");
|
||||||
registration.setProvider("google");
|
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();
|
this.properties.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,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";
|
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void autoConfigurationShouldImportConfigurations() {
|
public void autoConfigurationShouldImportConfigurations() {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class ReactiveOAuth2ClientRegistrationRepositoryConfigurationTests {
|
||||||
|
|
||||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
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
|
@Test
|
||||||
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
|
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
|
||||||
|
|
|
@ -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";
|
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration.login";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() {
|
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.ClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
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.OAuth2AuthorizedClientRepository;
|
||||||
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
|
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
@ -68,7 +68,28 @@ public class OAuth2WebSecurityConfigurationTests {
|
||||||
ClientRegistrationRepository expected = context
|
ClientRegistrationRepository expected = context
|
||||||
.getBean(ClientRegistrationRepository.class);
|
.getBean(ClientRegistrationRepository.class);
|
||||||
ClientRegistrationRepository actual = (ClientRegistrationRepository) ReflectionTestUtils
|
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");
|
"clientRegistrationRepository");
|
||||||
assertThat(isEqual(expected.findByRegistrationId("first"),
|
assertThat(isEqual(expected.findByRegistrationId("first"),
|
||||||
actual.findByRegistrationId("first"))).isTrue();
|
actual.findByRegistrationId("first"))).isTrue();
|
||||||
|
@ -79,10 +100,14 @@ public class OAuth2WebSecurityConfigurationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void securityConfigurerBacksOffWhenClientRegistrationBeanAbsent() {
|
public void securityConfigurerBacksOffWhenClientRegistrationBeanAbsent() {
|
||||||
this.contextRunner
|
this.contextRunner.withUserConfiguration(TestConfig.class,
|
||||||
.withUserConfiguration(TestConfig.class,
|
OAuth2WebSecurityConfiguration.class).run((context) -> {
|
||||||
OAuth2WebSecurityConfiguration.class)
|
assertThat(getFilters(context, OAuth2LoginAuthenticationFilter.class))
|
||||||
.run((context) -> assertThat(getAuthCodeFilters(context)).isEmpty());
|
.isEmpty();
|
||||||
|
assertThat(
|
||||||
|
getFilters(context, OAuth2AuthorizationCodeGrantFilter.class))
|
||||||
|
.isEmpty();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -107,7 +132,11 @@ public class OAuth2WebSecurityConfigurationTests {
|
||||||
public void securityConfigurerBacksOffWhenOtherWebSecurityAdapterPresent() {
|
public void securityConfigurerBacksOffWhenOtherWebSecurityAdapterPresent() {
|
||||||
this.contextRunner.withUserConfiguration(TestWebSecurityConfigurerConfig.class,
|
this.contextRunner.withUserConfiguration(TestWebSecurityConfigurerConfig.class,
|
||||||
OAuth2WebSecurityConfiguration.class).run((context) -> {
|
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)
|
assertThat(context).getBean(OAuth2AuthorizedClientService.class)
|
||||||
.isNotNull();
|
.isNotNull();
|
||||||
});
|
});
|
||||||
|
@ -137,19 +166,14 @@ public class OAuth2WebSecurityConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private List<Filter> getAuthCodeFilters(AssertableApplicationContext context) {
|
private List<Filter> getFilters(AssertableApplicationContext context,
|
||||||
|
Class<? extends Filter> filter) {
|
||||||
FilterChainProxy filterChain = (FilterChainProxy) context
|
FilterChainProxy filterChain = (FilterChainProxy) context
|
||||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||||
List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
|
List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
|
||||||
List<Filter> filters = (List<Filter>) ReflectionTestUtils
|
List<Filter> filters = (List<Filter>) ReflectionTestUtils
|
||||||
.getField(filterChains.get(0), "filters");
|
.getField(filterChains.get(0), "filters");
|
||||||
List<Filter> oauth2Filters = filters.stream()
|
return filters.stream().filter(filter::isInstance).collect(Collectors.toList());
|
||||||
.filter((f) -> f instanceof OAuth2LoginAuthenticationFilter
|
|
||||||
|| f instanceof OAuth2AuthorizationRequestRedirectFilter)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
return oauth2Filters.stream()
|
|
||||||
.filter((f) -> f instanceof OAuth2LoginAuthenticationFilter)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEqual(ClientRegistration reg1, ClientRegistration reg2) {
|
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.
|
Spring.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[boot-features-security-oauth2-client]]
|
[[boot-features-security-oauth2-client]]
|
||||||
==== 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 Client. 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`. The same properties are applicable
|
makes use of the properties under `OAuth2ClientProperties`.
|
||||||
for both servlet and reactive applications.
|
|
||||||
|
|
||||||
You can register multiple OAuth2 clients and providers under the
|
You can register multiple OAuth2/OpenID Connect providers under the `spring.security.oauth2.client.provider`
|
||||||
`spring.security.oauth2.client` prefix, as shown in the following example:
|
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
|
||||||
|
@ -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
|
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
|
By default, Spring Security's `OAuth2LoginAuthenticationFilter` only processes URLs
|
||||||
matching `/login/oauth2/code/*`. If you want to customize the `redirect-uri-template` to
|
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.
|
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,
|
For common OAuth2 and OpenID providers, including Google, Github, Facebook, and Okta,
|
||||||
we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta`,
|
we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta`,
|
||||||
respectively).
|
respectively).
|
||||||
|
@ -3292,27 +3350,12 @@ In other words, the two configurations in the following example use the Google p
|
||||||
|
|
||||||
[source,properties,indent=0]
|
[source,properties,indent=0]
|
||||||
----
|
----
|
||||||
spring.security.oauth2.client.registration.my-client.client-id=abcd
|
spring.security.oauth2.client.registration.login.my-client.client-id=abcd
|
||||||
spring.security.oauth2.client.registration.my-client.client-secret=password
|
spring.security.oauth2.client.registration.login.my-client.client-secret=password
|
||||||
spring.security.oauth2.client.registration.my-client.provider=google
|
spring.security.oauth2.client.registration.login.my-client.provider=google
|
||||||
|
|
||||||
spring.security.oauth2.client.registration.google.client-id=abcd
|
spring.security.oauth2.client.registration.login.google.client-id=abcd
|
||||||
spring.security.oauth2.client.registration.google.client-secret=password
|
spring.security.oauth2.client.registration.login.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/
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,32 @@ spring:
|
||||||
oauth2:
|
oauth2:
|
||||||
client:
|
client:
|
||||||
registration:
|
registration:
|
||||||
github-client-1:
|
login:
|
||||||
client-id: ${APP-CLIENT-ID}
|
github-client-1:
|
||||||
client-secret: ${APP-CLIENT-SECRET}
|
client-id: ${APP-CLIENT-ID}
|
||||||
client-name: Github user
|
client-secret: ${APP-CLIENT-SECRET}
|
||||||
provider: github
|
client-name: Github user
|
||||||
scope: user
|
provider: github
|
||||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
scope: user
|
||||||
github-client-2:
|
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||||
client-id: ${APP-CLIENT-ID}
|
github-client-2:
|
||||||
client-secret: ${APP-CLIENT-SECRET}
|
client-id: ${APP-CLIENT-ID}
|
||||||
client-name: Github email
|
client-secret: ${APP-CLIENT-SECRET}
|
||||||
provider: github
|
client-name: Github email
|
||||||
scope: user:email
|
provider: github
|
||||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
scope: user:email
|
||||||
yahoo-oidc:
|
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||||
client-id: ${YAHOO-CLIENT-ID}
|
yahoo-oidc:
|
||||||
client-secret: ${YAHOO-CLIENT-SECRET}
|
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:
|
provider:
|
||||||
yahoo-oidc:
|
yahoo-oidc:
|
||||||
issuer-uri: https://api.login.yahoo.com/
|
issuer-uri: https://api.login.yahoo.com/
|
|
@ -53,7 +53,7 @@ public class SampleOAuth2ClientApplicationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginShouldHaveBothOAuthClientsToChooseFrom() {
|
public void loginShouldHaveBothOAuth2LoginClientsToChooseFrom() {
|
||||||
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);
|
||||||
|
@ -62,4 +62,12 @@ public class SampleOAuth2ClientApplicationTests {
|
||||||
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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,24 @@ spring:
|
||||||
oauth2:
|
oauth2:
|
||||||
client:
|
client:
|
||||||
registration:
|
registration:
|
||||||
github-client-1:
|
login:
|
||||||
client-id: ${APP-CLIENT-ID}
|
github-client-1:
|
||||||
client-secret: ${APP-CLIENT-SECRET}
|
client-id: ${APP-CLIENT-ID}
|
||||||
client-name: Github user
|
client-secret: ${APP-CLIENT-SECRET}
|
||||||
provider: github
|
client-name: Github user
|
||||||
scope: user
|
provider: github
|
||||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
scope: user
|
||||||
github-client-2:
|
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||||
client-id: ${APP-CLIENT-ID}
|
github-client-2:
|
||||||
client-secret: ${APP-CLIENT-SECRET}
|
client-id: ${APP-CLIENT-ID}
|
||||||
client-name: Github email
|
client-secret: ${APP-CLIENT-SECRET}
|
||||||
provider: github
|
client-name: Github email
|
||||||
scope: user:email
|
provider: github
|
||||||
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
scope: user:email
|
||||||
yahoo-oidc:
|
redirect-uri-template: http://localhost:8080/login/oauth2/code/github
|
||||||
client-id: ${YAHOO-CLIENT-ID}
|
yahoo-oidc:
|
||||||
client-secret: ${YAHOO-CLIENT-SECRET}
|
client-id: ${YAHOO-CLIENT-ID}
|
||||||
|
client-secret: ${YAHOO-CLIENT-SECRET}
|
||||||
provider:
|
provider:
|
||||||
yahoo-oidc:
|
yahoo-oidc:
|
||||||
issuer-uri: https://api.login.yahoo.com/
|
issuer-uri: https://api.login.yahoo.com/
|
Loading…
Reference in New Issue