I was writing an API in rails a while back and as I started to write the client I noticed that, while using Devise’s TokenAuthenticatable strategy, there was no other way to pass the token in besides the ?token=XXXX
parameter.
This was a little odd since you would expect something like a token to be able to be passed in through the Authorization headers. I ended up writing a custom devise strategy that will now allow you to pass your user’s token in via the Authorization header.
Note: This is dependent on rails’ ActionController::HttpAuthentication::Token
parser, but that can easily be removed if you need to. The token is pretty simple to deconstruct.
Note 2: I submitted a pull request which was merged into devise/master - see
plataformatec/devise PullRequest #2271 for some context.
Here is the gist, should you not be able to update to the latest Devise.
View Gist
File: README.md
---------------
This will allow you to use devise's TokenAuthenticatable strategy while passing your :auth_token in using HTTP Authorization headers instead of params.
You can check it out using
```sh
curl -H "Authorization: Token token=XXXXXX, option=1" -vv http://railsapp.dev/api/v1/endpoint.json
```
It should properly authenticate using the token as well as set `request.env['token.options']` to `{"options" => "1"}`.
File: api_authenticatable.rb
----------------------------
# File: lib/devise/strategies/api_authenticatable.rb
require 'devise/strategies/token_authenticatable'
module Devise
module Strategies
class ApiAuthenticatable < TokenAuthenticatable
private
# Try to rip out authorization and override our authentication_key
def params_auth_hash
return_params = super
token_key = mapping.to.token_authentication_key
token = ActionController::HttpAuthentication::Token
.token_and_options(request)
if token
return_params.merge! token_key => token.first
request.env['token.options'] = token.last || {}
end
return_params
end
end
end
end
Warden::Strategies.add(
:api_authenticatable,
Devise::Strategies::ApiAuthenticatable
)
File: devise.rb
---------------
# File: config/initializers/devise.rb
require 'devise/strategies/api_authenticatable'
Devise.setup do |config|
# ...
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :api_authenticatable
end
# ...
end
File: example_spec.rb
---------------------
describe 'authenticating with Authorization header' do
let(:token_key){ Devise.token_authentication_key }
let!(:user){ create :user }
let(:token){ user.authentication_token }
controller(described_class) do
def index
head :ok
end
end
before do
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Token.encode_credentials(
token, {hello: 'world'})
end
it 'sets env[token.options]' do
json_get :index
request.env['token.options'] == {'hello' => 'world'}
end
context 'when auth_token param is missing' do
it 'sets the auth token from Authorization header' do
json_get :index
controller.params[token_key].should eq token
response.should be_success
end
end
context 'when auth_token param is present' do
it 'it overrides the param' do
json_get :index, auth_token: 'bad token'
controller.params[token_key].should eq token
response.should be_success
end
end
end