Spring Security 5.3 OAuth2 integration with Facebook along with Form-Based login
Introduction
OAuth2 configurations are quite intimidating. You might have come across to see many people have tried to simplify by writing blogs, series about how to integrate OAuth2 facebook with Spring security in many different ways but when we start we get stuck as it does not work because of so many different configurations. Also, you don’t understand where to make changes to customize it according to your project.
In this post, we are going to see the simplest way of spring security 5 Facebook oauth2 integration along with form-based where users can either log in via Facebook or by entering username password in the login form.
In this post implementation is going to in-memory only. The whole idea is to show the minimal configuration of spring security 5 for OAuth2 integration with Facebook along with form-based authentication. I have a plan to take it to the next level using DB and email integration and remember me blah blah. So stay tuned for the next post!!!
Adding Dependency
With the assumption that you have knowledge on how to create spring boot project and have an understanding of the basics of maven, spring MVC following is the dependency, you need to add in your pom.xml.
- The latest spring security project provides support for the OAuth2 server dependency but not the client. Since we need to connect to the Facebook OAuth server as a client so we need to add ‘oauth2-client’ dependency.
For experts who know the internals of spring security, OAuth2-client dependency is needed along with spring security because spring security needs OAuth2LoginAuthenticationFilter in its filter chain to be present on classpath in order to support OAuth client Integration. Which is present inside spring-boot-starter-oauth2-client. We will cover this later in upcoming posts.
- Adding tomcat-embed-jasper dependency in order to support spring-MVC with JSP.
Project Structure
Hint: To support JSP you need to have webapp as a source folder and create WEB-INF/jsp/ under it. In eclipse you can Right-click on project>new>source folder and name it webapp. And then create two folders WEB-INF/jsp/.
Creating a Facebook App for Oauth 2 Login
- Go to https://developers.facebook.com/
- Login and create a new app
- Add details like application name, URL, and any required details.
- After successful app creation, you would see something like below where you can navigate to settings>basic and copy client-id and client-secret
Note: The above steps may change on the Facebook developer page, depending on how long they keep the same UI.
Application Properties
- Copy client-id and client-secret from the previous step, you need to add that in application.yml/properties file on given placeholders(<>) below which is self-explanatory. One thing you need to keep in mind that client-secret is something that you need to keep encrypted. To know how to encrypt properties in spring boot property files see my other post.
- Technically scope is a grant_type that is specific to the provider. In our case, Facebook has these two default scope which will grant to the client(our app) to access public profile and email once the user is authenticated. There are various scopes (see here) that you can define but that depends if your Facebook app is allowed to access or not.
- Added Jsp view resolver mapping to look Jsp pages inside
WEB-INF/jsp
folder.
Login Page
Before jumping to spring security configuration what our expectation is, we want to have one custom login page where we can add the Sign In with Facebook button along with form where we can enter username password. Something like below —
Security Configuration
Keeping the login page in mind lets configure our security configuration.
Explanation: Okay so this is the main part that you need to understand that what we have done step by step.
While reading the below points parallelly map it with the configuration we did in the above code snippet.
Below is how you can read-
Hey spring-security (HttpSecurity technically)-
- authorize all the request and let everyone access
/login
page (permitAll()
)see line:8
- After that, any request should be authenticated
see line:9
- And enable form-based login
see line:10
- And add a custom login page
/login
for form-based and redirect to/formLoginSuccess
after successful authentication seeline:11
- And enable logout (default URL would be
/logout
)see line:12
- And enable OAuth2Login flow
see line:13
- And add a custom login page
/login
(same as form-based) and redirect to/oauth2LoginSuccess
once the user is authenticated from Facebooksee line:14
- And disable CSRF token. By default, it is enabled and this will force you to call
/logout
usingHTTP POST
instead ofGET
Method. So let's disable it for now.see line 15
After that configure form-based credentials-
Configure an in-memory username password so that we can use the login-form to submit a username password for authentication.see line:19 configure(AuthenticationManagerBuilder auth) method
for more details in the above code snippet. Which is self-explanatory.
Things to know —
- spring-security by default provides oauth2 client implementation for common providers like Facebook, Google, GitHub, and Okta. That is why we didn’t do anything just added facebook details in application.properties and bang!
- When you enable OAuth2Login flow
see line 13
, by default spring-security enables authorization endpoint as/oauth2/authorization/{registerationId}
. In our case, registration id isfacebook(see application props)
hence URL to trigger Facebook authorization would be/oauth2/authorization/facebook
and our app will talk to Facebook for OAuth2 flow. - Since our application has two different login flow Oauth and form-based hence spring security stores user details into 2 different objects
Oauth2User
for OAuth-flow andUserDetails
for form-based flow. We will see this in detail soon.
Adding Controller
Now we need to configure all the custom URLs that we have mentioned in the security configuration which is
1. /login
page
Since we need to show custom login page so this is going to be like —
2. /oauth2LoginSuccess
Request Mapping
Once a user clicks on sign in with facebook
, Oauth flow triggers our application will redirect the user to go on Facebook and enter the username password after successful authentication spring-security will redirect him to /oauth2LoginSuccess
as we mention it in security config as default success URL.
A lot of things happens behind the scene in OAuth2 flow. Intent of this post is to cover basic confiugration and integration. We will understand flow in upcoming posts as we will dive in. Stay tunned !
Below is the request handler mapping code snippet —
Things to know —
When a user logs in in your application through OAuth2 Flow
- The very first time your application won’t have any user details present in DB. So in
/oauth2LoginSuccess
method you can look into DB and see if the user is not present the DB then insert into DB and send a welcome email else to load other profile related information. [We will see this in an upcoming post when we will integrate this flow with DB] - Sometime you may need more user details from Facebook like
first_name
last_name
which by default is not present insideOAuth2User
object then here you have to make another call here to Facebook to get more details. So to do that you have to obtain facebook authenticated token firstsee line 17–20
in the above code snippet and pass that into rest call to facebook asBearer <token>
in header to fetch more details. We will see this next post when we will store the first name last name in DB. - To obtain user info you need to get
OAuth2User
object fromOAuth2AuthenticationToken.getPricipal()
andOAuth2User.getAttributes()
will return you a map of user info. @AuthenticationPricipal
is a new annotation in spring security which extracts the authentication object. This is exactly similar to —
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
In OAuth2 login flow OAuth2User object is encapsulated inside authentication object which we can obtain via
Authentication
object orOAuth2AuthenticationToken
.
3. /formLoginSuccess
Request Mapping
Once the user will enter a valid username password which is admin/admin
in our case as we have defined in memory. After successful authentication user will be redirected to /formLoginSuccess
In form login flow UserDetails object encapsulated in authentication which we can obtain using authentication.getPrincipal()
Login Page HTML
I will leave it on you to understand below HTML :).
Home Page HTML
Home page after Facebook login
Home page username password flow
In both cases, we land on the same screen after successful authentication.
Summary
So far we have seen one of the simplest ways to integrate with Facebook along with form-based. Which most of us need in our application to support. But this is not enough. We need to customize it more and more and integrate it with DB. Add Forget-Password flow, Remember me, Or you may be looking for REST-based flow where you send Bearer <Token>
in the response to the client (like angular) as many of us might be working in the microservices world. We will cover all the topics one by one in my upcoming posts. They are all in draft mode :).
👉🏽Check out the complete working code on my GitHub account.
PS: Your comments/claps keep me motivated to write more stuff like this. Please keep me motivated 😜.