410
votes

Goal: Allow a user to authentication with Facebook into an iOS application which requires access to a protected web service that I'm running.

Assumptions: There is a native authentication (and registration) system in place for those users that opt not to use Facebook for sign in.

Details:

  • Assume we want to offer the option for a user to sign in with Facebook without creating a separate account/credential for our system.
  • Because we support our own native auth mechanism (username and password) we have our own user IDs and issue an authentication token that is used for subsequent interactions after the initial credential validation.

I'm surprised that Facebook doesn't have best practices for this in their developer documentation. All the existing documentation is either assuming you are building FB auth into a website, or a standalone mobile app with no service that requires authentication.

Here's my initial thoughts on how this would be designed but want validation on whether it's correct.

  1. Client pops the Facebook iOS Login
  2. UI User signs in with Facebook credentials and gets access token
  3. iOS App passes access token to our server
  4. Our server talks to FB graph API using access token to (a) validate the token and (b) get the FB user ID for that access token.

    e.g. Our server would call https://graph.facebook.com/me/?access_token=XYZ which would return profile info in a JSON object

  5. Assuming it's valid, our server extracts the User ID from the JSON object and checks whether the user already has an account. If so, we issue our own auth ticket to client to use for that session. If user doesn't have an account, we create a new one with the Facebook User ID, assign our own unique UserID and issue our auth ticket.

  6. Client then passes auth ticket back on subsequent interactions that need authentication.

This seems like the right approach to me but not sure if I'm missing something insanely basic and going down the wrong (complicated) path.

4
How was this resolved? I am looking at passing the access token as well, and spinning up the user on the server. Seems academic, but am asking.Chris Van Buskirk
This was my implementation using rails and devise: stackoverflow.com/questions/7232490/…Matt
Why not pass the whole auth_hash instead of having to make two calls to the FB API (one from iOS device and once from server)?bsiddiqui
What if you want to log in from a different device (meaning you don't have your auth ticket)? And if you just receive a new auth ticket, what prevents someone from hijacking the facebook id/token on the way and use it on his own device?Amitloaf
Out of curiosity, in step 5, why issue your own auth ticket? Could you not use the Facebook access token for every subsequent server call? I do realize that it would require a call from the server to the Facebook API for every app-->server call rather than just the first one.Anders

4 Answers

83
votes

I just dealt with this myself, and here's the part that bit me:

In your step 5... It's possible for a user to register for an account with you entirely separate from their Facebook ID, right? Then some other time they log in with Facebook.... And you just created them a second account and lost their first one.

There needs to be a way to be logged in to your web service, then log in to facebook, and capture the association between the facebook ID and the local account.

Apart from that, your plan sounds solid.

Update: Facebook has added a doc outlining such a scenario HERE

30
votes

Use https to transmit the auth token to your server, as stated by Facebook

Sharing of Access Tokens

Our Data Policies explicitly prohibit any sharing of an Access Token for your app with any other app. However, we do allow developers to share Tokens between a native implementation and a server implementation of the same App (ie. using the same App ID) as long as the transfer takes place using HTTPS.

16
votes

One problem I can see with this strategy, is that somebody can give you an access token obtained for a different facebook app. As far as I know, there's no way to verify that the access token is for your application, so you'll just go on and use it.

It doesn't sound very harmful, though. Generally people/apps try to protect the access tokens, rather than sharing them.

One possible exploit of this would be, for somebody to create their own site or mobile app, obtain access tokens for their users and try to authenticate them, using your API. If this succeeds (the user is has a facebook account in your site), the malicious site will be able to use your API impersonating the user.

It's a bit of a long shot, but I think it could work.

Edit: It looks like there is a way to validate the access token after all. See the answer by @Daaniel on question Get application id from user access token (or verify the source application for a token).

4
votes

your solution totally works.

Maybe an alternative: why not just get the email on the client from the initial social service request and send to your web service? The web service could just store the email, and maybe a social_provider as well. I understand that your web service will not be able to validate where the email came from, but isn't there a high-trust relationship between your web service and your client? If there is, seems like you can depend on the email coming from the right place. Someone please let me know what obvious thing I'm missing that makes the email-based approach silly...