APIでUser Authenticationを理解して適切に実装するのが困難です。つまり、Grape APIとBackbone.js、AngularJS、Ember.jsなどのフロントエンドフレームワークとの統合を理解するのに深刻な問題があります。
私はさまざまなアプローチをピボットし、それについてたくさん読んでいますが、Googleは本当に悪いリソースを返し、このトピックに関する本当に良い記事はないようです-RailsとユーザーDeviseおよびフロントエンドフレームワークによる認証。
私の現在の要点について説明します。私の実装に関するフィードバックを提供していただければ、正しい方向に向けることができます。
現在の実装
私はバックエンドを持っていますRails REST API with following Gemfile(私はすべて意図的に短縮しますファイルコード)
gem 'Rails', '4.1.6'
gem 'mongoid', '~> 4.0.0'
gem 'devise'
gem 'grape'
gem 'rack-cors', :require => 'rack/cors'
現在の実装には、次のルートを持つAPIのみがあります(routes.rb):
api_base /api API::Base
GET /:version/posts(.:format)
GET /:version/posts/:id(.:format)
POST /:version/posts(.:format)
DELETE /:version/posts/:id(.:format)
POST /:version/users/authenticate(.:format)
POST /:version/users/register(.:format)
DELETE /:version/users/logout(.:format)
私は次のモデルを作成しましたuser.rb
class User
include Mongoid::Document
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
field :email, type: String, default: ""
field :encrypted_password, type: String, default: ""
field :authentication_token, type: String
before_save :ensure_authentication_token!
def ensure_authentication_token!
self.authentication_token ||= generate_authentication_token
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
end
私のコントローラーでは、次のフォルダー構造を作成しました:controllers-> api-> v1そして、次の共有モジュール認証(authentication。 rb)
module API
module V1
module Authentication
extend ActiveSupport::Concern
included do
before do
error!("401 Unauthorized", 401) unless authenticated?
end
helpers do
def warden
env['warden']
end
def authenticated?
return true if warden.authenticated?
params[:access_token] && @user = User.find_by(authentication_token: params[:access_token])
end
def current_user
warden.user || @user
end
end
end
end
end
end
したがって、リソースが認証トークンで呼び出されることを確認するたびに、次のように呼び出すだけでこれを追加できます:include API::V1::Authentication
をGrapeリソースに追加します。
module API
module V1
class Posts < Grape::API
include API::V1::Defaults
include API::V1::Authentication
これで、Users(users.rb)と呼ばれる別のGrapeリソースができました。ここで、認証、登録、ログアウトのメソッドを実装します(ここではリンゴと梨を混ぜていると思います。ログイン/ログアウトプロセスを別のGrapeリソースに抽出する必要があります-セッション)。
module API
module V1
class Users < Grape::API
include API::V1::Defaults
resources :users do
desc "Authenticate user and return user object, access token"
params do
requires :email, :type => String, :desc => "User email"
requires :password, :type => String, :desc => "User password"
end
post 'authenticate' do
email = params[:email]
password = params[:password]
if email.nil? or password.nil?
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
end
user = User.find_by(email: email.downcase)
if user.nil?
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
end
if !user.valid_password?(password)
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
return
else
user.ensure_authentication_token!
user.save
status(201){status: 'ok', token: user.authentication_token }
end
end
desc "Register user and return user object, access token"
params do
requires :first_name, :type => String, :desc => "First Name"
requires :last_name, :type => String, :desc => "Last Name"
requires :email, :type => String, :desc => "Email"
requires :password, :type => String, :desc => "Password"
end
post 'register' do
user = User.new(
first_name: params[:first_name],
last_name: params[:last_name],
password: params[:password],
email: params[:email]
)
if user.valid?
user.save
return user
else
error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
end
end
desc "Logout user and return user object, access token"
params do
requires :token, :type => String, :desc => "Authenticaiton Token"
end
delete 'logout' do
user = User.find_by(authentication_token: params[:token])
if !user.nil?
user.remove_authentication_token!
status(200)
{
status: 'ok',
token: user.authentication_token
}
else
error!({:error_code => 404, :error_message => "Invalid token."}, 401)
end
end
end
end
end
end
私はここに大量のコードを提示し、それが意味をなさないかもしれないことを理解していますが、これは私が現在持っているものであり、authentication_token
モジュールAuthentication
で保護されているAPIに対する呼び出し。
このソリューションは良いとは思えませんが、APIを介してユーザー認証を実現する簡単な方法を本当に探しています。以下に挙げる質問がいくつかあります。
質問
Token
を使用した実装も確認しました。ただし、この目的のためにOAuth2を実装できるため、これはホイールを再発明するようなものだと思います。もっと軽い解決策が欲しいのですが。投稿が長かったことをお詫びしますが、同じ問題を抱えている人が増えて、質問に対する回答が増えることを期待しています。
Token_authenticableをdeviseモジュールに追加します(deviseバージョン<= 3.2で動作)
User.rbで、:token_authenticatableをdeviseモジュールのリストに追加すると、以下のようになります。
_class User < ActiveRecord::Base
# ..code..
devise :database_authenticatable,
:token_authenticatable,
:invitable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable
attr_accessible :name, :email, :authentication_token
before_save :ensure_authentication_token
# ..code..
end
_
自分で認証トークンを生成する(デバイスバージョン> 3.2の場合)
_class User < ActiveRecord::Base
# ..code..
devise :database_authenticatable,
:invitable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable
attr_accessible :name, :email, :authentication_token
before_save :ensure_authentication_token
def ensure_authentication_token
self.authentication_token ||= generate_authentication_token
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
_
認証トークンの移行を追加する
_Rails g migration add_auth_token_to_users
invoke active_record
create db/migrate/20141101204628_add_auth_token_to_users.rb
_
移行ファイルを編集して、ユーザーに:authentication_token列を追加します
_class AddAuthTokenToUsers < ActiveRecord::Migration
def self.up
change_table :users do |t|
t.string :authentication_token
end
add_index :users, :authentication_token, :unique => true
end
def self.down
remove_column :users, :authentication_token
end
end
_
移行を実行する
_rake db:migrate
_
既存のユーザーのトークンを生成する
ユーザーごとに認証トークンが確実に存在するように、ユーザーのすべてのインスタンスでsaveを呼び出す必要があります。
User.all.each(&:save)
認証トークンを使用した安全なGrape API
トークンベースの認証を追加するには、以下のコードをAPI :: Rootに追加する必要があります。 API :: Rootを理解していない場合は、 Grapeを使用したRESTful APIの構築 をお読みください。
以下の例では、2つのシナリオに基づいてユーザーを認証しています–ユーザーがWebアプリにログオンしている場合は、同じセッションを使用します–セッションが利用できず、認証トークンが渡された場合は、トークンに基づいてユーザーを検索します
_# lib/api/root.rb
module API
class Root < Grape::API
prefix 'api'
format :json
rescue_from :all, :backtrace => true
error_formatter :json, API::ErrorFormatter
before do
error!("401 Unauthorized", 401) unless authenticated
end
helpers do
def warden
env['warden']
end
def authenticated
return true if warden.authenticated?
params[:access_token] && @user = User.find_by_authentication_token(params[:access_token])
end
def current_user
warden.user || @user
end
end
mount API::V1::Root
mount API::V2::Root
end
end
_
@MZaragozaの質問と回答は気に入っていますが、token_authenticalが理由によりDeviseから削除されたことは注目に値します。トークンの使用は、タイミング攻撃に対して脆弱です。 この投稿 と Deviseのブログ も参照してください。そのため、@ MZaragozaの回答に賛成していません。
APIをDoorkeeperと組み合わせて使用する場合、同様のことを実行できますが、Userテーブル/モデルでauthentication_tokenを確認する代わりに、OauthAccessTokensテーブルでトークンを探します。
def authenticated
return true if warden.authenticated?
params[:access_token] && @user = OauthAccessToken.find_by_token(params[:access_token]).user
end
そのトークン(つまり、実際のaccess_token)は一定の時間だけ存在するため、これはより安全です。
これを行うには、次のように、ユーザーモデルとOauthAccessTokenモデルが必要です。
class User < ActiveRecord::Base
has_many :oauth_access_tokens
end
class OauthAccessToken < ActiveRecord::Base
belongs_to :user, foreign_key: 'resource_owner_id'
end
EDIT:また、一般的には、URLにaccess_tokenを含めないでください: http://tools.ietf.org/html /draft-ietf-oauth-v2-bearer-16#section-2.