Sinatra + omniauth + Android, advice sought

2019-08-13 19:02发布

问题:

I'm developing a Sinatra app for which I'd like to use OmniAuth. So far, I have something similar to this for the web app:

http://codebiff.com/omniauth-with-sinatra

I'd like the web app to be usable via Android phones which would use an API, authenticating by means of a token. The development of an API seems to be covered nicely here:

Sinatra - API - Authentication

What is not clear is now I might arrange the login procedure. Presumably it would be along these lines:

  1. User selects what service to use, e.g. Twitter, FaceBook &c., by means of an in-app button on the Android device.
  2. The Android app opens a webview to log in to the web app.
  3. A token is somehow created, stored in the web app's database, and returned to the Android app so that it can be stored and used for subsequent API requests.

I'm not very clear on how point 3 might be managed - does anyone have any suggestions?

回答1:

As no-one seems to have any suggestions, here's what I've come up with so far. I don't think it's very good, though.

I've added an API key to the user model, which is created when the user is first authenticated:

class User
  include DataMapper::Resource
  property :id,         Serial, :key => true
  property :uid,        String
  property :name,       String
  property :nickname,   String
  property :created_at, DateTime
  property :api_key,    String, :key => true
end

....


get '/auth/:name/callback' do
  auth = request.env["omniauth.auth"]
  user = User.first_or_create({ :uid => auth["uid"]}, 
                              { :uid => auth["uid"], 
                                :nickname => auth["info"]["nickname"], 
                                :name => auth["info"]["name"],
                                :api_key => SecureRandom.hex(20),
                                :created_at => Time.now })
  session[:user_id] = user.id
  session[:api_key] = user.api_key
  flash[:info] = "Welcome, #{user.name}"
  redirect "/success/#{user.id}/#{user.api_key}"
end

If the authorisation works then the api_key is supplied to the Android app, which will presumably store it on the device somewhere:

get '/success/:id/:api_key', :check => :valid_key? do
  user = User.get(params[:id],params[:api_key])
  if user.api_key == params[:api_key]
    {'api_key' => user.api_key}.to_json 
  else
    error 401
  end
end

All API calls are protected as in the link in my original post:

register do
  def check (name)
    condition do
      error 401 unless send(name) == true
    end
  end
end

helpers do
  def valid_key?
    user = User.first(:api_key => params[:api_key])
    if !user.nil?
      return true
    end
    return false
  end
end

For public use I'll only allow SSL connections to the server. Any suggestions for improvement would be welcome.