Facebook Login with OAuth2 in Spring Boot
In this tutorial, we’re back to Spring Boot and we’ll show you how to create a very simple Facebook Login app with OAuth2, spring boot and Facebook social login
Project Setup
So here is the our Spring boot project that serving a static html page.
package springbootdemo; import ... @SpringBootApplication @RestController public class SpringBootDemo { public static void main(String[] args) throws Exception { SpringApplication.run(SpringBootDemo.class, args); } }
I already have a button which when clicked, it will navigate to /login. You’ll see that I’ve prepared another button and a text span which currently hidden. We’ll use that later to display the username and logout button once we have authenticated with Facebook.
<html> ... <body> ... <div class="unauthenticated"> <a class="btn" href="/login">Login</a> </div> <div class="authenticated" style="display: none"> Logged in as: <span id="user"></span> <div><button onClick="logout()" class="btn">Logout</button></div> </div> </body> </html>
The first step is to add 2 dependencies. Spring Boot starter security and Oauth2 to pom.xml. If you refresh the page now, you’ll see that our site has been secured by a HTTP basic login form.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency>
Enable OAuth2
We’ll add EnableOAuth2Sso annotation to our main class
@EnableOAuth2Sso
Then, add application.yml to your project and add the oauth2 setting.
security: oauth2: client: clientId: {your Facebook App ID} clientSecret: {your Facebook App secret ID} accessTokenUri: https://graph.facebook.com/oauth/access_token userAuthorizationUri: https://www.facebook.com/dialog/oauth tokenName: oauth_token authenticationScheme: query clientAuthenticationScheme: form resource: userInfoUri: https://graph.facebook.com/me
This is Facebook specific setting, what you only need to provide is the clientID (or App ID) and clientSecret or your App secret key. Both of this can be found on Facebook Developer page when you created the app.
It should be noted that you need to add your endpoint url that you’ll use for authentication to the “valid oAuth redirected url” setting on Facebook. Because the oAuth will only work for a specific address. In this case, we’re running this locally so I’m going to put localhost:8080 and localhost:8080/login
Now I’m going to restart the server and refresh the page.
You’ll see that once I refreshed, it will automatically redirected to OAuth because everything on your site is restricted for access and you must be authenticated first. We need to fix this since we should let the user click the login button by themselves before we redirected to OAuth.
Login/Logout Button
We need to allow access to root url and /login by adding security configuration. First, extend our main class with WebsecurityConfigurerAdapter and override the configured method.
public class SpringBootDemo extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**", "/webjars/**") .permitAll() .anyRequest() .authenticated() .and().logout().logoutSuccessUrl("/").permitAll(); } ... }
Now we also wanna retrieve the the username from Facebook so let’s create another endpoint at /user and return the principle object we receive from the authentication.
@RequestMapping("/user") public Principal user(Principal principal) { return principal; }
And finally on our html, we’ll execute the GET Ajax to /user endpoint. Then show logout button and username and also hide the login button if the ajax call success
$.get("/user", function(data) { $("#user").html(data.userAuthentication.details.name); $(".unauthenticated").hide() $(".authenticated").show() });
Now the last part, the logout button. Spring boot already provide support for logout endpoint so all you have to do is to make the POST request to it. So I’m going to add a javascript to make the POST Ajax to /logout endpoint. Also, clear the username and show the login button.
var logout = function() { $.post("/logout", function() { $("#user").html(''); $(".unauthenticated").show(); $(".authenticated").hide(); }); return true; }
CSRF and URL Fragment
The important note is Spring Boot has a built in CSRF (Cross Site Request Forgery protection) which need you to provide CSRF token along with the POST request to make it works. But to avoid the complication, we’ll disable this feature by call csrf.disable() in security configuration.
protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() ...
Let’s reboot the server and try again.
That works almost perfectly, but if you notice, there are weird characters at the end of the url.
This was added by Facebook to protect the security session exploit. Per Facebook it’s the responsibility of the developers to handle this. Well, it does look ugly and confusing for the user so I’m going to add a few javascript to remove this from the url.
if (window.location.hash == '#_=_'){ history.replaceState ? history.replaceState(null, null, window.location.href.split('#')[0]) : window.location.hash = ''; }
You can watch the whole tutorial in this video.
I hope this should gave some idea about it. Let me know if you have any questions or feedback. And please follow us on our Facebook and Youtube Channel to stay tune if you love this tutorial.
The final code should look like this
package springbootdemo; import java.security.Principal; import org.springframework.boot.*; import org.springframework.boot.autoconfigure.*; import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.web.bind.annotation.*; @SpringBootApplication @RestController @EnableOAuth2Sso public class SpringBootDemo extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**", "/webjars/**") .permitAll() .anyRequest() .authenticated() .and().logout().logoutSuccessUrl("/").permitAll(); } @RequestMapping("/user") public Principal user(Principal principal) { return principal; } public static void main(String[] args) throws Exception { SpringApplication.run(SpringBootDemo.class, args); } }
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>Red Stapler</title> <meta name="description" content="" /> <meta name="viewport" content="width=device-width" /> <base href="/" /> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" /> <link rel="stylesheet" type="text/css" href="css/style.css" /> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script> </head> <body> <div id="navbar"> <span>OAuth2 - Single Sign On with Facebook</span> </div> <div id="wrapper"> <div class="unauthenticated"> <a class="btn" href="/login">Login</a> </div> <div class="authenticated" style="display: none"> Logged in as: <span id="user"></span> <div> <button onClick="logout()" class="btn">Logout</button> </div> </div> </div> <script> if (window.location.hash == '#_=_'){ history.replaceState ? history.replaceState(null, null, window.location.href.split('#')[0]) : window.location.hash = ''; } $.get("/user", function(data) { $("#user").html(data.userAuthentication.details.name); $(".unauthenticated").hide() $(".authenticated").show() }); var logout = function() { $.post("/logout", function() { $("#user").html(''); $(".unauthenticated").show(); $(".authenticated").hide(); }); return true; } </script> </body> </html>
security: oauth2: client: clientId: {your Facebook App ID} clientSecret: {your Facebook App secret ID} accessTokenUri: https://graph.facebook.com/oauth/access_token userAuthorizationUri: https://www.facebook.com/dialog/oauth tokenName: oauth_token authenticationScheme: query clientAuthenticationScheme: form resource: userInfoUri: https://graph.facebook.com/me
doesn’t work
can you please provide me the source code or source code url
Thanks so much, it very useful and it cut a time I could spend seeking information.
if I click logout button, it seems to work do fine. but 3s later, if i refresh, it automatically login. what is wrong that logic