Facebook Authentication for Spring Security in Grails
 

grails_logo_plus_facebook

In this article we will use Spring Security Facebook Plugin for Grails to integrate our grails application with facebook. By the end of this article you will be able to login in your grails web application using facebook authentication. To read more about the Spring Security Facebook Plugin for Grails click here.  

Guide to configure Spring Security Facebook Plugin for Grails

Step 1 :

Create a new grails app or use your existing one.
grails create-app grails-facebook-integration-example

Step 2 :

Add Spring Security Facebook Plugin for Grails dependency in your project’s BuildConfig.groovy under plugins. You can get the latest version from here.
compile ":spring-security-facebook:0.17"
Step 3 : Run the following command to configure Spring Security Core in your grails application.
grails s2-quickstart DOMAIN_CLASS_PACKAGE USER_CLASS_NAME ROLE_CLASS_NAME [REQUESTMAP_CLASS_NAME] [--groupClassName=GROUP_CLASS_NAME]
Note: Remember to replace DOMAIN_CLASS_PACKAGE, USER_CLASS_NAME, ROLE_CLASS_NAME and other optional attributes with your domain class package, user class name, role class name etc respectively. i.e :
grails s2-quickstart com.facebook.integration.example User Role
After this step you should have three domain classes created under the package you mentioned in the step. You should also find following configurations in your Config.groovy
grails.plugin.springsecurity.userLookup.userDomainClassName = '<your User domain>' grails.plugin.springsecurity.userLookup.authorityJoinClassName = '<your UserRole domain>' grails.plugin.springsecurity.authority.className = '<your Role domain>' grails.plugin.springsecurity.controllerAnnotations.staticRules = '<your default static rules map>'

 Step 4 :

Create a web application on facebook by following steps here. After following this step successfully you will have App ID and Secret for you facebook app like

create_app

 

Step 5 :

Note : Make sure that you have installed and configured spring-security-core plugin (Step 3) and created a facebook web application with your account (Step 4) before starting this step. Run the following command to configure Spring Security Facebook in your grails application
grails s2-init-facebook
Calling "grails s2-init-facebook" will make default configuration of plugin for you. It will ask if you have a FacebookUser domain class and will automatically create one with the name you specify if you don’t have any. It will ask for your Facebook App ID and Facebook App Secret respectively. Use the App ID and secret provided by the application you created on Facebook in (Step 4). After following this step successfully you should have following configurations added in your Config.groovy
grails.plugin.springsecurity.facebook.domain.classname='<your FacebookUser domain>'
grails.plugin.springsecurity.facebook.secret = '<Facebook secret for your app>'
grails.plugin.springsecurity.facebook.appId = '<Facebook app ID>'

Step 6 :

Now simply create a LoginController and add an action auth in it, also create a auth view under login package in views. Add following code to display the facebook connect button
<sec:ifNotGranted roles="ROLE_USER">
 <facebookAuth:connect />
</sec:ifNotGranted>
<sec:ifAllGranted roles="ROLE_USER">
 Welcome <sec:username/>! (<g:link uri="/j_spring_security_logout">Logout</g:link>)
</sec:ifAllGranted>
Now if you go to http://localhost:8080/${appName}/login/auth you will see a facebook login button. Here ${appName} is your grails application name. Or you can also add the code above in your existing view and you will see the facebook connect button there.  

Step 7:

Open your app you created on facebook by going here goto settings and add your Site URL and save. i.e : http://localhost:8080/${appName} if you are running your grails web application locally. or http://www.example.com if you want to direct to application URL. At this moment if you login in your web application using facebook, you will be able to access basic information about user like Facebook UID etc. The information will be automatically saved in your database tables named same as domain classes created in (Step 3). You can add more configurations to this Spring Security Facebook Plugin, for information about those configurations visit here.  

Guide to joining Spring Social Core & Spring Social Facebook Plugins with Spring Security Facebook Plugin for Grails

You can join Spring Security Facebook Plugin with Spring Social Core and Spring Social Facebook Plugins and extend default plugin behaviour to get information about facebook user like User Name, Email Address, Profile Picture, User Friends List etc. Now in the rest of the article I will show you guys how simple it is to get user facebook details. For this I will use Spring Social Core and Spring Social Facebook Plugins. I will get and save facebook user’s email address and full name. Follow following steps to do so in your grails web application.  

Step 1:

To use the Spring Social Core and Spring Social Facebook plugins add their dependencies in your application’s BuildConfig.groovy under dependencies as
compile 'org.springframework.social:spring-social-core:1.0.1.RELEASE'
compile 'org.springframework.social:spring-social-facebook:1.0.1.RELEASE'

Step 2:

Now add two new variables in your User domain class as
String fullName
String emailAddress

Step 3:

Create a new service in your grails web application named “FacebookAuthService” and add following code in it simply. In this service we will manually create and save facebook user, add its default roles and save his details.
class FacebookAuthService {

    void onCreate(FacebookUser user, FacebookAuthToken token) {
        log.info("Creating user: $user for fb user: $token.uid")
    }

    void afterCreate(FacebookUser user, FacebookAuthToken token) {
        log.info("User created: $user for fb user: $token.uid")
    }

    Map onJsonSuccess(Map input, FacebookAuthToken token) {
        input['demo'] = 'An message from FacebookAuthService'
        return input
    }

    Map onJsonFailure(Map input, AuthenticationException exception) {
        StackTraceElement stack = exception.stackTrace.last()
        input['stacktrace'] = [
                line: stack.lineNumber,
                classname: stack.className
        ]
        return input
    }

    /**
     * Called when we have a new facebook user, called on first login to create all required
     * data structures. Replaces .createAppUser and .createRoles methods.
     *
     * @param token facebook authentication token
     */
    FacebookUser create(FacebookAuthToken token) {
        log.info("Create domain for facebook user $token.uid")

        //Use Spring Social Facebook to load details for current user from Facebook API
        Facebook facebook = new FacebookTemplate(token.accessToken.accessToken)
        FacebookProfile fbProfile = facebook.userOperations().userProfile
        String email = fbProfile.email
        String username = fbProfile.username
        String firstName = fbProfile.firstName
        String lastName = fbProfile.lastName

        User person = new User(
                username: username,
                password: token.accessToken.accessToken, //not really necessary
                enabled: true,
                accountExpired:  false,
                accountLocked: false,
                passwordExpired: false,

                //fill with data loaded from Facebook API
                name: [firstName, lastName].join(' '),
                email: email
        )
        person.save()
        UserRole.create(person, Role.findByAuthority('ROLE_USER'))
        UserRole.create(person, Role.findByAuthority('ROLE_FACEBOOK'))
        FacebookUser fbUser = new FacebookUser(
                uid: token.uid,
                accessToken: token.accessToken.accessToken,
                accessTokenExpires: token.accessToken.expireAt,
                user: person
        )
        fbUser.save()
        return fbUser
    }

    // ********************************************************************************************
    //
    // You can remove X_ prefix from following methods, if you need some logic specific for your app
    //
    // ********************************************************************************************

    /**
     * !!! remove X_ to use this method
     *
     * Called when facebook user is authenticated (on every request), must return existing
     * instance for specified facebook uid, if exits. If doesn't - return null
     *
     * @param uid facebook user id
     */
    FacebookUser X_findUser(Long uid) {
        log.info("Search for facebook user with id $uid")
        return FacebookUser.findByUid(uid)
    }

    /**
     * !!! remove X_ to use this method
     *
     * Called when we have a new facebook user, called on first login to create main app User domain (when
     * we store Facebook User details in own domain)
     *
     * @param token facebook authentication token
     */
    User X_createAppUser(FacebookUser user, FacebookAuthToken token) {
        log.info("Create app user for facebook user $token.uid")
        User person = new User(
                username: "test_$token.uid",
                password: '********',
                enabled: true,
                accountExpired:  false,
                accountLocked: false,
                passwordExpired: false
        )
        person.save(failOnError: true)
        return person
    }

    /**
     * !!! remove X_ to use this method
     *
     * Called when we have a new facebook user, called on first login to create roles list for new user
     *
     * @param user facebook user
     */
    void X_createRoles(FacebookUser user) {
        log.info("Create role for facebook user $user.uid")
        UserRole.create(user.user, Role.findByAuthority('ROLE_USER'))
        UserRole.create(user.user, Role.findByAuthority('ROLE_FACEBOOK'))
    }

    /**
     * !!! remove X_ to use this method
     *
     * Must returns object to store in security context for specified facebook user (can return itself)
     *
     * @param user facebook user
     */
    def X_getPrincipal(FacebookUser user) {
        log.info("Ger principal for facebook user #$user.id")
        return user.user
    }

    /**
     * !!! remove X_ to use this method
     *
     * Must return roles list for specified facebook user
     *
     * @param user facebook user
     */
    Collection<GrantedAuthority> X_getRoles(FacebookUser user) {
        log.info("Ger roles for facebook user #$user.id")
        return user.user.authorities.collect {
            new GrantedAuthorityImpl(it.authority)
        }
    }

    /**
     * !!! remove X_ to use this method
     *
     * Must return roles list for specified facebook user
     *
     * @param user facebook user
     */
    void X_prepopulateAppUser(User user, FacebookAuthToken token) {
        log.info("Prepopulate app user")
        user.password = '*******'
        user.username = "test_$token.uid"
        user.accountExpired = false
        user.accountLocked = false
        user.enabled = true
        user.passwordExpired = false
    }

}
Now your application is able to access and save facebook user’s full name and email address. We hope that you find this article helpful, please feel free to post any questions or suggestions in the comments section below.


Tags: , , , , , ,