Facebook Canvas App Authentication – Java

Facebook will stop supporting canvas FBML apps very soon. So this article only talks about iFrame apps build in Java and using OAuth 2.0 protocol. The basic flow of authentication procedure can be found at facebook’s developer section.

When user is logged in Facebook and access to your app, it sends a signed request to the canvas page of the app using POST method. So in your servlet you have to handle the request in doPost method. Check for the presence of Oauth token in the signed request. If that is not present ask for it.

The signed request is a base64url encoded Json object. Now I couldn’t find anywhere a mapping class which can be mapped to the Json object after base64url decoded signed request. So I had to write it by myself.

public class FacebookSignedRequest {

	private String algorithm;
	private Long expires;
	private Long issued_at;
	private String oauth_token;
	private Long user_id;
	private FacebookSignedRequestUser user;
	
	public String getAlgorithm() {
		return algorithm;
	}
	
	public void setAlgorithm(String algorithm) {
		this.algorithm = algorithm;
	}
	
	public Long getExpires() {
		return expires;
	}
	
	public void setExpires(Long expires) {
		this.expires = expires;
	}
	
	public Long getIssued_at() {
		return issued_at;
	}
	
	public void setIssued_at(Long issued_at) {
		this.issued_at = issued_at;
	}
	
	public String getOauth_token() {
		return oauth_token;
	}
	
	public void setOauth_token(String oauth_token) {
		this.oauth_token = oauth_token;
	}
	
	public Long getUser_id() {
		return user_id;
	}
	
	public void setUser_id(Long user_id) {
		this.user_id = user_id;
	}

	public FacebookSignedRequestUser getUser() {
		return user;
	}

	public void setUser(FacebookSignedRequestUser user) {
		this.user = user;
	}
	
	public static class FacebookSignedRequestUser {

		private String country;
		private String locale;
		private FacebookSignedRequestUserAge age;
		
		public String getCountry() {
			return country;
		}

		public void setCountry(String country) {
			this.country = country;
		}

		public String getLocale() {
			return locale;
		}

		public void setLocale(String locale) {
			this.locale = locale;
		}

		public FacebookSignedRequestUserAge getAge() {
			return age;
		}

		public void setAge(FacebookSignedRequestUserAge age) {
			this.age = age;
		}

		public static class FacebookSignedRequestUserAge{
			private int min;
			private int max;

			public int getMin() {
				return min;
			}

			public void setMin(int min) {
				this.min = min;
			}

			public int getMax() {
				return max;
			}

			public void setMax(int max) {
				this.max = max;
			}
		}
	}
}

Now I write a Facebook util class which works as a configuration set up for Facebook app

public class FacebookAuthService {

	private static final String apiKey = "APP_KEY";
	private static final String appSecret = "APP_SECRET";
	private static final String appId = "APP_ID";
	
	private static final String redirect_uri = "https://apps.facebook.com/YOUR_APP_PATH";
	
	private static final String[] perms = new String[] {"publish_stream", "email"};
	
	public static String getAPIKey() {
		return apiKey;
	}

	public static String getSecret() {
		return appSecret;
	}

	public static String getLoginRedirectURL() {
		return "https://graph.facebook.com/oauth/authorize?client_id=" + appId
				+ "&display=page&redirect_uri=" + redirect_uri + "&scope="
				+ StringUtils.join(perms);
	}

	public static String getAuthURL(String authCode) {
		return "https://graph.facebook.com/oauth/access_token?client_id="
				+ appId + "&redirect_uri=" + redirect_uri + "&client_secret="
				+ appSecret + "&code=" + authCode;
	}
	
	public static String getAuthURL() {
		return "https://www.facebook.com/dialog/oauth?client_id="
				+ appId + "&redirect_uri=" + redirect_uri + "&scope="
				+ StringUtils.join(perms);
	}
	
	public static FacebookSignedRequest getFacebookSignedRequest(String signedRequest) throws Exception{
		
		String payLoad = signedRequest.split("[.]", 2)[1];
		payLoad = payLoad.replace("-", "+").replace("_", "/").trim();
		
		String jsonString = new String(Base64.decodeBase64(payLoad));
		return new ObjectMapper().readValue(jsonString, FacebookSignedRequest.class);
	}
}

I’ve used Jackson to map Json to Object here. But any other API can be used too.

Now in my entry servlet (my canvas URL) I check for it in the doPost method like

String signedRequest = (String) request.getParameter("signed_request");

FacebookSignedRequest facebookSignedRequest = FacebookAuthService.getFacebookSignedRequest(signedRequest);
PrintWriter writer = response.getWriter();
if (facebookSignedRequest.getOauth_token() == null) {
	response.setContentType("text/html");
	writer.print("<script> top.location.href='"	+ FacebookAuthService.getAuthURL() + "'</script>");
	writer.close();
} else {
	request.setAttribute("accessToken",	facebookSignedRequest.getOauth_token());
	RequestDispatcher requestDispatcher = getServletContext().getRequestDispatcher("/YOUR_NEXT_PATH");
	requestDispatcher.forward(request, response);
}

So, if the Oauth token is not present in the signed request the servlet redirects user to get the app permission. Once user allows the app the servlet gets the Oauth token in the signed request and pass it to the next page of the app.

Advertisements

Facebook Canvas Application Authentication – OAuth 2.0 protocol

This article only aims towards ‘OAuth 2.0 protocol‘ authorization for ‘Apps on Facebook.com‘  runs as ‘Canvas FBML‘, written in PHP.

Use facebook.php if you really don’t want to put yourself into miseries.
So the first step : (creating the object)

require('facebook.php');

$facebook = new Facebook(array(
	'appId' => APP_ID,
	'secret' => APP_SECRET,
	'cookie' => true
));

Now make the user login to and authorize your app on his first visit.

if($facebook->getSession()) {
	//User is already logged in & authorized your app
	$uid = $facebook->getUser();
} else {
	$params = array(
			'fbconnect' => 0,
			'canvas' => 1,
			'next' => "http://apps.facebook.com/YOUR_APP/"
		);
	$redirect = $facebook->getLoginUrl($params);
	echo '<fb:redirect url="' . $redirect . '">';
}

This will redirect the user to application authorization page. On giving the access user will be sent to the next page.

You need to have the authentication token of the user to make any call.

if ($facebook->getSession()) {
	$uid = $facebook->getUser();
	$access_token = $facebook->getAccessToken();
}

Now if you need to have any extended permissions, check for it & ask for it,

$hasPublishPermission = json_decode(
	file_get_contents(
		'https://api.facebook.com/method/
		users.hasAppPermission?uid='.$uid.'&'.
		'ext_perm=publish_stream&
		format=json&access_token='
		.$access_token));

if(!$hasPublishPermission){
	$params = array(
		'fbconnect' => 0,
		'canvas' => 1,
		'next' => "http://apps.facebook.com/YOUR_APP/",
		'req_perms' => 'publish_stream'
	);
	$redirect = $facebook->getLoginUrl($params);
	echo '<fb:redirect url="' . $redirect . '">';
}

More than one extended permissions can be asked by a single call, just put them separated by commas.

$params = array(
	'fbconnect' => 0,
	'canvas' => 1,
	'next' => "http://apps.facebook.com/YOUR_APP/",
	'req_perms' => 'publish_stream, user_photos'
);

An example of making a call to Facebook. I’ve used Old REST API, but the new Graph API will also work as well.

$album = json_decode(
	file_get_contents(
		'https://api.facebook.com/method/
		photos.createAlbum?name='.urlencode($albumTitle).'
		&'format=json&access_token=' .$access_token));