facebook-authentication-with-spring-security-and-social
A Secondary Facebook Login with Spring Social
1. Overview
We’re going to be using the Spring Social support to interact with Facebook and keep things clean and simple.
2. Maven Configuration
<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
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
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
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.