Google device authentication in your Rails app

Authentication is one of the most common issues with mobile development.

Note: this post assumes you are developing an android app, though the logic should be the same for iOS apps with Google SDK.

Let’s say we have a regular web app that authenticates people via Google OAuth 2 in order to let the user complete some action (e.g. turning on/off a light).

At a certain point we want to add a mobile app that lets the user log in via google and operate on the web app. How can we solve the authentication issue?

A common solution, a tricky one indeed, is to use a WebView component inside the app to simulate the browser behavior. You can read a guide on how to achieve this solution here.

I don’t like tricky solutions so I started searching for a better one.

As every guide will tell you, on Android you can fetch an oauth2 token from google using GoogleAuthUtil, like in this gist.

You can use the received token only to interact with google services. If you have to interact with your custom server, there is no way to let it know that you are really you, or to let it fetch informations about you.

Now, to achieve these features, first of all create an Android certificate in your Google Developer Console, as explained in this guide.

Sign your messages

Official doc to the rescue, if you specify a particular scope when requesting a token (audience:server:client_id:<your_web_client_id>) google will return you an ID token. This token is intended to be used in post requests over an https channel to sign your messages. It is in fact a json object (containing the account email and a bunch of other information) cryptographically signed with a certificate.

So you could use this token to sign your messages. And what about our Rails app? There we’ll use this fork of the google-id-token gem (because the original one is outdated). Usage is as easy as a pie. In our APIController we’ll add the following code:

before_filter :authenticate_android_user

private

def user_email
  id_token.try(:email)
end

def authenticate_android_user
  if params[:token].blank? || id_token.nil?
    head 401
    false
  end
end

def id_token
  @id_token ||= begin
    validator = GoogleIDToken::Validator.new
    validator.check params[:token], 'my_web_client_id', 'my_android_client_id'
  end
end

If an API request has no token param or an invalid one, the rails app will return a 401 status code. Otherwise we can use the user_email method to fetch the user email.

Offline Access

Another approach is to grant offline access to the server. Official doc to the rescue again, if you specify a particular scope when requesting a token (oauth2:server:client_id:<your_web_client_id>:api_scope:resource1 resource2) google will return you a short-lived authorization code.

The mobile app can send this code to the server app that can exchange it for an access token and a refresh token that will allow the server app to make requests for the specified resources (resource1 and resource2 in this example).

In our Rails app we have to add the google-api-client gem. The following method would exchange the auth token for an access token and a refresh token:

def exchange_token auth_token
  client = Google::API::Client.new application_name: 'MyApp', application_version: '1.0.0'
  client.authorization.client_id = '123'
  client.authorization.client_secret = 'asd'
  client.authorization.code = params[:code]
  client.fetch_access_token!
  [client.authorization.access_token, client.authorization.refresh_token]
end

Conclusion

What approach should you follow? You need the ID token for sure, because it lets the user identify himself. But if you also want the server to retrieve informations about the user you need both the ID token and the auth token.

When the mobile app starts it has to require two tokens: the ID token (to let the server identify the user) and the auth token (to let the server operate as if it was the user). Then the app should send both of them to the server calling a sort of ‘login’ API.

Now the server validates the ID Token and, if the user is logging in for the first time, it exchanges the auth token with google for an access token and a refresh token, storing both of them on the DB.

From now on the mobile app should embed the ID token every time it calls the server, to identify itself. And it’s done!

Leave a Reply

Please Login to comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.