Wednesday, April 9, 2014

SSO Integration guide with Play-Authenticate




1. Add dependencies


Add below dependencies and the repositories to your play project’s Build.scala file (fully tested with play 2.1)  and build the project. do play eclipse. This will add SSO module, Deadbolt and Play-Authenticate dependencies to your application.


play 2.1.1
"com.redmart.sso.integration"   %  "sso-integration" % "1.0",
"be.objectify" %% "deadbolt-java" % "2.1-RC2",
"com.feth"      %%  "play-authenticate" % "0.3.5-SNAPSHOT"


play 2.2.0
"com.redmart.sso.integration"   %  "sso-integration" % "2.2",
"be.objectify" %% "deadbolt-java" % "2.2-RC4",
"com.feth"      %%  "play-authenticate" % "0.5.0-SNAPSHOT"



Add below repos if they are not already present


resolvers += "maven-repo" at "https://github.com/Redmart/maven-repo/raw/master/",
resolvers += Resolver.url("Objectify Play Repository",     
url("http://schaloner.github.com/releases/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("Objectify Play Snapshot Repository", url("http://schaloner.github.com/snapshots/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("play-authenticate (release)", url("http://joscha.github.com/play-authenticate/repo/releases/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("play-authenticate (snapshot)", url("http://joscha.github.com/play-authenticate/repo/snapshots/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("Objectify Play Repository (release)", url("http://schaloner.github.com/releases/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("Objectify Play Repository (snapshot)", url("http://schaloner.github.com/snapshots/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("play-easymail (release)", url("http://joscha.github.com/play-easymail/repo/releases/"))(Resolver.ivyStylePatterns),
resolvers += Resolver.url("play-easymail (snapshot)", url("http://joscha.github.com/play-easymail/repo/snapshots/"))(Resolver.ivyStylePatterns)




2. Add required plugins with necessary configs


2.1 Add Play Authenticate and Deadbolt plugins to your project’s conf/play.plugins file


10000:be.objectify.deadbolt.java.DeadboltPlugin
9000:service.MyUserServicePlugin
10010:com.feth.play.module.pa.providers.oauth2.google.GoogleAuthProvider



2.2. Create a directory named play-authenticate in conf directory and  add xxx.conf  file with below content.


play-authenticate {
   accountMergeEnabled=false
   accountAutoLink=true


   google {
       redirectUri {
       }


#Google client keys           
clientId="1026436145178-eglfmfarkopfnj823leutk27u0s4c3u7.apps.googleusercontent.com"
     clientSecret="qwoTMbT3xoua_el2Qh3Y-1lI"
   }
}




2.3. After that you need to include that file in your main application.conf file like below.


include "play-authenticate/xxx.conf"


2.4 Add deadbolt handler in your application.conf


deadbolt.java.handler=security.AuthenticationHandler



3. Add Authorizer URL


Add authorizer URL in your application.conf like below, at the end add your application Id which should be defined inside Authorizer.





4. Configure Routes


Add below routes to your applications routes files.



GET     /logout                                      com.feth.play.module.pa.controllers.Authenticate.logout
GET     /authenticate/:provider           com.feth.play.module.pa.controllers.Authenticate.authenticate(provider: String)
GET     /authenticate/:provider/denied     controllers.Application.oAuthDenied(provider: String)
GET     /login                                         controllers.Application.login()



Add root to your home page.


GET     /                                              controllers.SlotDashboard.index()



Application class has to have below methods.



   public static Result login() {
       return ok(login.render());
   }


  public static Result oAuthDenied(final String providerKey) {
       flash(FLASH_ERROR_KEY,
               "You need to accept the OAuth connection in order to use this website!");
       return ok(login.render());
   }




5. Update Global class to use Play-Authenticate and SSO module


5.1 Add below code snippet to your Global class’s onStart method and change according to your landing page.


PlayAuthenticate.setResolver(new Resolver() {


    @Override
    public Call login() {
        return routes.Application.login();
    }


    @Override
    public Call afterAuth() {
        return routes..index();
    }


    @Override
    public Call afterLogout() {
        play.mvc.Controller.response().discardCookie("access_token");
        play.mvc.Controller.session().clear();
        play.mvc.Controller.flash("message", "Logout successfully");
        return routes.Application.login();
    }


    @Override
    public Call auth(final String provider) {
                Cookie token = play.mvc.Controller.request().cookies().get("access_token");
        try {
            if (token != null && token.value() != null && !token.value().isEmpty()) {
                int status = SsoUtil.setUserRoles(token.value());
                if(status == 200){
                    return routes..index();
                }else {
                    return routes.Application.login();
                }
            } else {
        return com.feth.play.module.pa.controllers.routes.Authenticate.authenticate(provider);
                    }
        } catch (Exception e) {
    Logger.error("Exception occurred while validating the token or setting roles : " + e.toString());
        }
        return null;       
    }


    @Override
    public Call onException(final AuthException e) {
        if (e instanceof AccessDeniedException) {
        return routes.Application.oAuthDenied(((AccessDeniedException) e).getProviderKey());
        }
        return super.onException(e);
    }


    @Override
    public Call askLink() {
        return null;
    }


    @Override
        public Call askMerge() {
            return null;
        }
    });



5.2 Set datastore to be used in SSO-module as below inside Global class. I am assuming you are using Morphia for accessing mongo DB.


//Set SSO morphia object which is bundled in the sso-integration jar.


SsoMorphiaObject.morphia = new Morphia();
SsoMorphiaObject.datastore = SsoMorphiaObject.morphia.createDatastore(MorphiaObject.mongo,DB);
SsoMorphiaObject.datastore.ensureIndexes();
SsoMorphiaObject.datastore.ensureCaps();    SsoMorphiaObject.datastore.setDefaultWriteConcern(WriteConcern.UNACKNOWLEDGED);




6. Update Mongo DB


6.1 Add user to your counter collection
db.counters.insert({"collection":"user","c":100})


6.2 Create an acl collection with your local methods and views you need to control with roles.


i.e
db.acl.insert({"method":"controllers.Zones.index","roles":["developer","admin","superadmin"]})
db.acl.insert({"method":"controllers.SubZones.index","roles":["developer","admin","superadmin"]})
db.acl.insert({"method":"views.zones","roles":["developer","admin","superadmin"]})





7. How to control User accesses


7.1 Add below annotation to control your method accesses only for a logged in user. It will validate whether the user is present in the Session if not it will redirect to the login page.



@Security.Authenticated(Secured.class)
public static Result index() {
}


7.2 Add below annotation to control methods based on specific roles.


@Dynamic(value="controllers.Zones.index")
public static Result index() {
}




7.3 Add both annotations if you need both.


@Security.Authenticated(Secured.class)
@Dynamic(value="controllers.Zones.index")
public static Result index() {
}






7.4 Add below annotations inside your scala.html pages to control role based views.


@dynamic("views.zones") {
  • "active">"/zones">Zone
  • }


    This will check whether the logged in user is having required roles to access this method. When you sign in, the relevant roles will be fetched from Authorizer and set it in the user’s session. Whenever the user is trying to access methods, it checks if the required roles are present in the session to access these methods.




    8. Finally update the Login Page


    Add login.scala.html inside your app/views directory with the below content, add google image to your image directory. You can design your login page as you wish and add below code snippet to use google authentication.


    i.e


    @import com.feth.play.module.pa.views.html._



            }








    9. Sample login page.




    This will take you to Google Account chooser page



    You can sign in with whatever account. But once you are authenticated with google, the required roles will be fetched from the Authorizer before rendering the landing page. So based on the roles the application will function accordingly.



    When you logout you will not be logged out from Google, thats the intended behavior.Because signing out from your application should not signed you out from other google applications which you might have already signed in, as an example, Google docs etc. hence if you logout, you will only be logged out from your application, not from Google. Hence in the consequent re-logins, it will not take you to the account chooser, because your details are already cached, if you need to go to the account choose again, you can delete your local cookies in the browser, so that it will take you to the account chooser again.


    2 comments:

    Unknown said...
    This comment has been removed by the author.
    Unknown said...

    how to login pa via REST? can support token?