Lots of webmasters love to give their users the option to login with either Facebook or Twitter. Fortunately, you can add social media login support to your Spring Boot app.

That’s because Spring offers a project called Spring Social that enables you to add social media login options to your app. Even better: those login solutions integrate nicely with Spring Security.

Wait a second. Didn’t we already cover a Facebook login solution and a Twitter login solution?

Why, yes. Yes we did. But those solutions didn’t integrate with Spring Security. They used external libraries and are suitable options if, for whatever reason, you don’t want to use Spring Security.

In this article, we’ll go over how to enable your users to login with either Facebook or Twitter. If you want to see the complete code for this project, have a look on GitHub.

 

Create the Apps

Before you can offer your users a Facebook/Twitter login solution, you need to create an application for both platforms.

There’s no need to reinvent the wheel. We’ve already covered how to create a Facebook app and a Twitter app in the past. Feel free to browse those articles if you need to.

 

Set the Properties

Once you’ve created your social media applications, you need to give your Spring Boot app permission to communicate with them. You’ll do that by adding properties in the application.properties file.

Behold:

1
2
3
4
spring.social.facebook.appId=[your Facebook app ID]
spring.social.facebook.appSecret=[your Facebook app secret]
spring.social.twitter.appId=[your Twitter app ID[
spring.social.twitter.appSecret=[your Twitter app secret]

Obviously, you’ll want to update the code above with your own credentials.

 

Add the Necessary Dependencies

You’re going to need quite a few dependencies for this one. Expect your JAR file footprint to be large.

For starters, you’ll need the Spring Social dependencies for both Facebook and Twitter. In addition to that, you’ll need related Spring Security dependencies as well.

Here’s what the list of dependencies should look like your POM file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-facebook</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-twitter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

 

Create the User Class

Next, create a User class to represent the user that logs in via Facebook or Twitter.

Keep it simple for now by just including a single field that stores the user’s display name. Later on, you can add other fields (such as address, phone number, etc.).

Here’s the very simple User class:

1
2
3
4
5
6
7
8
9
10
11
public class User {
 
    private String name;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

Configure the MVC Views

Next, it’s time to configure a few views. For the purposes of this demo, you’ll just need 3 of them: a home page, a login page, and a successful login page.

The home page is the “front end” of your website. That’s where you market your brand, so you’ll let visitors view the home page even if they’re not logged in.

The login page allows the user to login with either Facebook or Twitter.

The successful login page is a simple “Welcome!” page that confirms to the user that the login attempt was successful.

Here’s what the code looks like for configuring the views.

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/socialloginhome").setViewName("socialloginhome");
        registry.addViewController("/").setViewName("socialloginhome");
        registry.addViewController("/socialloginsuccess").setViewName("socialloginsuccess");
        registry.addViewController("/sociallogin").setViewName("sociallogin");
    }
 
}

 

Configure Security

Fasten your seat belts. Things are about to get quite a bit more complicated.

The next step is to configure web security. You’ll do that in a class that extends WebSecurityConfigurerAdapter.

First, override the configure() method to define site-wide security. That code looks like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/", "/socialloginhome", "/assets/**", "/images/**","/login*","/signin/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/sociallogin")
            .permitAll()
            .and()
        .logout()
            .permitAll();
}

As you can see, right out of the gate the code is disabling cross-site forgery requests with the csrf().disable() code. Yes, that opens up some security issues, but it’s necessary when you need to use a third-party API.

In the authorizeRequests() block, the code is essentially establishing permissions. The first antMatchers() line specifies all the URLs that visitors can access without logging in. Note that URL wildcards like “/images/**” and “/assets/**” are included because they contain images, CSS code, and JavaScript libraries that you want all users to access regardless of login status.

The formLogin() block is fairly self-explanatory. It establishes the URL for the form login and allows all visitors to access it.

Finally, logout() is also universally allowed.

 

The ProviderSignInController

In addition to specifying HTTP security, you also need to use Spring Social’s ProviderSignInController to enable social logins. Here’s what that code looks like:

1
2
3
4
5
6
7
8
9
10
@Bean
public ProviderSignInController providerSignInController() {
    ((InMemoryUsersConnectionRepository) usersConnectionRepository)
      .setConnectionSignUp(socialConnectionSignup);
      
    return new ProviderSignInController(
      connectionFactoryLocator,
      usersConnectionRepository,
      new SocialSignInAdapter());
}

First of all notice, that the method is annotated with @Bean. That’s because the object returned by the method is a bean managed by the Spring container.

That first line of code in the method references a couple of autowired objects. In a nutshell, that line of code specifies the object that will handle user signups. That’s where you’ll instantiate the User object once the user logs in.

The next line instantiates the ProviderSignInController itself with the constructor that takes 3 parameters.

The first parameter is an autowired ConnectionFactoryLocator object. That code determines what type of ConnectionFactory (i.e., Facebook or Twitter) should be used when the user logs in.

The second parameter, a type of UserConnectionFactory, is also autowired. That object is used to manage user connections to service providers.

The third parameter is a class that you’ll have to write. It’s the bridge between this controller and the code that handles application-specific login.

Keep in mind that ProviderSignInController lives up to its name. It’s really a controller class.

 

Utility Classes

Before coding the adapter and signup object, create a couple of utility classes that will make your life easier.

First, create ConnectionHelper. That class will expose one method used to determine whether the user is logging in with Facebook or Twitter.

Here’s what that code looks like:

1
2
3
4
5
6
7
8
9
10
11
public static ConnectionType getConnectionType(Connection<?> connection) {
    Object api = connection.getApi();
     
    if (api instanceof Twitter) {
        return ConnectionType.TWITTER;
    } else if (api instanceof Facebook) {
        return ConnectionType.FACEBOOK;
    } else {
        throw new RuntimeException("Unknown API!");
    }
}

Note that the method returns ConnectionType. That’s a simple enum that identifies the connection type as either a Facebook connection or a Twitter connection.

To determine the connection type, the code grabs the API object from the Connection. Then, it just evaluates the interface and returns the appropriate value.

The next utility class you should create is UserHelper. That will populate the User object based on information gleaned from the connection.

Here’s what that code looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Component
public class UserHelper {
     
    /**
     * Instantiates a User object based on login
     *
     * @param connection
     * @return user object
     */
    public User getUser(Connection<?> connection) {
        User user = null;
 
        //get the connection type
        ConnectionType type = ConnectionHelper.getConnectionType(connection);
         
        //create a user based on API type
        if (type.equals(ConnectionType.TWITTER)) {
            user = getTwitterUser(connection);
        } else if (type.equals(ConnectionType.FACEBOOK)) {
            user = getFacebookUser(connection);
        }
         
        return user;
    }
     
     
    /**
     * Handles users who've logged in via Twitter
     */
    private User getTwitterUser(Connection<?> connection) {
        User user = new User();
        Twitter twitterApi = (Twitter)connection.getApi();
         
        String name = twitterApi.userOperations().getUserProfile().getName();
         
        user.setName(name);
         
        return user;
    }
     
     
    /**
     * Handles users who've logged in via Facebook
     */
    private User getFacebookUser(Connection<?> connection) {
        User user = new User();
        Facebook facebookApi = (Facebook)connection.getApi();
         
        String name = facebookApi.userOperations().getUserProfile().getName();
         
        user.setName(name);
         
        return user;
    }
}

The getUser() method accepts a Connection object. It uses that object to determine the connection type and then populates the User object accordingly.

Note that there are two methods for populating the User object: one for Facebook and one for Twitter. They look strikingly similar, so you might think that they can be combined.

However, the object models are different for each API. It’s best to leave them separate if you plan on getting API-specific info for your users.

 

The Adapter

Next, create the adapter class that handles social media logins. Here’s what that code should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@Service
public class SocialSignInAdapter implements SignInAdapter {
     
    @Override
    public String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
        //get the authentication token
        Authentication authentication = getAuthentication(localUserId, connection);
         
        //put the authentication token in the context
        SecurityContextHolder.getContext().setAuthentication(authentication);
         
        //return the "success" page
        return "/socialloginsuccess";
    }
     
     
    /**
     * Creates the token for an authentication request
     *
     * @param localUserId
     * @param connection
     * @return token for an authentication request
     */
    private Authentication getAuthentication(String localUserId, Connection<?> connection) {
        //get the roles
        List<GrantedAuthority> roles = getRoles(connection);
        
        //no need for password here because the user logged in with social media
        String password = null;
         
        //instantiate the authentication object
        Authentication authentication = new UsernamePasswordAuthenticationToken(localUserId, password, roles);
         
        //get out
        return authentication;
    }
     
     
    /**
     * Returns a List of roles. For this demo, there's only one role.
     * The role is defined by how the user logged in (Facebook or Twitter)
     *
     * @param connection
     * @return list of roles
     */
    private List<GrantedAuthority> getRoles(Connection<?> connection) {
        //start with an empty list
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
         
        //get the connection type
        ConnectionType type = ConnectionHelper.getConnectionType(connection);
         
        //set the role based on the type
        String role = type.toString();
         
        //add the role to the list
        roles.add(new SimpleGrantedAuthority(role));
         
        //get out
        return roles;
    }
}

The signIn() method is where everything happens. That’s the implemented method from the interface.

First, the method constructs an Authentication object. We’ll look at that code in a bit.

Next, the code adds the Authentication object to the security context. That’s where the user officially becomes part of the family.

Finally, the method returns “/socialloginsuccess.” That’s the URL path for a successful login.

The getAuthentication() method constructs an Authentication object. In this case, it’s an instance of UserNamePasswordAuthenticationToken.

First, the code gets a List of roles for the user. Here, that List only contains one item. Feel free to expand that list based on your own requirements, though.

The next line of code sets the password to null. A password isn’t needed here because the user logged in via social media.

Finally, the code instantiates the Authentication object with the user’s ID, null password, and List of roles.

The last method in the code block above determines how the user logged in (with Facebook or Twitter) and then sets the role accordingly.

 

The ConnectionSignUp Class

When a user logs in via Facebook or Twitter, you’re almost certainly going to want to do something with that user information. You’ll probably want to persist it.

It’s beyond the scope of this tutorial to get into persistence, but you’ll see enough here to at least get the ball rolling.

Take a look at the SocialConnectionSignup class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service
public class SocialConnectionSignup implements ConnectionSignUp {
 
    @Autowired
    UserHelper userHelper;
 
    /**
     * This is where you would create the user object and persist it
     */
    @Override
    public String execute(Connection<?> connection) {
        //create the user based on the API type (Facebook or Twiter)
        User user = userHelper.getUser(connection);
         
        //return the user name
        return user.getName();
    }
}

As you can see, it’s autowiring the UserHelper class that you created above. It will use that class to create a User object.

The only method in the SocialConnectionSignup class is execute(). That’s where the User object is created with the help of UserHelper.

Finally, the method returns the user’s name.

 

The Web Pages

Now that you’ve got the underlying code in place, it’s time to create the front end.

First, create the home page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
    <div th:include="fragments/topmenuheader :: header"></div>
    <div class="page-container">
        <div class="page-content-wrapper">
            <div class="page-head" style="background-color:#eff3f8;padding-top:40px">
                <div class="container">
                    <div class="row">
                        <div class="col-md-6 ">
                            <h1>Welcome to Our Website!</h1>
 
                            <h4 style="margin-bottom:200px">Click <a th:href="@{/sociallogin}">here</a> to login.</h4>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div th:include="fragments/homefooter :: footer"></div>
</body>

That’s nothing special. It’s just a “Welcome” message with a link so the visitor can login.

Here’s what the login page looks like:

1
2
3
4
5
6
7
8
9
10
<div class="form-group">
    <div style="padding-bottom:10px;padding-left:15px">
        <form action="/signin/facebook" id="facebookSignin" method="POST" style="margin-top:30px">
            <a href="javascript:$('#facebookSignin').submit();" class="btn btn-primary"><i class="fa fa-facebook"></i> Login with Facebook </a>
        </form>
        <form action="/signin/twitter" id="twitterSignin" method="POST" style="margin-top:30px;margin-bottom:20px">
            <a href="javascript:$('#twitterSignin').submit();" class="btn btn-primary"><i class="fa fa-twitter"></i> Login with Twitter </a>
        </form>
    </div>
</div>                     

As you can see, there are two forms: one for Facebook login and one for Twitter login.

Each form contains only a single button. In the first form, it’s a button that enables the user to login with Facebook. In the second form, it’s a button that enables the user to login with Twitter.

Note that each form has a different action. The action for the first form is “/signin/facebook” and the action for the second form is “/signin/twitter.”

You’ll have to leave that alone for now. If you want to change those actions, you need to make some changes to the ProviderSignInController class.

That controller class “listens” for URLs that start with “/signin.” Then, it evaluates the next component in the path (“/facebook” or “/twitter”) and handles the request appropriately.

Finally, create a “successful login” page:

1
2
3
4
5
6
<form th:action="@{/logout}" method="post">
    <div class="form-body" style="margin-bottom:200px">
        <h2 th:inline="text">Congrats! You're logged in as !</h2>
        <input style="margin-top:30px" type="submit" class="btn blue" value="Logout"/>
    </div>
</form>

That code is doing is just spitting out a message that congratulates the user for logging in. It also allows the user to logout.

 

Testing It Out

Now that the coding is done, it’s time to give the girl a whirl.

Fire up the Spring Boot app and visit its root URL. All the Spring Boot apps around these parts, unless otherwise stated, use port 8090. So the root URL looks like this:

http://localhost:8090

When you visit that URL, you should see the welcome page.

welcome-page

 

Click the link and you should be taken to a login page that looks like this:

login-page

 

Click either one of those buttons, wait a few seconds for the login process to complete, and you should eventually see this:

congrats-page

 

Of course, your name might be different.

 

Wrapping It Up

Well done! You’ve created a Spring Boot that supports login from either Facebook or Twitter. You’ve effectively given your website SSO-like capabilities.

Remember, all the code isn’t covered in this article. If you want to see it, feel free to visit the GitHub repository.

Feel free to tinker with the code and update it to suit your own business requirements.

Have fun!