Skip to content

Step 9 authentication

opensas edited this page Nov 3, 2011 · 8 revisions

Step 9 - authentication

Purpose: in this step we will see how our user can authenticate themselves using facebook or twitter.

cd ~/devel/apps
git clone git://github.com/opensas/play-demo.git
cd play-demo
git checkout origin/09-authentication

Register your app at twitter

First of all you'll need to register your app at twitter. Go to https://dev.twitter.com/apps, sign in with your twitter account, click on Create a new application

Name: play-demo

Description: Play framework demonstration app

Web site: play-demo.appspot.com

Callback url: http://127.0.0.1/oautht

For more info about signing in with your twitter account see here.

After this, you'll see a page with the following info

Consumer key         kkkkkkkkkkkkkkkkkk
Consumer secret      ssssssssssssssssssssssssssssss
Request token URL    https://api.twitter.com/oauth/request_token
Authorize URL        https://api.twitter.com/oauth/authorize
Access token URL     https://api.twitter.com/oauth/access_token

Now go to your application.conf and create the following entries:

[...]
twitter.requestTokenURL=http://twitter.com/oauth/request_token
twitter.accessTokenURL=http://twitter.com/oauth/access_token
twitter.authorizationURL=http://twitter.com/oauth/authenticate
twitter.consumerKey=kkkkkkkkkkkkkkkkkk
twitter.consumerSecret=ssssssssssssssssssssssssssssss

Create the user model

Now we'll create a new User model to store user credentials

model.User

@Entity
public class User extends Model {
    public String name;
    public String avatarUrl;

    // Twitter
    public String token;
    public String secret;

    // Facebook
    public String accessToken;
}

add routes

Add the following routes to the routes file

conf/routes

[...]
#
# Authentication
#
GET     /oauthtw                                Secure.oauthTwitter
GET     /oauthfb                                Secure.oauthFacebook
GET     /login                                  Secure.login
GET     /logout                                 Secure.logout

Create the Secure controller

Now we'll create the controller that will take care of interacting with twitter and facebook

controllers.Secure

public class Secure extends Controller {

	private final static String USER_COOKIE = "user";
	
	private final static OAuth.ServiceInfo TWITTER = new OAuth.ServiceInfo(
		Play.configuration.getProperty("twitter.requestTokenURL"),
		Play.configuration.getProperty("twitter.accessTokenURL"),
		Play.configuration.getProperty("twitter.authorizationURL"),
		Play.configuration.getProperty("twitter.consumerKey"),
		Play.configuration.getProperty("twitter.consumerSecret")
	);

	public static void login() {
		render();
	}
	
	public static void logout() {
		session.clear();
		Application.list();
	}
}

We will add the action that will authentication with twitter

	public static void oauthTwitter() {

		// first time the request comes here
		// the user has just pushed the "sign in with twitter button"
		if (!OAuth.isVerifierResponse()) {
			final OAuth twitter = OAuth.service(TWITTER);
			final OAuth.Response response = twitter.retrieveRequestToken();
			if (response.error==null) {
				final User user = new User();
				user.token = response.token;
				user.secret = response.secret;
				user.save();
				session.put("userId", user.id);
				redirect(twitter.redirectUrl(response.token));
			} else {
	            Logger.error("Error contacting twitter: " + response.error);
	            login();
	        }
			
		// the user has been redirected by twitter
		// OAuth.isVerifierResponse() == true
		} else {		
			// user has not authorized access
			if (params.get("denied")!=null) login();

			// user authorized access
			final String userId = session.get("userId");
			if(userId==null) login();
			session.remove("userId");
			
			final User user = User.findById(Long.valueOf(userId));
			final OAuth.Response response = OAuth.service(TWITTER).retrieveAccessToken(user.token, user.secret);
			
			if (response.error==null) {
				// replace old token and secret with new ones
				user.token = response.token;
				user.secret = response.secret;
				
				// get user info
				JsonObject twitterUser = 
						WS.url("http://api.twitter.com/1/account/verify_credentials.json")
						.oauth(TWITTER, user.token, user.secret).get().getJson().getAsJsonObject();
				
				if (twitterUser.get("error") != null) {
                    // error fetching user info, probably the token has expired 
                    Logger.error("Twitter authentication error: %s", twitterUser.get("error"));
                    login();
				}
				user.name = twitterUser.get("name").getAsString();
				user.avatarUrl = twitterUser.get("profile_image_url").getAsString();
				user.save();
				session.put(USER_COOKIE, user.id);
			}
			Application.list();
		}
	}

And now we'll create the login view.

views/Secure/login.html

#{extends 'main.html' /}
#{set title:'Login' /}

             <div class="alert-message block-message error">
                <h3>&{'login.legend'}</h3>
                <p style="padding-top:5px;">
                    <a href="@{Secure.oauthTwitter}"><img src="@{'/public/images/sign-in-with-twitter.png'}"/></a>&nbsp;&nbsp;
                    <a href="@{Secure.oauthFacebook}"><img style="padding-bottom:1px;" src="@{'/public/images/sign-in-with-facebook.png'}"/></a>
                </p>
            </div>

We can give it a try

After that, go to facebook developer's page, register the app, and implement the action that will authenticate us with facebook. So add the following action to Secure controller:

controllers.Secure

[...]
	public static OAuth2 FACEBOOK = new OAuth2(
	    Play.configuration.getProperty("facebook.authorizationURL"),
	    Play.configuration.getProperty("facebook.accessTokenURL"),
	    Play.configuration.getProperty("facebook.clientid"),
	    Play.configuration.getProperty("facebook.secret")
	);
[...]
public static void oauthFacebook() throws Throwable
    {
        if ( params.get("error") != null ) {
            // El usuario no autorizo el acceso
            // error_reason y error_description traen mas info
            login();
        }
        final String authUrl = play.mvc.Router.getFullUrl("Secure.oauthFacebook");

        if (OAuth2.isCodeResponse()) {
            // El usuario autorizo el acceso desde Facebook
            OAuth2.Response response = FACEBOOK.retrieveAccessToken(authUrl);
            User user = User.find("accessToken = ?", response.accessToken).first();
            if ( user == null ) {
                user = new User();
                user.accessToken = response.accessToken;
            }
            JsonObject me = WS.url("https://graph.facebook.com/me?fields=name,picture&access_token=%s", WS.encode(user.accessToken)).get().getJson().getAsJsonObject();
            user.name = me.get("name").getAsString();
            user.avatarUrl = me.get("picture").getAsString();
            user.save();
            session.put(USER_COOKIE, user.id);

            Application.list();
        }
        FACEBOOK.retrieveVerificationCode(authUrl);
    }
[...]

Now we are going to Step 10 - authorization.