Recently I’m been working on a single sign-on system (SSO, in the biz) and this week I added support for logging into the SSO via the Facebook Login Button. It’s cool that you can have a user log into you website using Facebook but as it stands there are a few… unfortunate… aspects to the system, all based around one fact: logging into a site using a Facebook account is logging into Facebook.
However, by using the Login Button you’re tying your app’s basic user state to Facebook. If you’re like me, you want to do some stuff, both client-side and server-side, when a user is logged into your app. Likewise, you want to preform some actions when the user logs out of your app. And we do want to let the user to be able to easily log out: maybe they’re using a computer at an internet cafe, maybe they want to let their sister log into the same site under her own account, maybe they just don’t like leaving themselves logged in. This is where I believe Login with Facebook has some problems.
Let’s assume that we have stand-alone login and logout pages. My experience is with this case, though I believe that my observations stand for other cases. Let’s take the flow in reverse, as I think it will make more sense in the end. The user clicks a link to your logout page. What do you do? Perhaps we’d like to do it all server-side so we can then redirect them to the home page without any prompting. Seems fair enough. So, let’s destroy any sessions (including cookies that sessions might be recreated from) that our website set.
But wait, Facebook created a cookie on our domain with the current user’s access token. Let’s delete that too to make sure our user is logged out of our app via Facebook. But no, that won’t work (more explanation in a bit). I know, you say: let’s use the FB.logout() function. Except that logs you out of Facebook entirely. Remember what I said about the Login Button logging you into Facebook? Well, it logically follows that the logout call logs you out.
Except, why would anyone ever want to do that to their users? It seems like such a bewildering and user-hostile option, to log the user out of Facebook when they think they’re just logging out of your site. And if you make it clear to the user that logging out of your site will also involve logging out of Facebook, who would ever want to do that?
But Facebook is using OAuth 2.0, right? So that means there’s an access token and you can revoke it, right? There definitely is an access token: Facebook nicely gives it to you at the end of a successful login process. However, you can’t programmatically revoke your own access token other than by calling
FB.logout(). There’s no function to do that and for the life of me I can’t figure out as a user how to revoke access that I’ve given out. Twitter, in contrast, has a pretty easy to manage Connections page. Update: it’s the Apps You Use page.
All this hints at what I believe to be an underlying assumption: a user will never (should never) want to log out of your site once logged in by Facebook. I think this is fundamentally wrong, but let me skip that issue and explain why being unable to simply logout of the Facebook login of your app (yes, it’s a mouthful) is so annoying.
There are two main ways to detect that a user has logged into Facebook via the Login button. First, you can add an
So that doesn’t work, on to the second option. How can we handle the case if the user clicks the login button but they’re already logged in? Luckily you can subscribe to the
user.login event, which will eventually fire whenever the user is logged in. That is, it will always eventually fire. This includes when you load your login page with the Login Button: the Facebook API will be initialized, it will detect the user is logged in, and then it will fire the
I think Facebook assumes you’ll avoid this login page flicker by first checking the Facebook cookie left on your domain on the server-side. I am doing that but I run the risk of logging a user into my app based upon expired credentials which are still stored in the cookie, since I refuse to call
FB.logout(). So, my website’s logout functionality currently destroys the Facebook cookie but does not call
For this and other reasons we naturally start wondering whether some of this page flicker can be avoided by processing on the server-side. The only solution to potentially using an expired Facebook access token seems to be to use the server-side API’s authentication functionality with the cookie Facebook set, but then you start to fall down the rabbit hole of duplicating all client-side functionality in your server-side code. And if you’re like me, the original reason you chose client-side login and the Login Button was because it was so easy to implement and seemed to have a superior user experience to server-side authentication (notice Facebook never says ‘login’ when talking about the server-side API).
One thing I’ve noticed is that Facebook’s cookies have quite short life times (like, several hours). I believe that Facebook has made the basic assumption, probably fair in most cases, that a user logging into a website using Facebook generally just wants to use it for a little bit and won’t be too put out if upon returning a day later they have to login again. And so, logout happens essentially magically as access tokens expire.
However, that still seems rather user- and developer-hostile: users will be logged out of the site without knowing it, or the site will keep them logged in based upon a stale access token, or the site must be constantly refreshing its access token behind the scenes. I don’t even know if a site can refresh it behind the scenes and in fact I think it would be somewhat bizarre if they could. The server-side authentication flow I mentioned earlier seems to be the closest thing to this.
To wrap up, I think the basic workings of the Login Button and the ability for a user to login to your website via the Facebook API is a good one, but the system makes some assumptions that are ultimately user-hostile. In some (all?) cases I think Facebook was actually trying to make the user-experience better but their solutions went against established practices for logging users into and out of websites, including facebook.com itself. I don’t have any specific recommendations beyond doing what’s expected and allowing sites to log users out of their sites (expire or destroy the access tokens).