Twitter authentication

Our application is strongly integrated with Twitter, so it seems logical that we would allow authentication through Twitter.

Before going further, make sure that you have enabled Twitter sign in on your app on Twitter (https://apps.twitter.com):

Twitter authentication

Setting up social authentication

Spring social enables authentication through an OAuth provider such as Twitter through a signin/signup scenario. It will intercept a POST request on /signin/twitter. If the user is not known to the UsersConnectionRepository interface, the signup endpoint will be called. It will allow us to take the necessary measures to register the user on our system and maybe ask them for additional details.

Let's get to work. The first thing we need to do is to add the signin/** and /signup URLs as publicly available resources. Let's modify our WebSecurityConfiguration class, changing the permitAll line:

.antMatchers("/webjars/**", "/login", "/signin/**", "/signup").permitAll()

To enable the signin/signup scenario, we also need a SignInAdapter interface, a simple listener that will be called when an already known user signs in again.

We can create an AuthenticatingSignInAdapter class right next to our LoginController:

package masterSpringMvc.authentication;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.UserProfile;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.NativeWebRequest;

@Component
public class AuthenticatingSignInAdapter implements SignInAdapter {

    public static void authenticate(Connection<?> connection) {
        UserProfile userProfile = connection.fetchUserProfile();
        String username = userProfile.getUsername();
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, null);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        System.out.println(String.format("User %s %s connected.", userProfile.getFirstName(), userProfile.getLastName()));
    }

    @Override
    public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
        authenticate(connection);
        return null;
    }
}

As you can see, this handler is called at the perfect time to allow user authentication with Spring Security. We'll come back to that in just a moment. For now, we need to define our SignupController class in the same package, the one in charge of first-time visiting users:

package masterSpringMvc.authentication;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;

@Controller
public class SignupController {
    private final ProviderSignInUtils signInUtils;

    @Autowired
    public SignupController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository connectionRepository) {
        signInUtils = new ProviderSignInUtils(connectionFactoryLocator, connectionRepository);
    }

    @RequestMapping(value = "/signup")
    public String signup(WebRequest request) {
        Connection<?> connection = signInUtils.getConnectionFromSession(request);
        if (connection != null) {
            AuthenticatingSignInAdapter.authenticate(connection);
            signInUtils.doPostSignUp(connection.getDisplayName(), request);
        }
        return "redirect:/profile";
    }
}

First, this controller retrieves the current connection from the session. Then, it authenticates the user through the same method as before. Lastly, it will trigger the doPostSignUp event, which will allow Spring Social to store information relative to our user in the UsersConnectionRepository interface that we mentioned earlier.

The last thing we need to do is add a triumphant "login with twitter" button to our login page, right below the previous form:

<form th:action="@{/signin/twitter}" method="POST" class="center">
    <div class="row">
        <button class="btn indigo" name="twitterSignin" type="submit">Connect with Twitter
            <i class="mdi-social-group-add left"></i>
        </button>
    </div>
</form>
Setting up social authentication

When the user clicks on the CONNECT WITH TWITTER button, they will be redirected to a Twitter sign in page:

Setting up social authentication

Explanation

There isn't much code, but it is a bit tricky to understand all the parts. The first step to getting what's going on is to have a look at the SocialWebAutoConfiguration class of Spring Boot.

The SocialAutoConfigurationAdapter class declared in this class contains the following bean:

@Bean
@ConditionalOnBean(SignInAdapter.class)
@ConditionalOnMissingBean(ProviderSignInController.class)
public ProviderSignInController signInController(
        ConnectionFactoryLocator factoryLocator,
        UsersConnectionRepository usersRepository, SignInAdapter signInAdapter) {
    ProviderSignInController controller = new ProviderSignInController(
            factoryLocator, usersRepository, signInAdapter);
    if (!CollectionUtils.isEmpty(this.signInInterceptors)) {
        controller.setSignInInterceptors(this.signInInterceptors);
    }
    return controller;
}

The ProviderSignInController class will automatically be set up if one ProviderSignInController class is detected in our configuration. This controller is the cornerstone of the sign-in process. Have a look at what it does (I will only summarize the important parts):

  • It will handle the POST /signin/{providerId} from our connect button
  • It will redirect the user to the appropriate sign-in URL of our identification provider
  • It will be notified of the OAuth token by a GET /signin/{providerId} from the identification provider
  • It will then handle the sign in
  • If the user is not found in the UsersConnectionRepository interface, it will use a SessionStrategy interface to store the pending login request and will then redirect to the signupUrl page
  • If the user is found, the SignInAdapter interface is called and the user is redirected to the postSignupUrl page

The two important components of this identification are the UsersConnectionRepository interface in charge of storing and retrieving users from some kind of storage and the SessionStrategy interface that will temporarily store the user connection so it can be retrieved from the SignupController class.

By default, Spring Boot creates an InMemoryUsersConnectionRepository interface for each authentication provider, which means that our user connection data will be stored in memory. If we restart the server, the user will become unknown and will go through the sign-up process again.

The ProviderSignInController class defaults to HttpSessionSessionStrategy, which will store the connection in the HTTP session. The ProviderSignInUtils class that we use in our SignupController class also uses this strategy by default. If we were distributing our application on multiple servers, this would be problematic because the session would likely not be available on every server.

It is easy enough to override these defaults by providing a custom SessionStrategy interface to both the ProviderSignInController and ProviderSignInUtils classes to store data somewhere other than in the HTTP session.

Likewise, we can use another kind of storage for our user connection data by providing another implementation of the UsersConnectionRepository interface.

Spring Social provides a JdbcUsersConnectionRepository interface that will automatically save authenticated users in a UserConnection table in your database. This won't be covered in this book extensively, but you should be able to configure it easily by adding the following bean to your configuration:

@Bean
@Primary
public UsersConnectionRepository getUsersConnectionRepository(
  DataSource dataSource, ConnectionFactoryLocator connectionFactoryLocator) {
    return new JdbcUsersConnectionRepository(
      dataSource, connectionFactoryLocator, Encryptors.noOpText());
}

Note

Check out this article http://geowarin.github.io/spring/2015/08/02/social-login-with-spring.html on my blog for more details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.28.179