The Master Class of "Learn Spring Security" is out:
1. Overview
In this tutorial, we’ll focus on adding a new Facebook login to an existing form-login app.
We’re going to be using the Spring Social support to interact with Facebook and keep things clean and simple.
2. Maven Configuration
First, we will need to add spring-social-facebook dependency to our pom.xml:
<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-facebook</artifactId> </dependency>
3. Security Config – Just Form Login
Let’s first start from the simple security configuration where we just have form-based authentication:
@Configuration @EnableWebSecurity @ComponentScan(basePackages = { "org.baeldung.security" }) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/login*").permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/login").permitAll(); } }
We’re not going to spend a lot of time on this config – if you want to understand it better, have a look at the form login article.
4. Security Config – Adding Facebook
Now, let’s add a new way to authenticate into the system – driven by Facebook:
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ConnectionFactoryLocator connectionFactoryLocator; @Autowired private UsersConnectionRepository usersConnectionRepository; @Autowired private FacebookConnectionSignup facebookConnectionSignup; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login*","/signin/**","/signup/**").permitAll() ... } @Bean public ProviderSignInController providerSignInController() { ((InMemoryUsersConnectionRepository) usersConnectionRepository).setConnectionSignUp(facebookConnectionSignup); return new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, new FacebookSignInAdapter()); } }
Let’s carefully look at the new config:
- we’re using a ProviderSignInController to enable the Facebook authentication
- by sending a POST to “/signin/facebook” – this controller will initiate a user sign-in using the Facebook service provider
- we’re setting up a SignInAdapter to handle the login logic in our application
- and we also setting up a ConnectionSignUp to handle signing up users implicitly when they first authenticate with Facebook
5. The Sign-In Adapter
Simply put, this adapter is a bridge between the controller above – driving the Facebook user sign-in flow – and our specific local application:
public class FacebookSignInAdapter implements SignInAdapter { @Override public String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) { SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken( connection.getDisplayName(), null, Arrays.asList(new SimpleGrantedAuthority("FACEBOOK_USER")))); return null; } }
Note that users logged-in using Facebook will have role FACEBOOK_USER, while users logged in using form will have role USER.
6. Connection Sign Up
When a user authenticates with Facebook for the first time, they have no existing account in our application.
This is the point where we need to create that account automatically for them; we’re going to be using a ConnectionSignUp to drive that user creation logic:
@Service public class FacebookConnectionSignup implements ConnectionSignUp { @Autowired private UserRepository userRepository; @Override public String execute(Connection<?> connection) { User user = new User(); user.setUsername(connection.getDisplayName()); user.setPassword(randomAlphabetic(8)); userRepository.save(user); return user.getUsername(); } }
As you can see, we created an account for the new user – using their DisplayName as username.
7. The Facebook Properties
Next, let’s configure Facebook properties in our application.properties:
spring.social.facebook.appId=YOUR_APP_ID spring.social.facebook.appSecret=YOUR_APP_SECRET
Note that:
- We need to create a Facebook application to obtain appId and appSecret
- From Facebook application Settings, make sure to Add Platform “Website” and http://localhost:8080/ is the “Site URL”
8. The Front End
Finally, let’s take a look at our front end.
We’re going to now have support for these two authentication flows – form login and Facebook – on our login page:
<html> <body> <div th:if="${param.logout}">You have been logged out</div> <div th:if="${param.error}">There was an error, please try again</div> <form th:action="@{/login}" method="POST" > <input type="text" name="username" /> <input type="password" name="password" /> <input type="submit" value="Login" /> </form> <form action="/signin/facebook" method="POST"> <input type="hidden" name="scope" value="public_profile" /> <input type="submit" value="Login using Facebook"/> </form> </body> </html>
Finally – here’s the index.html:
<html> <body> <nav> <p sec:authentication="name">Username</p> <a th:href="@{/logout}">Logout</a> </nav> <h1>Welcome, <span sec:authentication="name">Username</span></h1> <p sec:authentication="authorities">User authorities</p> </body> </html>
Note how this index page is displaying usernames and authorities.
And that’s it – we now have two ways to authenticate into the application.
9. Conclusion
In this quick article we learned how to use spring-social-facebook to implement a secondary authentication flow for our application.
And of course, as always, the source code is fully available over on GitHub.