5. Signing in with Service Provider Accounts

5.1 Introduction

Once a connection has been established between a user's consumer account and their service provider profile, that connection can be used to authenticate them to the consumer application by asking them to sign in to the service provider. Spring Social supports such service provider-based authentication with Twitter and Facebook.

5.2 Sign in with Twitter

Spring Social's TwitterSigninController is a Spring MVC controller that processes the "Sign in with Twitter" flow described at http://dev.twitter.com/pages/sign_in_with_twitter. Essentially, this process is an OAuth 1 authorization flow, quite similar to the flow that ConnectController processes for connecting an account with Twitter. The key difference is that instead of presenting the user with an authorization page that asks for permission to be granted to the application, the sign in flow presents a simple authentication page that only asks the user to sign in to Twitter.

At the end of process, TwitterSigninController attempts to find a previously established connection and uses the connected account to authenticate the user to the application.

To add "Sign in with Twitter" capability to your Spring application, configure TwitterSigninController as a bean in your Spring MVC application:

<bean class="org.springframework.social.twitter.web.TwitterSigninController">
    <constructor-arg ref="twitterProvider" />
    <constructor-arg ref="connectionRepository" />
    <constructor-arg ref="signinService" />
    <constructor-arg value="http://localhost:8080/myapplication" />
</bean>
		

TwitterSigninController is constructed with four arguments:

  • A reference to a TwitterServiceProvider bean. TwitterSigninController will use this to negotiate the connection with Twitter.

  • A reference to a connection repository bean. After signing into Twitter, TwitterSigninController will use the connection repository to find an account ID that is connected to the Twitter profile.

  • A reference to an implementation of SignInService, used to perform the actual authentication into the application.

  • The application's base URL, used to construct a callback URL for the OAuth flow.

TwitterSigninController's constructor is annotated with @Inject, so it's not necessary to explicitly wire any of its arguments except for the application URL. Optimizing the configuration for autowiring, the TwitterSigninController bean looks like this:

<bean class="org.springframework.social.twitter.web.TwitterSigninController">
    <constructor-arg value="http://localhost:8080/myapplication" />
</bean>
		

TwitterSigninController supports the following flow:

  • POST /signin/twitter - Initiates the "Sign in with Twitter" flow, fetching a request token from Twitter and redirecting to Twitter's authentication page.

  • GET /signin/twitter?oauth_token={request token}&oauth_verifier={verifier} - Receives the authentication callback from Twitter, accepting a verification code. Exchanges this verification code along with the request token for an access token. It uses this access token to lookup a connected account and then authenticates to the application through the sign in service.

    • If the received access token doesn't match any existing connection, TwitterSigninController will redirect to a signup URL. The default signup URL is "/signup" (relative to the application root).

TwitterSigninController handles the authentication flow with Twitter, but relies on an implementation of SignInService to perform the actual authentication into the application. SignInService is defined as follows:

public interface SignInService<T extends Serializable> {
    void signIn(T accountId);	
}
		

Different applications will implement security differently, so each application must implement SignInService in a way that fits its unique security scheme. As an example, suppose that an application's security is based Spring Security and simply uses a user's account ID as their principal. In that case, a simple implementation of SignInService might look like this:

package org.springframework.social.showcase;

import java.io.Serializable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.web.signin.SignInService;

public class AccountIdAsPrincipalSigninService implements SignInService<Long> {
    public void signIn(Long accountId) {
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(accountId, null, null));
    }
}
		

The last thing to do is to add a "Sign in with Twitter" button to your application:

<form id="tw_signin" action="<c:url value="/signin/twitter"/>" method="POST">
    <button type="submit"><img src="<c:url value="/resources/social/twitter/sign-in-with-twitter-d.png"/>" /></button>
</form>
		

Clicking this button will trigger a POST request to "/signin/twitter", kicking off the Twitter sign in flow. If the user has not yet signed into Twitter, the user will be presented with the following page from Twitter:

After signing in, the flow will redirect back to the application to complete the sign in process.

If the user has already signed into Twitter prior to clicking the sign in button, Twitter will redirect the flow back to the application without presenting the user with a sign in page.

5.3 Sign in with Facebook

Spring Social's FacebookSigninController, when paired with Facebook's <fb:login-button> XFBML tag[3], enables a user to authenticate to an application by first signing into Facebook.

Facebook's XFBML tag collection includes <fb:login-button>, which displays a login button. When a user clicks on that button, they will be presented with a Facebook Login dialog that looks similar to this:

After successfully signing into Facebook, a cookie will be written that contains an access token. If the user has previously established a connection between their account and their Facebook profile, this access token will be the same token that was issued before. FacebookSigninController works by extracting that access token from the cookie and using it to lookup a connected account and authenticate to that account.

To enable "Sign in with Facebook" functionality in your application, start by configuring FacebookSigninController as a bean in the Spring MVC configuration:

<bean class="org.springframework.social.facebook.web.FacebookSigninController">
    <constructor-arg ref="facebookProvider" />
    <constructor-arg ref="connectionRepository" />
    <constructor-arg ref="signinService" />
</bean>
		

As with TwitterSigninController, FacebookSigninController depends on a connection repository to lookup connections and a sign in service to handle the actual application authentication. It also needs a reference to a FacebookServiceProvider bean that it will use to negotiate the connection with Facebook.

FacebookSigninController's constructor is annotated with @Inject, so it is not necessary to explicitly wire these dependencies. The FacebookSigninController configuration optimized for autowiring takes a simpler form:

<bean class="org.springframework.social.facebook.web.FacebookSigninController"/>
		

However, since Facebook's sign in process is not based on an OAuth 1 flow, FacebookSigninController does not need the base URL to create a callback URL from. Instead, FacebookSigninController needs to know the application's Facebook app id and secret that were issued to the application when it was first registered in Facebook. It uses the app id to find the cookie and the application secret to verify the authenticity of the cookie by calculating and comparing signatures.

With FacebookSigninController configured, the next thing to do is to load and initialize Facebook's JavaScript library and XFBML in your application's sign in page. Spring Social provides a convenient JSP tag to handle that work for you. Simply declare the Spring Social Facebook tag library in your JSP:

<%@ taglib uri="http://www.springframework.org/spring-social/facebook/tags" prefix="facebook" %>
		

Then use the <facebook:init> tag:

<facebook:init appId="@facebookProvider.appId" />
		

Note that the tag prefix cannot be "fb", as that would create a conflict between Spring Social's Facebook JSP tag library and Facebook's XFBML tags (which use "fb" as their prefix).

The <facebook:init> will initialize the Facebook JavaScript library with your application's App ID. This is the appId assigned to your application when you registered it with Facebook. Here the App ID is specified using the Spring Expression Langauge to be the value of the appId property of the bean whose ID is "facebookProvider".

Now that the Facebook JavaScript API is initialized you just need to add the <fb:login-button> to the page:

<form id="fb_signin" action="<c:url value="/signin/facebook"/>" method="POST">
    <div id="fb-root"></div>
    <p><fb:login-button onlogin="$('#fb_signin').submit();" v="2" length="long">Signin with Facebook</fb:login-button></p>
</form>
		

Notice that here <fb:login-button> is within a form that submits to "/signin/facebook" (the URL that FacebookSigninController handles). Although it's not strictly required that <fb:login-button> be within this form, the form itself is necessary to trigger FacebookSigninController. <fb:login-button> doesn't directly submit this form, so it doesn't matter whether or not it is in the form.

<fb:login-button>'s onlogin attribute indicates what action should be taken upon successful Facebook login. In this case, onlogin is set to use jQuery to submit the form, triggering FacebookSigninController to authenticate the user to the application.

5.4 Signing up after a failed sign in

With both TwitterSigninController and FacebookSigninController, the flow will redirect to a signup page if no connection can be found for the obtained access token. By default, the signup URL is "/signup", relative to the application root. You can override that default by setting the signupUrl property on the controller. For example, the following configuration of TwitterSigninController sets the signup URL to "/newUser":

<bean class="org.springframework.social.twitter.web.TwitterSigninController">
    <constructor-arg value="http://localhost:8080/myapplication" />
    <property name="signupUrl" value="/newUser" />
</bean>
		

After the user has successfully signed up in your application, you can complete the connection between the provider and the newly created account by calling ProviderSignInUtils.handleConnectPostSignUp():

ProviderSignInUtils.handleConnectPostSignUp(accountId, request);