Simple Token Authentication for Rails JSON API
I have a Rails application (it is mostly JSON API) I am using Devise to authenticate using a JSON request from any source (web, mobile) and I am using Simple Token Authentication to authenticate users using HTTP headers.
I'm not sure what the implementation should look like, but I've come up with an implementation that almost works.
There is only one problem. and this is when the user tries to log out ... Usually it should revoke the authentication token for the user ... but it is not, I am not sure where the problem really is ... whether it works with Devise or Simple Token Auth ... so any help is greatly appreciated.
but here is the code
# router
devise_for :users, controllers: { sessions: 'sessions',
registrations: 'registrations' }
api vendor_string: 'app', default_version: 1, path: '', format: 'json' do
version 1 do
cache as: 'v1' do
resource :some_resource
end
end
the session controller is like this
class SessionsController < Devise::SessionsController
respond_to :json
skip_filter :verify_signed_out_user, only: :destroy
def create
self.resource = warden.authenticate!(scope: resource_name)
render :create, status: :created
end
def destroy
current_user.authentication_token = nil
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ take note of this line
current_user.save!
super
end
end
the previous marked line seems to have a problem ... when the user provides the wrong token header, it still works and the current user is referencing a user that should not be authenticated in the first place .. eg here 2 calls, spec
describe 'DELETE sessions#destroy' do
let(:user) { Fabricate(:confirmed_user) }
let(:auth_token) { user.authentication_token }
describe 'with request headers' do
context 'valid credentials' do
it 'Returns 204' do
delete '/users/sign_out', {}, {
HTTP_CONTENT_TYPE: 'application/json',
HTTP_ACCEPT: "application/vnd.app+json; version=1",
"X-User-Email" => user.email,
"X-User-Token" => user.authentication_token
}
user.reload
expect(response.status).to eq 204
expect(user.authentication_token).not_to eq auth_token
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is ok cause it the valid user
end
end
context 'invalid credentials' do
it 'Returns 204' do
delete '/users/sign_out', {}, {
HTTP_CONTENT_TYPE: 'application/json',
HTTP_ACCEPT: "application/vnd.app+json; version=1",
"X-User-Email" => user.email,
"X-User-Token" => 'Invalid'
}
user.reload
expect(response.status).to eq 204
expect(user.authentication_token).to eq auth_token
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this FAILS
# why did the user get new auth token when didn't sign out ????
end
end
end
this is also reported on github
and for completeness, here is the application controller
class ApplicationController < ActionController::Base
# The API responds only to JSON
respond_to :json
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
# default to protect_from_forgery with: :exception
protect_from_forgery with: :null_session
# Token Authenticatable for API
acts_as_token_authentication_handler_for User
end
source to share
From the simple_authentication_token page on github, it current_user.authentication_token
will be automatically generated if it was empty (zero) every time current_user is saved.
Assuming the user is a user instance that is the token authenticate: every time the user is saved and user.authentication_token.blank? he receives a new and unique authentication token (via Devise.friendly_token).
Update
Add acts_as_token_authentication_handler_for User
to yoursessions_controller.rb
source to share
Please read https://github.com/gonzalo-bulnes/simple_token_authentication/issues/224 . I believe this is normal behavior. You need to remove the token on the client side (device)
source to share