Calciphus
Calciphus

Reputation: 146

Stored Android Accounts for Authentication of Rails + Devise Accounts

Summary: I'm trying to use stored Google accounts on an Android application to authenticate users in a native application, with an API provided by a Rails 3 + Devise application, and I'm stuck.

Details: We have an application (http://push-poll.com) which lets users gather feedback from a bunch of their friends. We've also built a native Android application. Our web app allows you to log in with your Google account, and we want to extend that functionality to the native Android app. The web app accomplishes this with Devise (for account permissions) + Omniauth (for Google account info and OAuth interaction).

Versions: Android 2.1+, Rails 3.2.2, Devise 2.1.2, Omniauth 1.1.0

Proposed authentication workflow:

  1. User hits "log in with Google account" on Android application
  2. User selects a stored account on Android device using AccountManager and gives permission
  3. Android app sends Google account token to Rails API
  4. Rails app sends account token to Google OAuth API <-- This is where I'm stuck
  5. Google returns success and account information (specifically, name + email)
  6. Rails app find/create user by email address
  7. Rails app create/read/update access token from user account
  8. Rails access token is retuned to Android app (this is how our app currently handles api access with standard login)
  9. Android app includes Rails access token in all API calls

I've scoured Google's documentation, but I am totally stuck on (4) and (5). Once I have the Google-provided email address, everything works as it does now (for either standard logins or Google/Omniauth/Devise accounts). By my best guess, this can be solved with either of the following:

I've been banging my head against a wall on this one for at least a week.

FYI: I'm the web dev, and our Android dev has been kind enough to furnish me with a stubbed-out Android app that'll send an access token from Android to the API endpoint of my choosing.

Upvotes: 1

Views: 1442

Answers (1)

Calciphus
Calciphus

Reputation: 146

OK, so I was able to resolve this with a little better understanding of OAuth. Apparently tokens are able to be passed around between different servers and it doesn't really matter who holds them, they continue to function.

So, using Android's AccountManager I was able to extract a token with sufficient permissions for the scope userinfo.email, and then sending that to the server. Then the server can hit:

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=TOKENVALUE

Note: This is being sent to the Rails app via HTTPS - you should never send a token over the internet in clear text.

Here's a partial clip of how we do this:

# Use Google's Token Verification scheme to extract the user's email address
token = params[:token]
uri = URI.parse("https://www.googleapis.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
path = "/oauth2/v1/tokeninfo?access_token=#{token}"
resp, data = http.get(path)
data = JSON.parse(data)
if resp.code == "200"
  # Find a user
  @user = User.where(:email => data["email"]).first
  if !@user
    #Create a user with the data we just got back
  end
else
  # Bad or revoked token 
end

Upvotes: 2

Related Questions