• Gunther Van Geetsom

  • Software Engineer

For those of you who have been playing around with Spring Social lately, you might have noticed that the Spring Social framework doesn’t have a built-in functionality that makes it possible for the social login flow to be rendered in a popup. Instead, it is always rendered in-page. It is possible, however, to work around that. In this post we will guide you through the steps to configure a working example that opens the login flow in a popup.

As a starting point, we’ll extend the existing quickstart application that can be found at Spring Social Quickstart. You can use this short demo project as a reference.

Front end: Opening the login-flow in a popup

Once you have followed the steps explained in the readme, you should have a working application that renders the login flow in-page.

To customize this, we first have to change the existing button so that it is able to open the login flow in a popup. Because it is not possible to do this without JavaScript, we first add a custom class which will be processed by jQuery:

<button type="button" class="js-social-login" data-social-provider="facebook">
Sign in with Facebook
</button> 

If you look closely, you’ll see the js-social-login class that will be processed by jQuery. We also added data-social-provider; this holds the key of the Spring Social provider as configured in SocialConfig.

The function that will process this button will open a popup to spring-social-quickstart/signin/{provider}/popup, which at the moment doesn’t do anything yet:

function loginSocial( e ) {
    e.preventDefault();
    var triggerElement = $( e.target );
    var __ret = calculatePopupDimensions(); 

    var provider = triggerElement.attr( 'data-social-provider' );
    var url = '/spring-social-quickstart/signin/' + provider + '/popup';
    var specs = 'scrollbars=yes, width=' + __ret.w 
        + ', height=' + __ret.h + ', top=' + __ret.top + ', left=' + __ret.left;
 
   window.open( url, '_blank', specs );
} 

We also need to do some calculations to find out where the popup will be shown; this can be done by calling the calculatePopupDimensions() function. In doing so, the popup will be nicely centered on any type of screen.

At this point we have a button that opens a popup and that shows us a “404 page not found” error. Let’s do something about that.

Back end: Supporting the popup-version

In order to receive the request to spring-social-quickstart/signin/{provider}/popup, we create a controller to handle this request:

@Controller
public class PopUpProviderSignInController {

    private static final String POPUP_VIEW = "socialPopUp";

    @RequestMapping( value = "/signin/{provider}/popup", method = 
      RequestMethod.GET )
    public String openPopUp( Model model, @PathVariable( "provider"
      ) String provider ) {
        model.addAttribute( "provider", provider );
        return POPUP_VIEW;
    }
} 

The controller returns a view that in its turn is responsible for calling the Spring Social ProviderSignController, which only accepts POST requests:

<form th:action="@{/signin/__${provider}__}" method="POST" name="socialSignin">
</form>
<script th:inline="javascript">
    document.forms['socialSignin'].submit()
</script> 

The provider is also supplied in the controller, which received the provider in the first instance from the button itself. By doing so, we have a reusable component that suits every registered Social Provider, exactly because the provider is only set on one place. In our case, on a button.

If you click on the login button, you’ll have the possibility to login at Facebook. But there still remains one problem: If you have logged in successfully, you’ll see that the popup is not automatically closed. To solve this problem we expand the existing SimpleSignInAdapter (which acts as a bridge between Spring Social and our own code):

public final class SimpleSignInAdapter implements SignInAdapter {

  private final UserCookieGenerator userCookieGenerator = new
     UserCookieGenerator();
  public String signIn( String userId, Connection<?> connection,
     NativeWebRequest request )   {

        SecurityContext.setCurrentUser( new User( userId ) );

        userCookieGenerator.addCookie( userId,
        request.getNativeResponse( HttpServletResponse.class ) );

        String provider = connection.getKey().getProviderId();
        return \"/signin/\" + provider + \"/popup/close”;
    }
} 

The signIn method now returns a URL that is called after authenticating at the social provider: /signin/{provider}/popup/close. In the existing PopUpProviderSignInController we add a method to handle this call: 

@RequestMapping( value = "/signin/popup/close", method = 
       RequestMethod.GET )

    public String closePopUp( Model model ) {
        model.addAttribute( "closeWindow", true );
        return POPUP_VIEW;
    } 

The previous code does nothing special except placing an attribute on the model that can be used on the popup screen, as you can see below:

<form th:action="@{/signin/__${provider}__}" method="POST" name="socialSignin">
</form>
<script th:inline="javascript">
    var closeWindow = /**/ null;
    if ( closeWindow ) {
        window.close();
    }
    document.forms['socialSignin'].submit()
</script> 

If you refresh the page, you’ll see that you are successfully logged in. As a result, the Quickstart application will show you your Facebook friends.

If you want to automatically refresh your page after authenticating, you could also add some neat tricks like Window.postMessage(). This will send a message that you can use in your JavaScript code.

With minimal changes, we should now have a working application that opens the login flow in a popup. We’ve changed the existing button to support opening a popup and we have created a custom controller to respond to these events. Also, we have used the hook supplied by Spring Social to make Spring Social aware of our changes by using the SimpleSignInAdapter.

For more information on the demo project, you can find a working sample at our repository on BitBucket. For more Spring Social use cases, see How to Add Multiple Connections to the Same Social Provider.

Verwante Artikels