Spring Boot in Action Part 3 : Social Login

This article is part3 of the spring boot in action series. Check out the previous parts Part1, Part2. The source code for all parts is available at this github repo.

We continue the series by implementing our first feature. Social Login via twitter.

The Social Login Flow

Before we dive into how to implement social login in our application, we need to visualize how the flow works. The diagram below gives a high level overview.

  • The flow begins when a user either visits the login page or tries to access any page that requires authentication.

  • The login page would give the user links to sign in with his prefered social provider (in our case twitter).

  • The user is redirected to the social provider's sign in page which in turn will redirect him to a permissions page to grant our app the required permissions (usually very limited permissions are asked for just doing sign in with).

  • The user is then redirected back to the application. There are two alternative flows that can be implemented in this step:

    1. Do an implicit signup in your application. In this approach your application creates an account for the user without asking him for extra information (We will be using this approach).
    2. Redirect the user to a signup page which will pre populate some of his information from the social login and leave extra information (like password etc.) to be filled by the user.

    Which approach to choose depends on the requirements of the app. Mainly whether the application requires more information about the user than what can be obtained through the social login and whether users are allowed to login with both a normal username and password as well as with social login. Another important factor is how many social providers are allowed for the application, if this is true and you are implementing implicit signup, the application will need to pad the usernames of the users as there could be username collisions between the various providers.

  • The final step of the flow is to redirect the user to the page that he was trying to access.

Implementing Social Login for Kotoby

The flow described above looks long and complicated and you dear reader might be dreading the amount of code required to implement it especially in Java. Luckily this is not as hard as it looks by using spring-social http://projects.spring.io/spring-social/. First and foremost since we will be using twitter as our social login provider, head over to http://dev.twitter.com and create an application and take note of the "API key" and "API secret" as we will need this information later on.

Required Dependencies

Just add the following dependencies to our pom.xml.

<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-web</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-config</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-twitter</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency>

Spring Security Configuration

Since we are using spring-boot our application has been secured by the default auto configuration of spring-security. We are going to change that now. All we need to do is to create a class which extends WebSecurityConfigurerAdapter and annotate it with @Configuration and @EnableWebSecuirty. Next we need to override some methods to finalize our configuration.

@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth. jdbcAuthentication() .dataSource(dataSource) .withDefaultSchema(); }

The code above tells spring-security to use JDBC authentication with the provided dataSource and with the default spring-security schema. You don't need to worry about creating the tables for this schema as the auto configuration of spring boot will initialize these tables for us.

Now we go deeper and configure the details.

@Override protected void configure(HttpSecurity http) throws Exception { http .formLogin() .loginPage("/login") .loginProcessingUrl("/login/authenticate") .failureUrl("/login?param.error=bad_credentials") .permitAll() .and() .authorizeRequests() .antMatchers("/favicon.ico", "/static-resources/**").permitAll() .antMatchers("/**").authenticated() .and() .rememberMe() .and() .apply(new SpringSocialConfigurer()); }

formLogin() tells spring-security to enable form login rather than the default basic authentication. loginPage("/login") specifies that the login page should be at /login whenever a user tries to access a restricted resource he will be redirected to this page. I won't go into the details of each part of the dsl as they are self explanatory and you can find out more from the respective spring-security documentation. The main point to note here is the SpringSocialConfigurer which is provided by spring-social to integrate into spring-security.

For spring-social to work it requires a SocialUserDetailsService bean to be configured. We will implement this by first creating the class SimpleSocialUsersDetailService and configuring it as a been in our SecuirtyConfiguration class. The code listing below shows this configuration

@Bean
public SocialUserDetailsService socialUsersDetailService() {
    return new SimpleSocialUsersDetailService(userDetailsService());
}

Spring Social Configuration

To configure spring-social we will need to create a class let's call it SocialConfig. As usual we need to annotate with @Configuration to tell spring this is a configuration class. We will also annotate it with @EnableSocial. This class will implement the SocialConfigurer interface. This interface has three methods to implement:
@Override public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) { connectionFactoryConfigurer.addConnectionFactory(new TwitterConnectionFactory( environment.getProperty("twitter.consumerKey"), environment.getProperty("twitter.consumerSecret"))); }

This method allows us to configure the social providers that we require and pass them the information. Notice the properties "twitter.consumerKey" and "twitter.consumerSecret" these are the keys that we got from twitter for our app. To wire those we need to add them to application.properties.
twitter.consumerKey=your-api-key twitter.consumerSecret=your-api-secret

The next method we will need to implement is
@Override public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText()); repository.setConnectionSignUp(new AccountConnectionSignUpService(usersDao)); return repository; }

This method configures the persistense of spring-social data. We opted for JdbcUsersConnectionRepository to persisted the connection in a relation database. Notice the setConnectionSignUp() method, this configures the implicit signup that we explained in the social flow section earlier in the post.

Are we done yet?

The main configurations that we need are now in place. However, there are some supporting classes that we need to implement to get everything up and running. These include implementing Controllers and templates for our pages. To keep this post as brief as possible I advice the reader to check the completed code for this part here

Wrap Up

I personally think the experience of implementing social login in Java is kinda sub optimal. Libraries such as spring-social do help a lot in this regard but yet it is a non trivial effort.

For any questions regarding this part please sound off in the comments or via twitter @miosman.

Mohamed Osman

Read more posts by this author.