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?
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