Commit dbe1d960 authored by Madhura Bhave's avatar Madhura Bhave

Add auto-config for spring-security-oauth2-client

Closes gh-10497
parent 494b79c4
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
......@@ -532,6 +533,16 @@
<artifactId>spring-security-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt-jose</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
......
/*
* Copyright 2012-2017 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;
/**
* OAuth 2.0 authorization grant types supported by Spring Boot.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
public enum AuthorizationGrantType {
/**
* An {@code "authorization_code"} grant type.
*/
AUTHORIZATION_CODE(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
private final org.springframework.security.oauth2.core.AuthorizationGrantType type;
AuthorizationGrantType(
org.springframework.security.oauth2.core.AuthorizationGrantType type) {
this.type = type;
}
org.springframework.security.oauth2.core.AuthorizationGrantType getType() {
return this.type;
}
}
/*
* Copyright 2012-2017 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;
/**
* OAuth 2.0 client authentication methods supported by Spring Boot.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
* @see org.springframework.security.oauth2.core.ClientAuthenticationMethod
*/
public enum ClientAuthenticationMethod {
/**
* HTTP BASIC client authentication.
*/
BASIC(org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC),
/**
* HTTP POST client authentication.
*/
POST(org.springframework.security.oauth2.core.ClientAuthenticationMethod.POST);
private final org.springframework.security.oauth2.core.ClientAuthenticationMethod method;
ClientAuthenticationMethod(
org.springframework.security.oauth2.core.ClientAuthenticationMethod method) {
this.method = method;
}
org.springframework.security.oauth2.core.ClientAuthenticationMethod getMethod() {
return this.method;
}
}
/*
* Copyright 2012-2017 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 org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
/**
* Common OAuth2 Providers that can be used to create
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
* builders} pre-configured with sensible defaults.
*
* @author Phillip Webb
* @since 2.0.0
*/
public enum CommonOAuth2Provider {
GOOGLE {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.scope("openid", "profile", "email", "address", "phone");
builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
builder.clientName("Google");
return builder;
}
},
GITHUB {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.scope("user");
builder.authorizationUri("https://github.com/login/oauth/authorize");
builder.tokenUri("https://github.com/login/oauth/access_token");
builder.userInfoUri("https://api.github.com/user");
builder.userNameAttributeName("name");
builder.clientName("GitHub");
return builder;
}
},
FACEBOOK {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.POST, DEFAULT_REDIRECT_URL);
builder.scope("public_profile", "email");
builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth");
builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token");
builder.userInfoUri("https://graph.facebook.com/me");
builder.userNameAttributeName("name");
builder.clientName("Facebook");
return builder;
}
},
OKTA {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId,
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
builder.scope("openid", "profile", "email", "address", "phone");
builder.clientName("Okta");
return builder;
}
};
private static final String DEFAULT_REDIRECT_URL = "{scheme}://{serverName}:{serverPort}{contextPath}/oauth2/authorize/code/{clientAlias}";
protected final ClientRegistration.Builder getBuilder(String registrationId,
ClientAuthenticationMethod method, String redirectUri) {
ClientRegistration.Builder builder = new ClientRegistration.Builder(registrationId);
builder.clientAuthenticationMethod(method);
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
builder.redirectUri(redirectUri);
return builder;
}
/**
* Create a new
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
* ClientRegistration.Builder} pre-initialized with the provider settings.
* @param registrationId the registration-id used with the new builder
* @return a builder instance
*/
public abstract ClientRegistration.Builder getBuilder(String registrationId);
}
/*
* Copyright 2012-2017 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 org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OAuth client support.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
@Configuration
@AutoConfigureBefore(SecurityAutoConfiguration.class)
@ConditionalOnClass({ EnableWebSecurity.class, ClientRegistration.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Import({ OAuth2ClientRegistrationRepositoryConfiguration.class,
OAuth2WebSecurityConfiguration.class })
public class OAuth2ClientAutoConfiguration {
}
/*
* Copyright 2012-2017 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 java.util.Set;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
/**
* OAuth 2.0 client properties.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
@ConfigurationProperties(prefix = "spring.security.oauth2.client")
public class OAuth2ClientProperties {
/**
* OAuth provider details.
*/
private Map<String, Provider> provider = new HashMap<>();
/**
* Client registrations.
*/
private Map<String, Registration> registration = new HashMap<>();
public Map<String, Provider> getProvider() {
return this.provider;
}
public Map<String, Registration> getRegistration() {
return this.registration;
}
@PostConstruct
public void validate() {
this.getRegistration().values().forEach(this::validateRegistration);
}
private void validateRegistration(Registration registration) {
if (!StringUtils.hasText(registration.getClientId())) {
throw new IllegalStateException("Client id must not be empty.");
}
if (!StringUtils.hasText(registration.getClientSecret())) {
throw new IllegalStateException("Client secret must not be empty.");
}
if (!StringUtils.hasText(registration.getProvider())) {
throw new IllegalStateException("Provider must not be empty.");
}
}
/**
* A single client registration.
*/
public static class Registration {
/**
* 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,
* facebook, okta).
*/
private String provider;
/**
* Client ID for the registration.
*/
private String clientId;
/**
* Client secret of the registration.
*/
private String clientSecret;
/**
* The client authentication method. May be left bank then using a pre-defined
* provider.
*/
private ClientAuthenticationMethod clientAuthenticationMethod;
/**
* The authorization grant type. May be left bank then using a pre-defined
* provider.
*/
private AuthorizationGrantType authorizationGrantType;
/**
* The redirect URI. May be left bank then using a pre-defined provider.
*/
private String redirectUri;
/**
* The authorization scopes. May be left bank then using a pre-defined provider.
*/
private Set<String> scope;
/**
* The client name. May be left bank then using a pre-defined provider.
*/
private String clientName;
public String getProvider() {
return this.provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public String getClientId() {
return this.clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return this.clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public ClientAuthenticationMethod getClientAuthenticationMethod() {
return this.clientAuthenticationMethod;
}
public void setClientAuthenticationMethod(
ClientAuthenticationMethod clientAuthenticationMethod) {
this.clientAuthenticationMethod = clientAuthenticationMethod;
}
public AuthorizationGrantType getAuthorizationGrantType() {
return this.authorizationGrantType;
}
public void setAuthorizationGrantType(
AuthorizationGrantType authorizationGrantType) {
this.authorizationGrantType = authorizationGrantType;
}
public String getRedirectUri() {
return this.redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public Set<String> getScope() {
return this.scope;
}
public void setScope(Set<String> scope) {
this.scope = scope;
}
public String getClientName() {
return this.clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
}
public static class Provider {
/**
* The authorization URI for the provider.
*/
private String authorizationUri;
/**
* The token URI for the provider.
*/
private String tokenUri;
/**
* The user info URI for the provider.
*/
private String userInfoUri;
/**
* The name of the attribute that will be used to extract the username from the
* call to 'userInfoUri'.
*/
private String userNameAttribute;
/**
* The JWK set URI for the provider.
*/
private String jwkSetUri;
public String getAuthorizationUri() {
return this.authorizationUri;
}
public void setAuthorizationUri(String authorizationUri) {
this.authorizationUri = authorizationUri;
}
public String getTokenUri() {
return this.tokenUri;
}
public void setTokenUri(String tokenUri) {
this.tokenUri = tokenUri;
}
public String getUserInfoUri() {
return this.userInfoUri;
}
public void setUserInfoUri(String userInfoUri) {
this.userInfoUri = userInfoUri;
}
public String getUserNameAttribute() {
return this.userNameAttribute;
}
public void setUserNameAttribute(String userNameAttribute) {
this.userNameAttribute = userNameAttribute;
}
public String getJwkSetUri() {
return this.jwkSetUri;
}
public void setJwkSetUri(String jwkSetUri) {
this.jwkSetUri = jwkSetUri;
}
}
}
/*
* Copyright 2012-2017 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 java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
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.bind.convert.BinderConversionService;
import org.springframework.core.convert.ConversionException;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
/**
* Adapter class to convert {@link OAuth2ClientProperties} to a
* {@link ClientRegistration}.
*
* @author Phillip Webb
* @since 2.0.0
*/
final class OAuth2ClientPropertiesRegistrationAdapter {
private 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()));
});
return clientRegistrations;
}
private static ClientRegistration getClientRegistration(String registrationId, Registration properties,
Map<String, Provider> providers) {
Builder builder = getBuilder(registrationId, properties.getProvider(),
providers);
copyIfNotNull(properties::getClientId, builder::clientId);
copyIfNotNull(properties::getClientSecret, builder::clientSecret);
copyIfNotNull(() -> properties.getClientAuthenticationMethod(),
builder::clientAuthenticationMethod,
ClientAuthenticationMethod::getMethod);
copyIfNotNull(() -> properties.getAuthorizationGrantType(),
builder::authorizationGrantType, AuthorizationGrantType::getType);
copyIfNotNull(properties::getRedirectUri, builder::redirectUri);
copyIfNotNull(properties::getScope, builder::scope,
(scope) -> scope.toArray(new String[scope.size()]));
copyIfNotNull(properties::getClientName, builder::clientName);
return builder.build();
}
private static Builder getBuilder(String registrationId, String providerId,
Map<String, Provider> providers) {
CommonOAuth2Provider provider = getCommonProvider(providerId);
if (provider == null && !providers.containsKey(providerId)) {
throw new IllegalStateException("Unknown provider ID '" + providerId + "'");
}
Builder builder = (provider != null ? provider.getBuilder(registrationId) : new Builder(registrationId));
if (providers.containsKey(providerId)) {
return getBuilder(builder, providers.get(providerId));
}
return builder;
}
private static Builder getBuilder(Builder builder, Provider provider) {
copyIfNotNull(provider::getAuthorizationUri, builder::authorizationUri);
copyIfNotNull(provider::getTokenUri, builder::tokenUri);
copyIfNotNull(provider::getUserInfoUri, builder::userInfoUri);
copyIfNotNull(provider::getJwkSetUri, builder::jwkSetUri);
return builder;
}
private static CommonOAuth2Provider getCommonProvider(String providerId) {
try {
return new BinderConversionService(null).convert(providerId,
CommonOAuth2Provider.class);
}
catch (ConversionException ex) {
return null;
}
}
private static <T> void copyIfNotNull(Supplier<T> supplier, Consumer<T> consumer) {
copyIfNotNull(supplier, consumer, Function.identity());
}
private static <S, C> void copyIfNotNull(Supplier<S> supplier, Consumer<C> consumer,
Function<S, C> converter) {
S value = supplier.get();
if (value != null) {
consumer.accept(converter.apply(value));
}
}
}
/*
* Copyright 2012-2017 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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Registration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
/**
* {@link Configuration} used to map {@link OAuth2ClientProperties} to client
* registrations.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
@Configuration
@EnableConfigurationProperties(OAuth2ClientProperties.class)
@Conditional(OAuth2ClientRegistrationRepositoryConfiguration.ClientsConfiguredCondition.class)
class OAuth2ClientRegistrationRepositoryConfiguration {
private final OAuth2ClientProperties properties;
OAuth2ClientRegistrationRepositoryConfiguration(
OAuth2ClientProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(ClientRegistrationRepository.class)
public InMemoryClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> registrations = new ArrayList<>(
OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(this.properties).values());
return new InMemoryClientRegistrationRepository(registrations);
}
/**
* Condition that matches if any {@code spring.security.oauth2.client.registration}
* properties are defined.
*/
static class ClientsConfiguredCondition extends SpringBootCondition {
private static final Bindable<Map<String, Registration>> BINDABLE_REGISTRATION = Bindable
.mapOf(String.class, OAuth2ClientProperties.Registration.class);
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth2 Clients Configured Condition");
Map<String, Registration> registrations = this
.getRegistrations(context.getEnvironment());
if (!registrations.isEmpty()) {
return ConditionOutcome.match(message.foundExactly(
"registered clients " + registrations.values().stream()
.map(OAuth2ClientProperties.Registration::getClientId)
.collect(Collectors.joining(", "))));
}
return ConditionOutcome.noMatch(message.notAvailable("registered clients"));
}
private Map<String, Registration> getRegistrations(Environment environment) {
return Binder.get(environment)
.bind("spring.security.oauth2.client.registration",
BINDABLE_REGISTRATION)
.orElse(Collections.emptyMap());
}
}
}
/*
* Copyright 2012-2017 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 org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
/**
* {@link WebSecurityConfigurerAdapter} to add OAuth client support.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
@Configuration
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnBean(ClientRegistrationRepository.class)
class OAuth2WebSecurityConfiguration {
@Configuration
private static class OAuth2WebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private final ClientRegistrationRepository clientRegistrationRepository;
OAuth2WebSecurityConfigurationAdapter(
ClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated().and()
.oauth2Login()
.clients(this.clientRegistrationRepository);
}
}
}
......@@ -102,6 +102,7 @@ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
......
/*
* Copyright 2012-2017 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 org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AuthorizationGrantType}.
*
* @author Phillip Webb
*/
public class AuthorizationGrantTypeTests {
@Test
public void getTypeShouldGetSpringSecurityVariant() throws Exception {
assertThat(AuthorizationGrantType.AUTHORIZATION_CODE.getType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
}
}
/*
* Copyright 2012-2017 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 org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ClientAuthenticationMethod}.
*
* @author Phillip Webb
*/
public class ClientAuthenticationMethodTests {
@Test
public void getMethodShouldGetSpringSecurityVariant() throws Exception {
assertThat(ClientAuthenticationMethod.BASIC.getMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC);
assertThat(ClientAuthenticationMethod.POST.getMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.POST);
}
}
/*
* Copyright 2012-2017 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 org.junit.Test;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CommonOAuth2Provider}.
*
* @author Phillip Webb
*/
public class CommonOAuth2ProviderTests {
private static final String DEFAULT_REDIRECT_URL = "{scheme}://{serverName}:{serverPort}{contextPath}/oauth2/authorize/code/{clientAlias}";
@Test
public void getBuilderWhenGoogleShouldHaveGoogleSettings() throws Exception {
ClientRegistration registration = build(CommonOAuth2Provider.GOOGLE);
ProviderDetails providerDetails = registration.getProviderDetails();
assertThat(providerDetails.getAuthorizationUri())
.isEqualTo("https://accounts.google.com/o/oauth2/v2/auth");
assertThat(providerDetails.getTokenUri())
.isEqualTo("https://www.googleapis.com/oauth2/v4/token");
assertThat(providerDetails.getUserInfoEndpoint().getUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/userinfo");
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName())
.isEqualTo(null);
assertThat(providerDetails.getJwkSetUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/certs");
assertThat(registration.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.BASIC);
assertThat(registration.getAuthorizationGrantType())
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);
assertThat(registration.getScope()).containsOnly("openid", "profile", "email",
"address", "phone");
assertThat(registration.getClientName()).isEqualTo("Google");
assertThat(registration.getRegistrationId()).isEqualTo("123");
}
@Test
public void getBuilderWhenGitHubShouldHaveGitHubSettings() throws Exception {
ClientRegistration registration = build(CommonOAuth2Provider.GITHUB);
ProviderDetails providerDetails = registration.getProviderDetails();
assertThat(providerDetails.getAuthorizationUri())
.isEqualTo("https://github.com/login/oauth/authorize");
assertThat(providerDetails.getTokenUri())
.isEqualTo("https://github.com/login/oauth/access_token");
assertThat(providerDetails.getUserInfoEndpoint().getUri())
.isEqualTo("https://api.github.com/user");
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName())
.isEqualTo("name");
assertThat(providerDetails.getJwkSetUri()).isNull();
assertThat(registration.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.BASIC);
assertThat(registration.getAuthorizationGrantType())
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);
assertThat(registration.getScope()).containsOnly("user");
assertThat(registration.getClientName()).isEqualTo("GitHub");
assertThat(registration.getRegistrationId()).isEqualTo("123");
}
@Test
public void getBuilderWhenFacebookShouldHaveFacebookSettings() throws Exception {
ClientRegistration registration = build(CommonOAuth2Provider.FACEBOOK);
ProviderDetails providerDetails = registration.getProviderDetails();
assertThat(providerDetails.getAuthorizationUri())
.isEqualTo("https://www.facebook.com/v2.8/dialog/oauth");
assertThat(providerDetails.getTokenUri())
.isEqualTo("https://graph.facebook.com/v2.8/oauth/access_token");
assertThat(providerDetails.getUserInfoEndpoint().getUri())
.isEqualTo("https://graph.facebook.com/me");
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName())
.isEqualTo("name");
assertThat(providerDetails.getJwkSetUri()).isNull();
assertThat(registration.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.POST);
assertThat(registration.getAuthorizationGrantType())
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);
assertThat(registration.getScope()).containsOnly("public_profile", "email");
assertThat(registration.getClientName()).isEqualTo("Facebook");
assertThat(registration.getRegistrationId()).isEqualTo("123");
}
@Test
public void getBuilderWhenOktaShouldHaveOktaSettings() throws Exception {
ClientRegistration registration = builder(CommonOAuth2Provider.OKTA)
.authorizationUri("http://example.com/auth")
.tokenUri("http://example.com/token")
.userInfoUri("http://example.com/info").build();
ProviderDetails providerDetails = registration.getProviderDetails();
assertThat(providerDetails.getAuthorizationUri())
.isEqualTo("http://example.com/auth");
assertThat(providerDetails.getTokenUri()).isEqualTo("http://example.com/token");
assertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo("http://example.com/info");
assertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName())
.isEqualTo(null);
assertThat(providerDetails.getJwkSetUri()).isNull();
assertThat(registration.getClientAuthenticationMethod())
.isEqualTo(ClientAuthenticationMethod.BASIC);
assertThat(registration.getAuthorizationGrantType())
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);
assertThat(registration.getScope()).containsOnly("openid", "profile", "email",
"address", "phone");
assertThat(registration.getClientName()).isEqualTo("Okta");
assertThat(registration.getRegistrationId()).isEqualTo("123");
}
private ClientRegistration build(CommonOAuth2Provider provider) {
return builder(provider).build();
}
private Builder builder(CommonOAuth2Provider provider) {
return provider.getBuilder("123")
.clientId("abcd")
.clientSecret("secret");
}
}
/*
* Copyright 2012-2017 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.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Registration;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2ClientPropertiesRegistrationAdapter}.
*
* @author Phillip Webb
*/
public class OAuth2ClientPropertiesRegistrationAdapterTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void getClientRegistrationsWhenUsingDefinedProviderShouldAdapt()
throws Exception {
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Provider provider = new Provider();
provider.setAuthorizationUri("http://example.com/auth");
provider.setTokenUri("http://example.com/token");
provider.setUserInfoUri("http://example.com/info");
provider.setJwkSetUri("http://example.com/jkw");
Registration registration = new Registration();
registration.setProvider("provider");
registration.setClientId("clientId");
registration.setClientSecret("clientSecret");
registration.setClientAuthenticationMethod(ClientAuthenticationMethod.POST);
registration.setAuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
registration.setRedirectUri("http://example.com/redirect");
registration.setScope(Collections.singleton("scope"));
registration.setClientName("clientName");
properties.getProvider().put("provider", provider);
properties.getRegistration().put("registration", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("registration");
ProviderDetails adaptedProvider = adapted.getProviderDetails();
assertThat(adaptedProvider.getAuthorizationUri())
.isEqualTo("http://example.com/auth");
assertThat(adaptedProvider.getTokenUri()).isEqualTo("http://example.com/token");
assertThat(adaptedProvider.getUserInfoEndpoint().getUri()).isEqualTo("http://example.com/info");
assertThat(adaptedProvider.getJwkSetUri()).isEqualTo("http://example.com/jkw");
assertThat(adapted.getRegistrationId()).isEqualTo("registration");
assertThat(adapted.getClientId()).isEqualTo("clientId");
assertThat(adapted.getClientSecret()).isEqualTo("clientSecret");
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.POST);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRedirectUri()).isEqualTo("http://example.com/redirect");
assertThat(adapted.getScope()).containsExactly("scope");
assertThat(adapted.getClientName()).isEqualTo("clientName");
}
@Test
public void getClientRegistrationsWhenUsingCommonProviderShouldAdapt()
throws Exception {
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Registration registration = new Registration();
registration.setProvider("google");
registration.setClientId("clientId");
registration.setClientSecret("clientSecret");
properties.getRegistration().put("registration", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("registration");
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.getJwkSetUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/certs");
assertThat(adapted.getRegistrationId()).isEqualTo("registration");
assertThat(adapted.getClientId()).isEqualTo("clientId");
assertThat(adapted.getClientSecret()).isEqualTo("clientSecret");
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRedirectUri()).isEqualTo(
"{scheme}://{serverName}:{serverPort}{contextPath}/oauth2/authorize/code/{clientAlias}");
assertThat(adapted.getScope()).containsExactly("openid", "profile", "email",
"address", "phone");
assertThat(adapted.getClientName()).isEqualTo("Google");
}
@Test
public void getClientRegistrationsWhenUsingCommonProviderWithOverrideShouldAdapt()
throws Exception {
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Registration registration = new Registration();
registration.setProvider("google");
registration.setClientId("clientId");
registration.setClientSecret("clientSecret");
registration.setClientAuthenticationMethod(ClientAuthenticationMethod.POST);
registration.setAuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
registration.setRedirectUri("http://example.com/redirect");
registration.setScope(Collections.singleton("scope"));
registration.setClientName("clientName");
properties.getRegistration().put("registration", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("registration");
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.getJwkSetUri())
.isEqualTo("https://www.googleapis.com/oauth2/v3/certs");
assertThat(adapted.getRegistrationId()).isEqualTo("registration");
assertThat(adapted.getClientId()).isEqualTo("clientId");
assertThat(adapted.getClientSecret()).isEqualTo("clientSecret");
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(
org.springframework.security.oauth2.core.ClientAuthenticationMethod.POST);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(
org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRedirectUri()).isEqualTo("http://example.com/redirect");
assertThat(adapted.getScope()).containsExactly("scope");
assertThat(adapted.getClientName()).isEqualTo("clientName");
}
@Test
public void getClientRegistrationsWhenUnknownProviderShouldThrowException()
throws Exception {
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Registration registration = new Registration();
registration.setProvider("missing");
properties.getRegistration().put("registration", registration);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Unknown provider ID 'missing'");
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties);
}
}
/*
* Copyright 2012-2017 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 org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
/**
* Tests for {@link OAuth2ClientProperties}.
*
* @author Madhura Bhave
*/
public class OAuth2ClientPropertiesTests {
private OAuth2ClientProperties properties = new OAuth2ClientProperties();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void clientIdAbsentThrowsException() throws Exception {
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
registration.setClientSecret("secret");
registration.setProvider("google");
this.properties.getRegistration().put("foo", registration);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Client id must not be empty.");
this.properties.validate();
}
@Test
public void clientSecretAbsentThrowsException() throws Exception {
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
registration.setClientId("foo");
registration.setProvider("google");
this.properties.getRegistration().put("foo", registration);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Client secret must not be empty.");
this.properties.validate();
}
@Test
public void providerAbsentThrowsException() throws Exception {
OAuth2ClientProperties.Registration registration = new OAuth2ClientProperties.Registration();
registration.setClientId("foo");
registration.setClientSecret("secret");
this.properties.getRegistration().put("foo", registration);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Provider must not be empty.");
this.properties.validate();
}
}
/*
* Copyright 2012-2017 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 org.junit.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2ClientRegistrationRepositoryConfiguration}.
*
* @author Madhura Bhave
*/
public class OAuth2ClientRegistrationRepositoryConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
@Test
public void clientRegistrationRepositoryBeanShouldNotBeCreatedWhenPropertiesAbsent() throws Exception {
this.contextRunner.withUserConfiguration(OAuth2ClientRegistrationRepositoryConfiguration.class)
.run(context -> assertThat(context).doesNotHaveBean(ClientRegistrationRepository.class));
}
@Test
public void clientRegistrationRepositoryBeanShouldBeCreatedWhenPropertiesPresent() throws Exception {
this.contextRunner.withUserConfiguration(OAuth2ClientRegistrationRepositoryConfiguration.class)
.withPropertyValues(REGISTRATION_PREFIX + ".foo.client-id=abcd",
REGISTRATION_PREFIX + ".foo.client-secret=secret",
REGISTRATION_PREFIX + ".foo.provider=github")
.run(context -> {
ClientRegistrationRepository repository = context.getBean(ClientRegistrationRepository.class);
ClientRegistration registration = repository.findByRegistrationId("foo");
assertThat(registration).isNotNull();
assertThat(registration.getClientSecret()).isEqualTo("secret");
});
}
}
/*
* Copyright 2012-2017 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.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.Filter;
import org.junit.Test;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.AuthorizationCodeAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.web.AuthorizationCodeRequestRedirectFilter;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2WebSecurityConfiguration}.
*
* @author Madhura Bhave
*/
public class OAuth2WebSecurityConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
@SuppressWarnings("unchecked")
public void securityConfigurerRegistersClientRegistrations() throws Exception {
this.contextRunner.withUserConfiguration(
ClientRepositoryConfiguration.class, OAuth2WebSecurityConfiguration.class)
.run(context -> {
ClientRegistrationRepository expected = context.getBean(ClientRegistrationRepository.class);
ClientRegistrationRepository actual = (ClientRegistrationRepository) ReflectionTestUtils.getField(
getAuthCodeFilters(context).get(0), "clientRegistrationRepository");
assertThat(isEqual(expected.findByRegistrationId("first"),
actual.findByRegistrationId("first"))).isTrue();
assertThat(isEqual(expected.findByRegistrationId("second"),
actual.findByRegistrationId("second"))).isTrue();
});
}
@Test
public void securityConfigurerBacksOffWhenClientRegistrationBeanAbsent() throws Exception {
this.contextRunner.withUserConfiguration(TestConfig.class, OAuth2WebSecurityConfiguration.class)
.run(context -> assertThat(getAuthCodeFilters(context)).isEmpty());
}
@Test
public void securityConfigurerBacksOffWhenOtherWebSecurityAdapterPresent() throws Exception {
this.contextRunner.withUserConfiguration(TestWebSecurityConfigurerConfig.class, OAuth2WebSecurityConfiguration.class)
.run(context -> assertThat(getAuthCodeFilters(context)).isEmpty());
}
@SuppressWarnings("unchecked")
private List<Filter> getAuthCodeFilters(AssertableApplicationContext context) {
FilterChainProxy filterChain = (FilterChainProxy) context.getBean("springSecurityFilterChain");
List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
List<Filter> filters = (List<Filter>) ReflectionTestUtils.getField(((List) filterChains).get(0), "filters");
List<Filter> oauth2Filters = filters.stream().filter(
f -> f instanceof AuthorizationCodeAuthenticationProcessingFilter ||
f instanceof AuthorizationCodeRequestRedirectFilter).collect(Collectors.toList());
return oauth2Filters.stream().filter(f -> f instanceof AuthorizationCodeAuthenticationProcessingFilter)
.collect(Collectors.toList());
}
private boolean isEqual(ClientRegistration reg1, ClientRegistration reg2) {
boolean result = ObjectUtils.nullSafeEquals(reg1.getClientId(), reg2.getClientId());
result = result && ObjectUtils.nullSafeEquals(reg1.getClientName(), reg2.getClientName());
result = result && ObjectUtils.nullSafeEquals(reg1.getClientSecret(), reg2.getClientSecret());
result = result && ObjectUtils.nullSafeEquals(reg1.getScope(), reg2.getScope());
result = result && ObjectUtils.nullSafeEquals(reg1.getRedirectUri(), reg2.getRedirectUri());
result = result && ObjectUtils.nullSafeEquals(reg1.getRegistrationId(), reg2.getRegistrationId());
result = result && ObjectUtils.nullSafeEquals(reg1.getAuthorizationGrantType(), reg2.getAuthorizationGrantType());
result = result && ObjectUtils.nullSafeEquals(reg1.getProviderDetails().getAuthorizationUri(),
reg2.getProviderDetails().getAuthorizationUri());
result = result && ObjectUtils.nullSafeEquals(reg1.getProviderDetails().getUserInfoEndpoint(),
reg2.getProviderDetails().getUserInfoEndpoint());
result = result && ObjectUtils.nullSafeEquals(reg1.getProviderDetails().getTokenUri(),
reg2.getProviderDetails().getTokenUri());
return result;
}
@Configuration
@EnableWebSecurity
protected static class TestConfig {
@Bean
public TomcatServletWebServerFactory tomcat() {
return new TomcatServletWebServerFactory(0);
}
}
@Configuration
@Import(TestConfig.class)
static class ClientRepositoryConfiguration {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> registrations = new ArrayList<>();
registrations.add(getClientRegistration("first", "http://user-info-uri.com"));
registrations.add(getClientRegistration("second", "http://other-user-info"));
return new InMemoryClientRegistrationRepository(registrations);
}
private ClientRegistration getClientRegistration(String id, String userInfoUri) {
ClientRegistration.Builder builder = new ClientRegistration.Builder(id);
builder.clientName("foo")
.clientId("foo")
.clientAuthenticationMethod(org.springframework.security.oauth2.core.ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.scope("read")
.clientSecret("secret")
.redirectUri("http://redirect-uri.com")
.authorizationUri("http://authorization-uri.com")
.tokenUri("http://token-uri.com")
.userInfoUri(userInfoUri)
.userNameAttributeName("login");
return builder.build();
}
}
@Configuration
@Import({ ClientRepositoryConfiguration.class })
static class TestWebSecurityConfigurerConfig extends WebSecurityConfigurerAdapter {
}
}
......@@ -2750,6 +2750,58 @@ NOTE: By default, a `WebSecurityConfigurerAdapter` will match any path. If you d
to completely override Spring Boot's auto-configured access rules, your adapter must
explicitly configure the paths that you do want to override.
[[boot-features-security-oauth2]]
=== OAuth2
=== 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`.
You can register multiple OAuth2 clients and providers under the `spring.security.oauth2.client` prefix.
For example,
[source,yaml,indent=0]
----
# application.yml
spring:
security:
oauth2:
client:
registration:
my-client-1:
client-id: abcd
client-secret: password
client-name: Client for user scope
provider: my-oauth-provider
scope: user
redirect-uri: http://my-redirect-uri.com
authentication-method: basic
authorization-grant-type: authorization_code
my-client2:
client-id: abcd
client-secret: password
client-name: Client for email scope
provider: my-oauth-provider
scope: email
redirect-uri: http://my-redirect-uri.com
authentication-method: basic
authorization-grant-type: authorization_code
provider:
my-oauth-provider:
authorization-uri: http://my-auth-server/oauth/authorize
token-uri: http://my-auth-server/oauth/token
user-info-uri: http://my-auth-server/userinfo
jwk-set-uri: http://my-auth-server/token_keys
user-name-attribute: name
# additional configuration as required
----
NOTE: For common OAuth2 and OpenID providers such as Google, Github, Facebook and Okta, we provide a set of
provider defaults. If you don't need to customize these providers, you do not need to provide the `provider`
configuration. The client registration `provider` key should reference one these providers.
[[boot-features-security-actuator]]
......
......@@ -62,6 +62,7 @@
<module>spring-boot-sample-junit-jupiter</module>
<module>spring-boot-sample-liquibase</module>
<module>spring-boot-sample-logback</module>
<module>spring-boot-sample-oauth2-client</module>
<module>spring-boot-sample-parent-context</module>
<module>spring-boot-sample-profile</module>
<module>spring-boot-sample-property-validation</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-oauth2-client</artifactId>
<name>Spring Boot Sample OAuth2 Client</name>
<description>Spring Boot Sample OAuth2 Client</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt-jose</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package sample.oauth2.client;
import java.security.Principal;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Madhura Bhave
*/
@RestController
public class ExampleController {
@RequestMapping("/")
public String email(Principal principal) {
return "Hello " + principal.getName();
}
}
package sample.oauth2.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Madhura Bhave
*/
@SpringBootApplication
public class SampleOAuth2ClientApplication {
public static void main(String[] args) {
SpringApplication.run(SampleOAuth2ClientApplication.class);
}
}
spring:
security:
oauth2:
client:
registration:
github-client-1:
client-id: 1b4c6c2d23a2dbec8959
client-secret: 8bdb4047d98f37e1c6aeba9ffd2017bc31682e1b
client-name: Github user
provider: github
scope: user
redirect_uri: http://localhost:8080/oauth2/authorize/code/github
github-client-2:
client-id: 1b4c6c2d23a2dbec8959
client-secret: 8bdb4047d98f37e1c6aeba9ffd2017bc31682e1b
client-name: Github email
provider: github
scope: user:email
redirect_uri: http://localhost:8080/oauth2/authorize/code/github
\ No newline at end of file
package sample.oauth2.client;
import java.net.URI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for an OAuth2 client application.
*
* @author Madhura Bhave
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class SampleOAuth2ClientApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void everythingShouldRedirectToLogin() throws Exception {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation())
.isEqualTo(URI.create("http://localhost:" + this.port + "/login"));
}
@Test
public void loginShouldHaveBothOAuthClientsToChooseFrom() throws Exception {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/login", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("/oauth2/authorization/code/github-client-1");
assertThat(entity.getBody()).contains("/oauth2/authorization/code/github-client-2");
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment