I created a little demo app to help me understand Facebook authentication from flash.
Goals
- Use only javascript and actionscript, no php or other Facebook client libraries
- The secret key is not hardcoded
- The same codebase can be deployed on non-Facebook site or in on Facebook app canvas
- The app can be used by both authenticated and unauthenticated users
- Users can authenticate with Facebook after loading app without reloading the app
The App
If you are logged into facebook and have authorized the app, the demo pulls in your profile picture and user name and displays them in a panel. If you are not logged into facebook or have not yet authorized the app, it shows a login button which will show a popup (from a connect site) or redirect to the login page.
The same app is available as a Facebook iframe canvas page here.
Goal 1: Use only javascript and actionscript, no php or other server-side facebook client libraries
I wanted to use as few languages as possible and maximize what is done from flash. It worked well as described below.
Goal 2: Secret key is not hardcoded
From what I can tell, there’s no way to start a web session solely from the actionscript api (at least not without hardcoding the secret key, which we want to avoid). Instead, the session information can be passed to the swf. In the demo, the actionscript session is created as:
protected function connect(parameters:Object) : void {
if (parameters.fb_sig_api_key && parameters.fb_sig_ss
&& parameters.fb_sig_session_key) {
session = new WebSession(parameters.fb_sig_api_key,
parameters.fb_sig_ss, parameters.fb_sig_session_key);
session.facebook_internal::_uid = parameters.fb_sig_user;
//Create our facebook instance
facebook = new Facebook();
session.addEventListener(FacebookEvent.CONNECT, onSessionConnect);
facebook.startSession(session);
session.verifySession();
}
...
}
Details on acquiring the session variables are covered under the next goal.
Goal 3: The same codebase can be deployed on a non-Facebook site or on a Facebook app canvas
When loading an app via a Facebook iframe canvas, a number of parameters are appended to the iframe url’s query string, listed here. (The fb_sig_ parameters can also be included using fb:swf on an fbml canvas). In this app, I’ve used javascript to parse the query string into an object and pass to the swf as flashvars on load if the fb_sig_in_iframe=1 parameter is available. The same could be accomplished with a server-side language like php, but I wanted to keep the mix of languages to a minimum.
When loading the app from a Facebook Connect site, the session information is acquired through the javascript Facebook client first, then passed to the swf which sets up its session in the same way as above. In this case, the javascript translates the session variables to the same names used in the iframe query string.
var sessionData = FB.Facebook.apiClient.get_session();
var fbdata = {};
if (sessionData) {
fbdata.fb_sig_session_key = sessionData.session_key;
fbdata.fb_sig_ss = sessionData.secret;
fbdata.fb_sig_user = sessionData.uid;
fbdata.fb_sig = sessionData.sig;
fbdata.fb_sig_api_key = api_key;
}
Goal 4: The app can be used by both authenticated and unauthenticated users
This one is pretty easy: simply do not require a session. Your app will have limited data available, but users who haven’t authorized the app or who don’t have Facebook accounts can still use it if you accommodate them.
Goal 5: Users can authenticate with Facebook after loading app without reloading the app
I had mixed results here. I was unable to get this to work in a canvas page since calls to window.open and FB.Connect.requireSession are ignored in the iframe canvas. Instead, I redirect to facebook.com/login.php if the user needs to log in or authorize the app and then they are returned to the canvas page. This makes some sense since the whole Facebook page needs to refresh if you are logging in.
Logging in from a facebook iframe canvas page:
self.parent.location = 'http://www.facebook.com/login.php?api_key=' + api_key
+ '&extern=1&fbconnect=1&v=1.0'
+ '&next=' + escape(CANVAS_PAGE_URL)
+ '&cancel_url=' + escape(CANVAS_PAGE_URL);
You can log in without a page refresh in a Facebook Connect site. When the user clicks the “Connect with Facebook” button in the swf, it makes an external interface call to a javascript function. When not in an iframe canvas page, the javascript calls FB.Connect.requireSession. By passing true for the isUserActionHint parameter, the function opens a popup window where the user can login or authorize the app and then closes itself when done.
Logging in via Facebook Connect
FB.ensureInit(function() {
FB.Connect.requireSession(null, true);
});
The app page is notified of the status change through the cross domain receiver channel. I initialized the javascript client with a callback for when the user’s logged in status changes. In the callback, the updated session parameters are passed to the swf through another external inteface call.
Initializing the javascript Facebook client:
FB_RequireFeatures(["Api", "Connect"], function() {
FB.Facebook.init(api_key, 'xd_receiver.html',
{ "ifUserConnected": onConnectStatus,
"ifUserNotConnected": onConnectStatus
});
});
The Facebook actionscript api docs for the WebSession class recommend using FacebookSessionUtil for managing sessions instead of using WebSession directly. However, FacebookSessionUtil looks for some values only in loaderInfo, which isn’t compatible for updating the session info after the app loads. The demo does use a WebSession instead of FacebookSessionUtil, but it still gets all the necessary information passed in via javascript and doesn’t have hardcoded api or secret keys. The demo doesn’t look for a stored session in a shared object, but it could be added to mimic FacebookSessionUtil closely that way.
Links
View demo using Facebook Connect.
View demo in a Facebook iframe canvas
View and download the source
Reference