Browse Source

Add How-to: Authenticate using Social Login

Closes gh-538
pull/1210/head
Steve Riesenberg 3 years ago
parent
commit
048896ef74
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
  1. 2
      docs/spring-authorization-server-docs.gradle
  2. 76
      docs/src/docs/asciidoc/examples/src/main/java/sample/socialLogin/SecurityConfig.java
  3. 23
      docs/src/docs/asciidoc/examples/src/main/java/sample/socialLogin/application.yml
  4. 217
      docs/src/docs/asciidoc/guides/how-to-social-login.adoc
  5. 1
      docs/src/docs/asciidoc/how-to.adoc
  6. 4
      samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationEntryPoint.java
  7. 4
      samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java
  8. 4
      samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityConfigurer.java
  9. 4
      samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityIdTokenCustomizer.java
  10. 4
      samples/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java

2
docs/spring-authorization-server-docs.gradle

@ -7,7 +7,9 @@ asciidoctor { @@ -7,7 +7,9 @@ asciidoctor {
"spring-authorization-server-version": project.version,
"spring-security-reference-base-url": "https://docs.spring.io/spring-security/reference",
"spring-security-api-base-url": "https://docs.spring.io/spring-security/site/docs/current/api",
"spring-boot-reference-base-url": "https://docs.spring.io/spring-boot/docs/current/reference/html",
"examples-dir": "examples",
"samples-dir": "$rootDir/samples",
"docs-java": "$sourceDir/examples/src/main/java",
"chomp": "default headers packages",
"toc": "left",

76
docs/src/docs/asciidoc/examples/src/main/java/sample/socialLogin/SecurityConfig.java

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
/*
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.socialLogin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean // <1>
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
// @formatter:off
http
// Redirect to the OAuth 2.0 Login endpoint when not authenticated
// from the authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor( // <2>
new LoginUrlAuthenticationEntryPoint("/oauth2/authorization/my-client"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
// @formatter:on
return http.build();
}
@Bean // <3>
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// OAuth2 Login handles the redirect to the OAuth 2.0 Login endpoint
// from the authorization server filter chain
.oauth2Login(Customizer.withDefaults()); // <4>
// @formatter:on
return http.build();
}
}

23
docs/src/docs/asciidoc/examples/src/main/java/sample/socialLogin/application.yml

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
okta:
base-url: ${OKTA_BASE_URL}
spring:
security:
oauth2:
client:
registration:
my-client:
provider: okta
client-id: ${OKTA_CLIENT_ID}
client-secret: ${OKTA_CLIENT_SECRET}
scope:
- openid
- profile
- email
provider:
okta:
authorization-uri: ${okta.base-url}/oauth2/v1/authorize
token-uri: ${okta.base-url}/oauth2/v1/token
user-info-uri: ${okta.base-url}/oauth2/v1/userinfo
jwk-set-uri: ${okta.base-url}/oauth2/v1/keys
user-name-attribute: sub

217
docs/src/docs/asciidoc/guides/how-to-social-login.adoc

@ -0,0 +1,217 @@ @@ -0,0 +1,217 @@
[[how-to-social-login]]
= How-to: Authenticate using Social Login
:index-link: ../how-to.html
:docs-dir: ..
:examples-dir: {docs-dir}/examples
:samples-dir: {docs-dir}/../../../../samples
:github-ref: main
:github-base-url: https://github.com/spring-projects/spring-authorization-server/blob/{github-ref}
This guide shows how to configure xref:{docs-dir}/index.adoc#top[Spring Authorization Server] with a social login provider (such as Google, GitHub, etc.) for {spring-security-reference-base-url}/servlet/authentication/index.html[authentication].
The purpose of this guide is to demonstrate how to replace {spring-security-reference-base-url}/servlet/authentication/passwords/form.html[Form Login] with {spring-security-reference-base-url}/servlet/oauth2/login/index.html[OAuth 2.0 Login].
NOTE: Spring Authorization Server is built on {spring-security-reference-base-url}/index.html[Spring Security] and we will be using Spring Security concepts throughout this guide.
* <<register-social-login-provider>>
* <<configure-oauth2-login>>
* <<advanced-use-cases>>
[[register-social-login-provider]]
== Register with Social Login Provider
To get started, you will need to set up an application with your chosen social login provider.
Common providers include:
* https://developers.google.com/identity/openid-connect/openid-connect#appsetup[Google]
* https://github.com/settings/developers[GitHub]
* https://developers.facebook.com/apps[Facebook]
* https://www.okta.com/developer/signup[Okta]
Follow the steps for your provider until you are asked to specify a Redirect URI.
To set up a Redirect URI, choose a `registrationId` (such as `google`, `my-client` or any other unique identifier you wish) which you will use to configure both Spring Security **and** your provider.
NOTE: The `registrationId` is a unique identifier for the `ClientRegistration` in Spring Security. The default Redirect URI template is `\{baseUrl\}/login/oauth2/code/\{registrationId\}`. See {spring-security-reference-base-url}/servlet/oauth2/login/core.html#oauth2login-sample-redirect-uri[Setting the Redirect URI] in the Spring Security reference for more information.
TIP: For example, testing locally on port `9000` with a `registrationId` of `google`, your Redirect URI would be `http://localhost:9000/login/oauth2/code/google`. Enter this value as the Redirect URI when setting up the application with your provider.
Once you've completed the set-up process with your social login provider, you should have obtained credentials (a Client ID and Client Secret).
In addition, you will need to reference the provider's documentation and take note of the following values:
* **Authorization URI**: The endpoint that is used to initiate the `authorization_code` flow at the provider.
* **Token URI**: The endpoint that is used to exchange an `authorization_code` for an `access_token` and optionally an `id_token`.
* **JWK Set URI**: The endpoint that is used to obtain keys for verifying the signature of a JWT, which is required when an `id_token` is available.
* **User Info URI**: The endpoint that is used to obtain user information, which is required when an `id_token` is not available.
* **User Name Attribute**: The claim in either the `id_token` or the User Info Response containing the username of the user.
[[configure-oauth2-login]]
== Configure OAuth 2.0 Login
Once you've <<register-social-login-provider,registered>> with a social login provider, you can proceed to configuring Spring Security for {spring-security-reference-base-url}/servlet/oauth2/login/index.html[OAuth 2.0 Login].
* <<configure-oauth2-login-dependency>>
* <<configure-oauth2-login-client-registration>>
* <<configure-oauth2-login-authentication>>
[[configure-oauth2-login-dependency]]
=== Add OAuth2 Client Dependency
First, add the following dependency:
[[configure-oauth2-login-maven-dependency]]
.Maven
[source,xml,role="primary",subs="attributes,verbatim"]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
----
[[configure-oauth2-login-gradle-dependency]]
.Gradle
[source,gradle,role="secondary",subs="attributes,verbatim"]
----
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
----
[[configure-oauth2-login-client-registration]]
=== Register a Client
Next, configure the `ClientRegistration` with the values obtained <<register-social-login-provider,earlier>>.
Using Okta as an example, configure the following properties:
[[configure-oauth2-login-okta-example]]
.application.yml
[source,yaml]
----
include::{examples-dir}/src/main/java/sample/socialLogin/application.yml[]
----
NOTE: The `registrationId` in the above example is `my-client`.
TIP: The above example demonstrates the *recommended* way to set the Provider URL, Client ID and Client Secret using environment variables (`OKTA_BASE_URL`, `OKTA_CLIENT_ID` and `OKTA_CLIENT_SECRET`). See {spring-boot-reference-base-url}/features.html#features.external-config[Externalized Configuration] in the Spring Boot reference for more information.
This simple example demonstrates a typical configuration, but some providers will require additional configuration.
For more information about configuring the `ClientRegistration`, see {spring-security-reference-base-url}/servlet/oauth2/login/core.html#oauth2login-boot-property-mappings[Spring Boot Property Mappings] in the Spring Security reference.
[[configure-oauth2-login-authentication]]
=== Configure Authentication
Finally, to configure Spring Authorization Server to use a social login provider for authentication, you can use `oauth2Login()` instead of `formLogin()`.
You can also automatically redirect an unauthenticated user to the provider by configuring `exceptionHandling()` with an `AuthenticationEntryPoint`.
Continuing our <<configure-oauth2-login-okta-example,earlier example>>, configure Spring Security using a `@Configuration` as in the following example:
.Configure OAuth 2.0 Login
[source,java]
----
include::{examples-dir}/src/main/java/sample/socialLogin/SecurityConfig.java[]
----
<1> A Spring Security filter chain for the xref:protocol-endpoints.adoc[Protocol Endpoints].
<2> Configure an `AuthenticationEntryPoint` for redirecting to the {spring-security-reference-base-url}/servlet/oauth2/login/advanced.html#oauth2login-advanced-login-page[OAuth 2.0 Login endpoint].
<3> A Spring Security filter chain for https://docs.spring.io/spring-security/reference/servlet/authentication/index.html[authentication].
<4> Configure {spring-security-reference-base-url}/servlet/oauth2/login/index.html[OAuth 2.0 Login] for authentication.
If you configured a `UserDetailsService` when xref:{docs-dir}/getting-started.adoc#developing-your-first-application[getting started], you can remove it now.
[[advanced-use-cases]]
== Advanced Use Cases
The https://github.com/spring-projects/spring-authorization-server/tree/{github-ref}/samples#demo-sample[demo authorization server sample^] demonstrates advanced configuration options for federating identity providers.
Select from the following use cases to see an example of each:
* I want to <<advanced-use-cases-automatically-redirect>>
* I want to <<advanced-use-cases-capture-users>>
* I want to <<advanced-use-cases-map-claims>>
* I want to <<advanced-use-cases-configurer>>
[[advanced-use-cases-automatically-redirect]]
=== Automatically Redirect to a Provider
The following example `AuthenticationEntryPoint` uses a query parameter as a hint from the client to indicate which provider to automatically redirect to for authentication.
For example, assuming Google is configured as a social login provider with a `registrationId` of `google`, a request to `/oauth2/authorize?idp=google&...` will redirect an unauthenticated user to `/oauth2/authorization/google` which will initiate logging in with Google:
.`FederatedIdentityAuthenticationEntryPoint`
[source,java]
----
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationEntryPoint.java[]
----
[[advanced-use-cases-capture-users]]
=== Capture Users in a Database
The following example `AuthenticationSuccessHandler` uses a custom component to capture users in a local database when they first log in:
.`FederatedIdentityAuthenticationSuccessHandler`
[source,java]
----
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java[]
----
Using the `AuthenticationSuccessHandler` above, you can plug in your own `Consumer<OAuth2User>` that can capture users in a database or other data store for concepts like Federated Account Linking or JIT Account Provisioning.
Here is an example that simply stores users in-memory:
.`UserRepositoryOAuth2UserHandler`
[source,java]
----
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java[]
----
[[advanced-use-cases-map-claims]]
=== Map Claims to an ID Token
The following example `OAuth2TokenCustomizer` maps a user's claims from an authentication provider to the `id_token` produced by Spring Authorization Server:
.`FederatedIdentityIdTokenCustomizer`
[source,java]
----
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityIdTokenCustomizer.java[]
----
You can configure Spring Authorization Server to use this customizer by publishing it as a `@Bean` as in the following example:
.Configure `FederatedIdentityIdTokenCustomizer`
[source,java]
----
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> idTokenCustomizer() {
return new FederatedIdentityIdTokenCustomizer();
}
----
[[advanced-use-cases-configurer]]
=== Create My Own Configurer
The following example `SecurityConfigurer` combines configuration for all of the above examples into a single reusable component:
.`FederatedIdentityConfigurer`
[source,java]
----
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityConfigurer.java[]
----
The configurer can be applied using the Spring Security DSL as in the following example:
.Apply Configurer
[source,java]
----
http.apply(new FederatedIdentityConfigurer());
----
The configurer also has its own DSL to customize the defaults.
Here's a full example:
.Customize using Configurer
[source,java]
----
http.apply(new FederatedIdentityConfigurer())
.loginPageUrl("/social/login")
.authorizationRequestUri("/social/login/{registrationId}")
.oauth2UserHandler((oauth2User) -> {
// TODO: Handle login of an OAuth2 user...
})
.oidcUserHandler((oidcUser) -> {
// TODO: Handle login of an OIDC user...
});
----

1
docs/src/docs/asciidoc/how-to.adoc

@ -4,5 +4,6 @@ @@ -4,5 +4,6 @@
[[how-to-overview]]
== List of Guides
* xref:guides/how-to-social-login.adoc[Authenticate using Social Login]
* xref:guides/how-to-userinfo.adoc[Customize the OpenID Connect 1.0 UserInfo response]
* xref:guides/how-to-jpa.adoc[Implement core services with JPA]

4
samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationEntryPoint.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package sample.federation;
// tag::imports[]
import java.io.IOException;
import jakarta.servlet.ServletException;
@ -31,6 +32,7 @@ import org.springframework.security.web.DefaultRedirectStrategy; @@ -31,6 +32,7 @@ import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.web.util.UriComponentsBuilder;
// end::imports[]
/**
* An {@link AuthenticationEntryPoint} for initiating the login flow to an
@ -40,6 +42,7 @@ import org.springframework.web.util.UriComponentsBuilder; @@ -40,6 +42,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* @author Steve Riesenberg
* @since 1.1
*/
// tag::class[]
public final class FederatedIdentityAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@ -80,3 +83,4 @@ public final class FederatedIdentityAuthenticationEntryPoint implements Authenti @@ -80,3 +83,4 @@ public final class FederatedIdentityAuthenticationEntryPoint implements Authenti
}
}
// end::class[]

4
samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package sample.federation;
// tag::imports[]
import java.io.IOException;
import java.util.function.Consumer;
@ -28,6 +29,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser; @@ -28,6 +29,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
// end::imports[]
/**
* An {@link AuthenticationSuccessHandler} for capturing the {@link OidcUser} or
@ -36,6 +38,7 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti @@ -36,6 +38,7 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
* @author Steve Riesenberg
* @since 1.1
*/
// tag::class[]
public final class FederatedIdentityAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final AuthenticationSuccessHandler delegate = new SavedRequestAwareAuthenticationSuccessHandler();
@ -66,3 +69,4 @@ public final class FederatedIdentityAuthenticationSuccessHandler implements Auth @@ -66,3 +69,4 @@ public final class FederatedIdentityAuthenticationSuccessHandler implements Auth
}
}
// end::class[]

4
samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityConfigurer.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package sample.federation;
// tag::imports[]
import java.util.function.Consumer;
import org.springframework.context.ApplicationContext;
@ -24,6 +25,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio @@ -24,6 +25,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.util.Assert;
// end::imports[]
/**
* A configurer for setting up Federated Identity Management.
@ -31,6 +33,7 @@ import org.springframework.util.Assert; @@ -31,6 +33,7 @@ import org.springframework.util.Assert;
* @author Steve Riesenberg
* @since 1.1
*/
// tag::class[]
public final class FederatedIdentityConfigurer extends AbstractHttpConfigurer<FederatedIdentityConfigurer, HttpSecurity> {
private String loginPageUrl = "/login";
@ -123,3 +126,4 @@ public final class FederatedIdentityConfigurer extends AbstractHttpConfigurer<Fe @@ -123,3 +126,4 @@ public final class FederatedIdentityConfigurer extends AbstractHttpConfigurer<Fe
// @formatter:on
}
// end::class[]

4
samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityIdTokenCustomizer.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package sample.federation;
// tag::imports[]
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -30,6 +31,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser; @@ -30,6 +31,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
// end::imports[]
/**
* An {@link OAuth2TokenCustomizer} to map claims from a federated identity to
@ -38,6 +40,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke @@ -38,6 +40,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
* @author Steve Riesenberg
* @since 1.1
*/
// tag::class[]
public final class FederatedIdentityIdTokenCustomizer implements OAuth2TokenCustomizer<JwtEncodingContext> {
private static final Set<String> ID_TOKEN_CLAIMS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
@ -89,3 +92,4 @@ public final class FederatedIdentityIdTokenCustomizer implements OAuth2TokenCust @@ -89,3 +92,4 @@ public final class FederatedIdentityIdTokenCustomizer implements OAuth2TokenCust
}
}
// end::class[]

4
samples/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java

@ -15,11 +15,13 @@ @@ -15,11 +15,13 @@
*/
package sample.federation;
// tag::imports[]
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import org.springframework.security.oauth2.core.user.OAuth2User;
// end::imports[]
/**
* Example {@link Consumer} to perform JIT provisioning of an {@link OAuth2User}.
@ -27,6 +29,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User; @@ -27,6 +29,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
* @author Steve Riesenberg
* @since 1.1
*/
// tag::class[]
public final class UserRepositoryOAuth2UserHandler implements Consumer<OAuth2User> {
private final UserRepository userRepository = new UserRepository();
@ -55,3 +58,4 @@ public final class UserRepositoryOAuth2UserHandler implements Consumer<OAuth2Use @@ -55,3 +58,4 @@ public final class UserRepositoryOAuth2UserHandler implements Consumer<OAuth2Use
}
}
// end::class[]

Loading…
Cancel
Save