This project was forked from the original project Knock
Simple JWT authentication for Rails API
Doorbell is an authentication solution for Rails API-only application based on JSON Web Tokens.
- It's lightweight.
- It's tailored for Rails API-only application.
- It's stateless.
- It works out of the box with Auth0.
Yes.
Add this line to your application's Gemfile:
gem 'doorbell'Then execute:
$ bundle install
Finally, run the install generator:
$ rails generate doorbell:install
It will create the following initializer config/initializers/doorbell.rb.
This file contains all the informations about the existing configuration options.
Doorbell makes one assumption about your user model:
It must have an from_token_payload method, to generate a user from a token
Include the Doorbell::Authenticable module in your ApplicationController
class ApplicationController < ActionController::API
include Doorbell::Authenticable
endYou can now protect your resources by calling authenticate_user as a before_action
inside your controllers:
class SecuredController < ApplicationController
before_action :authenticate_user
def index
# etc...
end
# etc...
endYou can access the current user in your controller with current_user.
If no valid token is passed with the request, Doorbell will respond with:
head :unauthorized
You can modify this behaviour by overriding unauthorized_entity in your controller.
You also have access directly to current_user which will try to authenticate or return nil:
def index
if current_user
# do something
else
# do something else
end
endNote: the authenticate_user method uses the current_user method. Overwriting current_user may cause unexpected behaviour.
You can do the exact same thing for any entity. E.g. for Admin, use authenticate_admin and current_admin instead.
If you're using a namespaced model, Doorbell won't be able to infer it automatically from the method name. Instead you can use authenticate_for directly like this:
class ApplicationController < ActionController::Base
include Doorbell::Authenticable
private
def authenticate_v1_user
authenticate_for V1::User
end
endclass SecuredController < ApplicationController
before_action :authenticate_v1_user
endThen you get the current user by calling current_v1_user instead of current_user.
The entity model (e.g. User) can implement specific methods to provide
customization over different parts of the authentication process.
- Find the authenticated entity from the token payload (when authenticating a request)
By default, Doorbell assumes the payload as a subject (sub) claim containing the entity's id
and calls find on the model. If you want to modify this behaviour, implement within
your entity model a class method from_token_payload that takes the
payload in argument.
E.g.
class User < ActiveRecord::Base
def self.from_token_payload payload
# Returns a valid user, `nil` or raise
# e.g.
# self.find payload["sub"]
end
end- Modify the token payload
By default the token payload contains the entity's id inside the subject (sub) claim.
If you want to modify this behaviour, implement within your entity model an instance method
to_token_payload that returns a hash representing the payload.
E.g.
class User < ActiveRecord::Base
def to_token_payload
# Returns the payload as a hash
end
endTo authenticate within your tests:
- Create a valid token
- Pass it in your request
e.g.
class SecuredResourcesControllerTest < ActionDispatch::IntegrationTest
def authenticated_header
token = Doorbell::AuthToken.new(payload: { sub: users(:one).id }).token
{
'Authorization': "Bearer #{token}"
}
end
it 'responds successfully' do
get secured_resources_url, headers: authenticated_header
assert_response :success
end
endIf no ActiveRecord is used, then you will need to specify what Exception will be used when the user is not found with the given credentials.
Doorbell.setup do |config|
# Exception Class
# ---------------
#
# Configure the Exception to be used (raised and rescued) for User Not Found.
# note: change this if ActiveRecord is not being used.
#
# Default:
config.not_found_exception_class_name = 'MyCustomException'
endThe JWT spec supports different kind of cryptographic signing algorithms.
You can set token_signature_algorithm to use the one you want in the
initializer or do nothing and use the default one (HS256).
You can specify any of the algorithms supported by the jwt gem.
If the algorithm you use requires a public key, you also need to set
token_public_key in the initializer.
To enable cross-origin resource sharing, check out the rack-cors gem.
MIT