User Authentication with Grape and Devise

后端 未结 2 1154
悲哀的现实
悲哀的现实 2020-12-12 23:57

I have difficulties to understand and also properly implement User Authentication in APIs. In other words, I have serious problem to understand the

相关标签:
2条回答
  • 2020-12-13 00:06

    Although I like the question and the answer given by @MZaragoza I think it is worth noting that token_authentical has been removed from Devise for a reason! Use of the tokens are vulnerable for timing attacks. See also this post and Devise's blog Therefor I haven't upvoted @MZaragoza's answer.

    In case you use your API in combination with Doorkeeper, you could do something similar, but instead of checking for the authentication_token in the User table/model you look for the token in the OauthAccessTokens table, i.e.

    def authenticated
       return true if warden.authenticated?
       params[:access_token] && @user = OauthAccessToken.find_by_token(params[:access_token]).user
    end
    

    This is more safe, because that token (i.e. the actual access_token) exists only for a certain amount of time.

    Note in order to be able to do this you must have a User model and OauthAccessToken model, with:

    class User < ActiveRecord::Base
    
       has_many :oauth_access_tokens
    
    end
    
    class OauthAccessToken < ActiveRecord::Base
        belongs_to :user, foreign_key: 'resource_owner_id'
    end
    

    EDIT: Please also note that generally you should not include the access_token in the URL: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-16#section-2.3

    0 讨论(0)
  • 2020-12-13 00:10

    Add token_authenticable to devise modules (works with devise versions <=3.2)

    In user.rb add :token_authenticatable to the list of devise modules, it should look something like below:

    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
    

    Generate Authentication token on your own (If devise version > 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
    

    Add migration for authentiction token

    rails g migration add_auth_token_to_users
          invoke  active_record
          create    db/migrate/20141101204628_add_auth_token_to_users.rb
    

    Edit migration file to add :authentication_token column to users

    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
    

    Run migrations

    rake db:migrate

    Generate token for existing users

    We need to call save on every instance of user that will ensure authentication token is present for each user.

    User.all.each(&:save)

    Secure Grape API using auth token

    You need to add below code to the API::Root in-order to add token based authentication. If you are unware of API::Root then please read Building RESTful API using Grape

    In below example, We are authenticating user based on two scenarios – If user is logged on to the web app then use the same session – If session is not available and auth token is passed then find user based on the token

    # 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
    
    0 讨论(0)
提交回复
热议问题