Installation
Add the following to your Gemfile:
gem 'devise_token_auth'
gem 'devise', '~> 4.0.0.rc1'
Then install the gem using bundle:
bundle install
Creating User Model and Migration
Next, let's create a model for authentication.
rails g devise_token_auth:install User auth
It will return such things at console:
Running via Spring preloader in process 78994
create config/initializers/devise_token_auth.rb
create db/migrate/20160328130022_devise_token_auth_create_users.rb
create app/models/user.rb
insert app/controllers/application_controller.rb
gsub config/routes.rb
Since we don't need omniauth at the moment, we should remove the configure(:omniauthable) in model:
class User < ActiveRecord::Base
# Include default devise modules.
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
include DeviseTokenAuth::Concerns::User
end
Migration comes next!
rake db:migrate
We may get an error while using devise that version is lower that 4.0.0:
DEPRECATION WARNING: alias_method_chain is deprecated. Please, use Module#prepend instead.....
NameError: uninitialized constant ActionController::RackDelegation...
Routes
Run this command to show all routes:
rake routes
The result is:
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
new_user_session | GET | /auth/sign_in(.:format) | devise_token_auth/sessions#new |
user_session | POST | /auth/sign_in(.:format) | devise_token_auth/sessions#create |
destroy_user_session | DELETE | /auth/sign_out(.:format) | devise_token_auth/sessions#destroy |
user_password | POST | /auth/password(.:format) | devise_token_auth/passwords#create |
new_user_password | GET | /auth/password/new(.:format) | devise_token_auth/passwords#new |
edit_user_password | GET | /auth/password/edit(.:format) | devise_token_auth/passwords#edit |
PATCH | /auth/password(.:format) | devise_token_auth/passwords#update | |
PUT | /auth/password(.:format) | devise_token_auth/passwords#update | |
cancel_user_registration | GET | /auth/cancel(.:format) | devise_token_auth/registrations#cancel |
user_registration | POST | /auth(.:format) | devise_token_auth/registrations#create |
new_user_registration | GET | /auth/sign_up(.:format) | devise_token_auth/registrations#new |
edit_user_registration | GET | /auth/edit(.:format) | devise_token_auth/registrations#edit |
PATCH | /auth(.:format) | devise_token_auth/registrations#update | |
PUT | /auth(.:format) | devise_token_auth/registrations#update | |
DELETE | /auth(.:format) | devise_token_auth/registrations#destroy | |
user_confirmation | POST | /auth/confirmation(.:format) | devise_token_auth/confirmations#create |
new_user_confirmation | GET | /auth/confirmation/new(.:format) | devise_token_auth/confirmations#new |
GET | /auth/confirmation(.:format) | devise_token_auth/confirmations#show | |
auth_validate_token | GET | /auth/validate_token(.:format) | devise_token_auth/token_validations#validate_token |
As shown above, /auth includes all the action related to authentication.
Authenticate
Let's create a Listing model to show how it works.
rails g scaffold listing content:text title:string
After migration, add one line code to application_controller.rb
#application_controller.rb
class ListingsController < ApplicationController
before_action :authenticate_user!
...
end
All right, you should get 401 while visit http://localhost:3000/listings/1.
Sign up
Firstly, wo don't need users confirm their registration with their own email, so we remove :confirmable in models/user.rb.
Sending a post requset to http://localhost:3000/auth with the parameters(email, password, password_confirmation) would create a user.
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 3a9aa5e9-2ba7-7de3-66b4-ec3a85f6ddd0" "http://localhost:3000/auth?email=zhen6939@163.com&password=51190109&password_confirmation=51190109"
Sign in
According to the routes sheet, implement 'sign in' needs a post request to */auth/sign_in *:
curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 2c7e777b-031d-c958-79e7-04ef75fe9bd5" "http://localhost:3000//auth/sign_in?email=zhen6939@163.com&password=51190109"
It returns access-token, client, expiry, token-type, uid.
param | description |
---|---|
access-token | This serves as the user's password for each request. A hashed version of this value is stored in the database for later comparison. This value should be changed on each request. |
client | This enables the use of multiple simultaneous sessions on different clients. (For example, a user may want to be authenticated on both their phone and their laptop at the same time.) |
expiry | The date at which the current session will expire. This can be used by clients to invalidate expired tokens without the need for an API request. |
uid | A unique value that is used to identify the user. This is necessary because searching the DB for users by their access token will make the API susceptible to timing attacks. |
Using those params properly, we can kindly keep user login at different devices.
How the authentication works
As shown above, access-token is used as 'password' for each request. By default, It should be changed after every request to server. At other words, every response of request to server will return different access-token. It means that every request you sent should include access-token which you got at previous request.