Skip to content

Commit 048896e

Browse files
author
Steve Riesenberg
committed
Add How-to: Authenticate using Social Login
Closes gh-538
1 parent 64ddcfc commit 048896e

10 files changed

+339
-0
lines changed

Diff for: docs/spring-authorization-server-docs.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ asciidoctor {
77
"spring-authorization-server-version": project.version,
88
"spring-security-reference-base-url": "https://docs.spring.io/spring-security/reference",
99
"spring-security-api-base-url": "https://docs.spring.io/spring-security/site/docs/current/api",
10+
"spring-boot-reference-base-url": "https://docs.spring.io/spring-boot/docs/current/reference/html",
1011
"examples-dir": "examples",
12+
"samples-dir": "$rootDir/samples",
1113
"docs-java": "$sourceDir/examples/src/main/java",
1214
"chomp": "default headers packages",
1315
"toc": "left",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package sample.socialLogin;
17+
18+
import org.springframework.context.annotation.Bean;
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.core.annotation.Order;
21+
import org.springframework.http.MediaType;
22+
import org.springframework.security.config.Customizer;
23+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
24+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
25+
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
26+
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
27+
import org.springframework.security.web.SecurityFilterChain;
28+
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
29+
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
30+
31+
@Configuration
32+
@EnableWebSecurity
33+
public class SecurityConfig {
34+
35+
@Bean // <1>
36+
@Order(1)
37+
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
38+
throws Exception {
39+
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
40+
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
41+
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
42+
// @formatter:off
43+
http
44+
// Redirect to the OAuth 2.0 Login endpoint when not authenticated
45+
// from the authorization endpoint
46+
.exceptionHandling((exceptions) -> exceptions
47+
.defaultAuthenticationEntryPointFor( // <2>
48+
new LoginUrlAuthenticationEntryPoint("/oauth2/authorization/my-client"),
49+
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
50+
)
51+
)
52+
// Accept access tokens for User Info and/or Client Registration
53+
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
54+
// @formatter:on
55+
56+
return http.build();
57+
}
58+
59+
@Bean // <3>
60+
@Order(2)
61+
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
62+
throws Exception {
63+
// @formatter:off
64+
http
65+
.authorizeHttpRequests((authorize) -> authorize
66+
.anyRequest().authenticated()
67+
)
68+
// OAuth2 Login handles the redirect to the OAuth 2.0 Login endpoint
69+
// from the authorization server filter chain
70+
.oauth2Login(Customizer.withDefaults()); // <4>
71+
// @formatter:on
72+
73+
return http.build();
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
okta:
2+
base-url: ${OKTA_BASE_URL}
3+
4+
spring:
5+
security:
6+
oauth2:
7+
client:
8+
registration:
9+
my-client:
10+
provider: okta
11+
client-id: ${OKTA_CLIENT_ID}
12+
client-secret: ${OKTA_CLIENT_SECRET}
13+
scope:
14+
- openid
15+
- profile
16+
- email
17+
provider:
18+
okta:
19+
authorization-uri: ${okta.base-url}/oauth2/v1/authorize
20+
token-uri: ${okta.base-url}/oauth2/v1/token
21+
user-info-uri: ${okta.base-url}/oauth2/v1/userinfo
22+
jwk-set-uri: ${okta.base-url}/oauth2/v1/keys
23+
user-name-attribute: sub
+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
[[how-to-social-login]]
2+
= How-to: Authenticate using Social Login
3+
:index-link: ../how-to.html
4+
:docs-dir: ..
5+
:examples-dir: {docs-dir}/examples
6+
:samples-dir: {docs-dir}/../../../../samples
7+
:github-ref: main
8+
:github-base-url: https://github.com/spring-projects/spring-authorization-server/blob/{github-ref}
9+
10+
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].
11+
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].
12+
13+
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.
14+
15+
* <<register-social-login-provider>>
16+
* <<configure-oauth2-login>>
17+
* <<advanced-use-cases>>
18+
19+
[[register-social-login-provider]]
20+
== Register with Social Login Provider
21+
22+
To get started, you will need to set up an application with your chosen social login provider.
23+
Common providers include:
24+
25+
* https://developers.google.com/identity/openid-connect/openid-connect#appsetup[Google]
26+
* https://github.com/settings/developers[GitHub]
27+
* https://developers.facebook.com/apps[Facebook]
28+
* https://www.okta.com/developer/signup[Okta]
29+
30+
Follow the steps for your provider until you are asked to specify a Redirect URI.
31+
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.
32+
33+
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.
34+
35+
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.
36+
37+
Once you've completed the set-up process with your social login provider, you should have obtained credentials (a Client ID and Client Secret).
38+
In addition, you will need to reference the provider's documentation and take note of the following values:
39+
40+
* **Authorization URI**: The endpoint that is used to initiate the `authorization_code` flow at the provider.
41+
* **Token URI**: The endpoint that is used to exchange an `authorization_code` for an `access_token` and optionally an `id_token`.
42+
* **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.
43+
* **User Info URI**: The endpoint that is used to obtain user information, which is required when an `id_token` is not available.
44+
* **User Name Attribute**: The claim in either the `id_token` or the User Info Response containing the username of the user.
45+
46+
[[configure-oauth2-login]]
47+
== Configure OAuth 2.0 Login
48+
49+
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].
50+
51+
* <<configure-oauth2-login-dependency>>
52+
* <<configure-oauth2-login-client-registration>>
53+
* <<configure-oauth2-login-authentication>>
54+
55+
[[configure-oauth2-login-dependency]]
56+
=== Add OAuth2 Client Dependency
57+
58+
First, add the following dependency:
59+
60+
[[configure-oauth2-login-maven-dependency]]
61+
.Maven
62+
[source,xml,role="primary",subs="attributes,verbatim"]
63+
----
64+
<dependency>
65+
<groupId>org.springframework.boot</groupId>
66+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
67+
</dependency>
68+
----
69+
70+
[[configure-oauth2-login-gradle-dependency]]
71+
.Gradle
72+
[source,gradle,role="secondary",subs="attributes,verbatim"]
73+
----
74+
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
75+
----
76+
77+
[[configure-oauth2-login-client-registration]]
78+
=== Register a Client
79+
80+
Next, configure the `ClientRegistration` with the values obtained <<register-social-login-provider,earlier>>.
81+
Using Okta as an example, configure the following properties:
82+
83+
[[configure-oauth2-login-okta-example]]
84+
.application.yml
85+
[source,yaml]
86+
----
87+
include::{examples-dir}/src/main/java/sample/socialLogin/application.yml[]
88+
----
89+
90+
NOTE: The `registrationId` in the above example is `my-client`.
91+
92+
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.
93+
94+
This simple example demonstrates a typical configuration, but some providers will require additional configuration.
95+
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.
96+
97+
[[configure-oauth2-login-authentication]]
98+
=== Configure Authentication
99+
100+
Finally, to configure Spring Authorization Server to use a social login provider for authentication, you can use `oauth2Login()` instead of `formLogin()`.
101+
You can also automatically redirect an unauthenticated user to the provider by configuring `exceptionHandling()` with an `AuthenticationEntryPoint`.
102+
103+
Continuing our <<configure-oauth2-login-okta-example,earlier example>>, configure Spring Security using a `@Configuration` as in the following example:
104+
105+
.Configure OAuth 2.0 Login
106+
[source,java]
107+
----
108+
include::{examples-dir}/src/main/java/sample/socialLogin/SecurityConfig.java[]
109+
----
110+
111+
<1> A Spring Security filter chain for the xref:protocol-endpoints.adoc[Protocol Endpoints].
112+
<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].
113+
<3> A Spring Security filter chain for https://docs.spring.io/spring-security/reference/servlet/authentication/index.html[authentication].
114+
<4> Configure {spring-security-reference-base-url}/servlet/oauth2/login/index.html[OAuth 2.0 Login] for authentication.
115+
116+
If you configured a `UserDetailsService` when xref:{docs-dir}/getting-started.adoc#developing-your-first-application[getting started], you can remove it now.
117+
118+
[[advanced-use-cases]]
119+
== Advanced Use Cases
120+
121+
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.
122+
Select from the following use cases to see an example of each:
123+
124+
* I want to <<advanced-use-cases-automatically-redirect>>
125+
* I want to <<advanced-use-cases-capture-users>>
126+
* I want to <<advanced-use-cases-map-claims>>
127+
* I want to <<advanced-use-cases-configurer>>
128+
129+
[[advanced-use-cases-automatically-redirect]]
130+
=== Automatically Redirect to a Provider
131+
132+
The following example `AuthenticationEntryPoint` uses a query parameter as a hint from the client to indicate which provider to automatically redirect to for authentication.
133+
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:
134+
135+
.`FederatedIdentityAuthenticationEntryPoint`
136+
[source,java]
137+
----
138+
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationEntryPoint.java[]
139+
----
140+
141+
[[advanced-use-cases-capture-users]]
142+
=== Capture Users in a Database
143+
144+
The following example `AuthenticationSuccessHandler` uses a custom component to capture users in a local database when they first log in:
145+
146+
.`FederatedIdentityAuthenticationSuccessHandler`
147+
[source,java]
148+
----
149+
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java[]
150+
----
151+
152+
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.
153+
Here is an example that simply stores users in-memory:
154+
155+
.`UserRepositoryOAuth2UserHandler`
156+
[source,java]
157+
----
158+
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java[]
159+
----
160+
161+
[[advanced-use-cases-map-claims]]
162+
=== Map Claims to an ID Token
163+
164+
The following example `OAuth2TokenCustomizer` maps a user's claims from an authentication provider to the `id_token` produced by Spring Authorization Server:
165+
166+
.`FederatedIdentityIdTokenCustomizer`
167+
[source,java]
168+
----
169+
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityIdTokenCustomizer.java[]
170+
----
171+
172+
You can configure Spring Authorization Server to use this customizer by publishing it as a `@Bean` as in the following example:
173+
174+
.Configure `FederatedIdentityIdTokenCustomizer`
175+
[source,java]
176+
----
177+
@Bean
178+
public OAuth2TokenCustomizer<JwtEncodingContext> idTokenCustomizer() {
179+
return new FederatedIdentityIdTokenCustomizer();
180+
}
181+
----
182+
183+
[[advanced-use-cases-configurer]]
184+
=== Create My Own Configurer
185+
186+
The following example `SecurityConfigurer` combines configuration for all of the above examples into a single reusable component:
187+
188+
.`FederatedIdentityConfigurer`
189+
[source,java]
190+
----
191+
include::{samples-dir}/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityConfigurer.java[]
192+
----
193+
194+
The configurer can be applied using the Spring Security DSL as in the following example:
195+
196+
.Apply Configurer
197+
[source,java]
198+
----
199+
http.apply(new FederatedIdentityConfigurer());
200+
----
201+
202+
The configurer also has its own DSL to customize the defaults.
203+
Here's a full example:
204+
205+
.Customize using Configurer
206+
[source,java]
207+
----
208+
http.apply(new FederatedIdentityConfigurer())
209+
.loginPageUrl("/social/login")
210+
.authorizationRequestUri("/social/login/{registrationId}")
211+
.oauth2UserHandler((oauth2User) -> {
212+
// TODO: Handle login of an OAuth2 user...
213+
})
214+
.oidcUserHandler((oidcUser) -> {
215+
// TODO: Handle login of an OIDC user...
216+
});
217+
----

Diff for: docs/src/docs/asciidoc/how-to.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
[[how-to-overview]]
55
== List of Guides
66

7+
* xref:guides/how-to-social-login.adoc[Authenticate using Social Login]
78
* xref:guides/how-to-userinfo.adoc[Customize the OpenID Connect 1.0 UserInfo response]
89
* xref:guides/how-to-jpa.adoc[Implement core services with JPA]

Diff for: samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationEntryPoint.java

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package sample.federation;
1717

18+
// tag::imports[]
1819
import java.io.IOException;
1920

2021
import jakarta.servlet.ServletException;
@@ -31,6 +32,7 @@
3132
import org.springframework.security.web.RedirectStrategy;
3233
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
3334
import org.springframework.web.util.UriComponentsBuilder;
35+
// end::imports[]
3436

3537
/**
3638
* An {@link AuthenticationEntryPoint} for initiating the login flow to an
@@ -40,6 +42,7 @@
4042
* @author Steve Riesenberg
4143
* @since 1.1
4244
*/
45+
// tag::class[]
4346
public final class FederatedIdentityAuthenticationEntryPoint implements AuthenticationEntryPoint {
4447

4548
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@@ -80,3 +83,4 @@ public void setAuthorizationRequestUri(String authorizationRequestUri) {
8083
}
8184

8285
}
86+
// end::class[]

Diff for: samples/demo-authorizationserver/src/main/java/sample/federation/FederatedIdentityAuthenticationSuccessHandler.java

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package sample.federation;
1717

18+
// tag::imports[]
1819
import java.io.IOException;
1920
import java.util.function.Consumer;
2021

@@ -28,6 +29,7 @@
2829
import org.springframework.security.oauth2.core.user.OAuth2User;
2930
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
3031
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
32+
// end::imports[]
3133

3234
/**
3335
* An {@link AuthenticationSuccessHandler} for capturing the {@link OidcUser} or
@@ -36,6 +38,7 @@
3638
* @author Steve Riesenberg
3739
* @since 1.1
3840
*/
41+
// tag::class[]
3942
public final class FederatedIdentityAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
4043

4144
private final AuthenticationSuccessHandler delegate = new SavedRequestAwareAuthenticationSuccessHandler();
@@ -66,3 +69,4 @@ public void setOidcUserHandler(Consumer<OidcUser> oidcUserHandler) {
6669
}
6770

6871
}
72+
// end::class[]

0 commit comments

Comments
 (0)