ruby-on-railsrubydevisewarden

Run warden strategy in devise when user is already authenticated


I'm using devise to authenticate my users via username / password or a couple of OAuth providers. Now I have to support a custom authentication Strategy that uses a token inside a header.

module WardenStrategies
  class ApiKey < Warden::Strategies::Base
    def valid?
      api_token.present?
    end

    def authenticate!
      user = User.find_by(api_token: api_token)

      if user
        success!(user)
      else
        fail!("Invalid email or password")
      end
    end

    private

    def api_token
      env["HTTP_AUTHORIZATION"].to_s.remove("Bearer ")
    end
  end
end

I've added the strategy to devise and warden

# config/initializers/devise.rb
Devise.setup do |config|
  # ...
  config.warden do |manager|
    manager.default_strategies(:scope => :user).unshift :api_key
  end
  # ...
end
# config/initializers/warden.rb
Warden::Strategies.add(:api_key, WardenStrategies::ApiKey)

The problem now is: I have a playground on the same site that sends requests to my API via JavaScript. In the playground I can provide a custom API key for a different user. But when the user is already logged in via a normal session, warden doesn't run the strategy any more, because it already has a User assigned.

So, how can I force warden / devise to try a strategy, even when the user is already authenticated?


Solution

  • Before calling any strategies warden checks if there's already a stored user in session (from previous requests, where some strategy with store?=true (the default) has succeeded)

    You can try faking a 'non-set' user (without full log out) by something like:

    # manager is Warden::Manager
    manager.prepend_on_request do |proxy|
      proxy.set_user(nil, scope: :user, store: false) if proxy.env["HTTP_AUTHORIZATION"].present?
    end
    

    PS. your strategy probably should also have def store?; false; end, as api keys are usually required with each request, and also should not result in persisted session