Design for Facebook authentication in an iOS app that also accesses a secured web service
Asked Answered
F

4

414

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.

Franciscka answered 7/1, 2011 at 9:11 Comment(5)
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.Coenurus
This was my implementation using rails and devise: #7232990Sequela
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)?Chlorate
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?Oblige
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.Hammerfest
B
83

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

Burnout answered 7/1, 2011 at 13:40 Comment(4)
Yep, I've considered that and you're spot on. We plan on merging accounts if it's the same email address, and if not, we will create another way to merge them.Franciscka
Which Server Library are you using on the Server side to make the request?Joeyjoffre
@Joeyjoffre - My understanding is that an access token embeds the app ID, and that you can't have an access token WITHOUT an app ID baked into it.Burnout
@TimLeunge: We are using the Graph API for requests with the user's access token.Franciscka
C
31

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.

Calpe answered 11/3, 2013 at 3:33 Comment(0)
O
17

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).

Otte answered 16/10, 2012 at 10:26 Comment(3)
Sending appsecret_proof should prevent this (see here)Jejunum
@Jejunum can you explain how appsecret_proof helps here? As far as I understand, it serves to prove to Facebook that the server knows the secret key. However, ivant was referring to a malicious app obtaining tokens and then sending them to your API. The server can verify the app ID, but the app ID can be easily spoofed by a malicious app. So... how would one mitigate this?Epigastrium
You can verify that the token was generated for your app through https://graph.facebook.com/app/?access_token=[user_access_token] which returns the app id, and then comparing the app idsApure
S
4

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...

Scheel answered 4/3, 2014 at 5:7 Comment(2)
I could easily find out how the client is talking to the server. Then I could just throw emails at it until I get a hit. There always needs to be some form of verificationSubmission
High trust and client? Never in the same sentence, please. Except previous sentence of course.Monahan

© 2022 - 2024 — McMap. All rights reserved.