Search code examples
ruby-on-railsember.jsauthorizationruby-grapeember-simple-auth

How to handle authentication token string in Grape API and token_and_options?


I'm using Ember front-end with Grape API and to authenticate a page I have something like:

  def current_user
    return nil if headers['Authorization'].nil?

    @current_user ||= User.find_by_authentication_token(
      headers['Authorization']
    )
  end

At the moment I'm using ember-simple-auth which sends something like as a header:

Token token="n2Rs5tokenLH", email="[email protected]"

Check out the Grape logger:

  User Load (38.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."authentication_token" = 'Token token="n2Rs5tokenLH", email="[email protected]"' LIMIT 1

Now in rails I usually use authenticate_or_request_with_http_token to handle this. and I don't want to handle this manually using stuff like gsub or regular expressions. What is the best way to handle this in Grape?

Please note that the Grape API is mounted inside a rails project and inside app/api/backend/v1/default.rb:

module Backend
  module V1
    module Defaults
      extend ActiveSupport::Concern

      included do
        version 'v1', using: :path
        format :json
        prefix :api

        rescue_from :all do |e|
          Backend::Base.logger.error e
        end

        helpers do
          def current_user
            return nil if headers['Authorization'].nil?

            @current_user ||= User.find_by_authentication_token(
              headers['Authorization']
            )
          end

          def authenticate!
            error!('401 Unauthorized', 401) unless current_user
          end
        end
      end
    end
  end
end

Edit:

I just found out that I can use ActionController::HttpAuthentication::Token.token_and_options to do the job, however I'm not sure how should I include this in Grape project.

The structure of the app looks like:

⇒  tree app/api
app/api
└── backend
    ├── base.rb
    └── v1
        ├── defaults.rb
        └── ....

I tried to include ActionController::HttpAuthentication::Token in defaults.rbsomething like:

helpers do
  include ActionController::HttpAuthentication::Token

  def current_user
    return nil if headers['Authorization'].nil?

    @current_user ||= User.find_by_authentication_token(
      token_and_options(headers['Authorization'])
    )
  end

  def authenticate!
    error!('401 Unauthorized', 401) unless current_user
  end
end

But now I'm getting:

 undefined method `authorization' for #<String:0x007ff51cdb85f8>

Solution

  • I had to add a customized token_and_options function which looks like:

      def token_and_options(auth_string)
        if header = auth_string.to_s[/^Token (.*)/]
          values = $1.split(',').
            inject({}) do |memo, value|
              value.strip!
              key, value = value.split(/\=\"?/)
              value.chomp!('"')
              value.gsub!(/\\\"/, '"')
              memo.update(key => value)
            end
          values.delete("token")
        end
      end
    

    And then parse token with something like: token_and_options(headers['Authorization'])