Search code examples
ruby-on-railsrubyapidevisegrape-api

Configuring rails grape api with devise token


I am trying to configure token generation with devise in a grape api rails app. Since I have the current version of devise, token generation has been disabled. I am having several issues. First, is that when I submit a username and password to the sessions controller, it gives me an error that "ensure_authentication_token":

undefined method `ensure_authentication_token!' for #<User:0x007f880cca9090>

This is so strange because as you can see below, I have it defined in my user model and when I manually create Users in rails console, it works properly.

Is that a scope issue or why is that occurring?

User Model:

class User < ActiveRecord::Base
  before_save :ensure_authentication_token
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable


  def ensure_authentication_token
    if authentication_token.blank?
      self.authentication_token = generate_authentication_token
    end
  end

  private

    def generate_authentication_token
      loop do
        token = Devise.friendly_token
        break token unless User.where(authentication_token: token).first
      end
    end
end

Sessions Grape API Controller:

module API
  module V1
    class Sessions < Grape::API
      include API::V1::Defaults

      resource :sessions do

        params do
          requires :email, type: String, desc: "Email"
          requires :password, type: String, desc: "Password"
        end

       post 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.where(email: email.downcase).first
         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: 'ok', token: user.authentication_token}.to_json
         end
       end
      end
    end
  end
end

The second problem is that when I follow this blog, it says that I need to add the following authentication check in my defaults.rb in the base api controller. When I add the "before do" section, I get access denied error even if I enter in the right credentials and it doesn't even go on to the rest of the sessions controller that I mentioned above.

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

Thanks for any help you can give!

EDIT: Phillip was absolutely correct that one of these issues was due to the bang versus non banged version of ensure_authentication_token. Removing the ! from the controller fixed that issue. The other problem was indeed from adding the "before do" loop I had.

This is so close to working, I can give and receive tokens in my api but when it connects to ember, it complains about a lack of a csrf token even though I have "protect_from_forgery with: :null_session" set in my application.rb


Solution

  • In your User model, you define a method called ensure_authentication_token.

    In your Session controller, you call a method called ensure_authentication_token!.

    These are not the same method: Why are exclamation marks used in Ruby methods?

    This is preventing you from generating an authentication token, which probably explains the "401 Unauthorized, 401" error.