Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a user creation / auth system for web and mobile that uses both email and facebook_id

I am writing a user creation system for an iOS app and a web app (ie a user can be created from mobile app or web frontend) using Ruby on Rails. I am not interested in using a system like Devise.

I haven't done this in a couple of years so just want to make sure that my ideas are current and make sense.

Steps / Assumptions

  1. We will manage state between our server and the clients via a custom HTTP field such as "X-auth_token".
  2. They can be created either via email / password credentials or via facebook.

In the case of email login, the user will just post the following json:

{
  user:{
    email:'[email protected]',
    password:'testpassword',
  }
}

I will be using Rails has_secure_password to hash the values.

and will return

{
  user:{
    id:23,
    auth_token:'md5value',
  }
}

For web, we will pass the auth_token as a cookie value. For the iOS app, the auth_token will be passed as a custom HTTP header field such as "X-auth_token"

  1. In the case of facebook login, they will use the FacebookSDK, get scoped permissions from FB, and post

{ user: { is_facebook_login: true, fb_email: '[email protected]', fb_auth_token: 'abigvaluefromFB' } }

On the server, we will ensure that they are passing a valid fb_auth_token credentials by calling facebook with

def self.verify_facebook fb_auth_token
  result = Net::HTTP.get(URI.parse("https://graph.facebook.com/me?access_token=#{fb_auth_token}"))
  obj=JSON.parse(result)
  obj["email"]
end 

and verifying that the fb_email sent in the initial request corresponds to the email provided back by facebook.

Here's the steps with a corresponding diagram:

STEPS

  1. oauth request to facebook, request email ONLY
  2. user approves, email and auth token come back
  3. send auth_token to our server; if a facebook auth'd client, set flag so that only can login via facebook
  4. verify with facebook that token is accurate

Not worried about right now, if they change their facebook email, tough luck. If they login via Facebook and then want to login via email / pwd, tough luck.

on our end, we will generate an auth_token (an MD5 string), and send it back to the client which now manages authentication. From that point on, we will send a custom HTTP header X-auth_token

enter image description here

We will respond with

{
  user:{
    id:23,
    auth_token:'md5value',
  }
}

and the iOS app will write the auth_token to the keychain via https://github.com/kishikawakatsumi/KeychainAccess

Does the above scenario seem reasonable for user creation?

like image 404
timpone Avatar asked Jan 15 '16 19:01

timpone


2 Answers

All the ideas you describe look valid for me, but the whole description looks incomplete.

Missing parts are:

  • How do you store and identify users?
  • How do you store auth tokens?
  • Can one user have multiple tokens?
  • Do you want to expire old tokens?

You already have a description of two processes - new user registration via email and new user registration via facebook.

On the server I would store them like this, users table:

  • ID - integer, unique user id in the application database
  • email - string, optional (can be absent for facebook), unique across table
  • facebook_id - string, optional (will be absent for users registered with email), unique across table
  • password - string, optional (no password for facebook users)

And I would also merge email / facebook users in scenarios like this:

  • User registers with email, we have ID and email
  • User registers / logins with facebook and provides email
  • We check that the user with such email already exists and set facebook_id for it
  • Now the user is able to login both with facebook or email

If the user changes his facebook email - this doesn't break anything, we can just ignore this change if we already have the email in the users table. So the user can still login with his old email. (Or, using additional login history, we could check that user had never logged in with email and replace it with new one).

It is also easy to provide a generate password feature for facebook users. They can enter email and generate a password when they are logged in, so they can also login with email/password in the future.

Also you can add forgot password feature which can generate a new password and send it to the user's email - this can be used both by email users and those facebook who provided the email.

If I get your idea right, you want to have a single unified method to both create new users and authenticate existing users, something like this:

  • User enter and application sends to the backend an email and a password
  • Check if user with such email exists
    • User exists - verify the password
    • Passed - send the access token
      • Failed - send an error
    • User does not exist - create the new user and token
    • Send the access token

It is similar for facebook, but instead of email / password we check facebook_id and verify with request to facebook.

You can also consider having separate backend endpoints - one to register new users and another one to login existing users. Because with the procedure above, if the user mis-types his email, you will create a new user which is not expected. For facebook registration / login will probably doesn't make much difference.

I assume that it is useful to allow one user to have multiple auth tokens to login from different locations, so here is the tokens table:

  • ID - integer, unique id, primary key
  • token - string, unique access token (you can consider making it to be primary key)
  • user_id - integer, reference to the user, who owns the token
  • created_at - datetime, creation date
  • expired - boolean, can be used to implement tokens expiration

Regular request looks like this:

  • There is a request with X-auth_token
  • Find the token in the database
  • Find the appropriate user
  • Allow the user access his resources

The token expiration can be done like this:

  • There is a request with X-auth_token
  • Find the token in the database
  • Check if now() - token.created_at > EXPIRATION_PERIOD
    • mark the token as expired (token.expired = True + save)
    • return an error "Token expired"
    • on next requests - return the same error, application should ask the user to login again
like image 121
Boris Serebrov Avatar answered Nov 13 '22 08:11

Boris Serebrov


Some tips:

1) Remember that the facebook email is not the facebook user login email, its the profile email, If the user leaves it blank it defaults to [email protected]. This is important because you may want users to validate the registration by email, is this scenario i suggest you to use a modal to ask user to input his email after the fb registration.

2) Facebook users won't have passwords but they can change it using "forget my password" and this scenario relies on the usecase above (they need a real email address).

3) I dont know your security scenario and the kind of app you are creating. But you can always just use the fb api to retrieve user data and save it as a default user. example: get data from fb api and save user as [email protected] password: tokenWithMd5OrSomethingLikeThat userType: "facebook" . This is usefull when you are developing a small application that you will not update too soon, its way faster.

like image 20
Renato Probst Avatar answered Nov 13 '22 07:11

Renato Probst