Forked from brunofacca/rails_api_token_auth_with_tiddle.md
Created
August 13, 2017 21:13
-
-
Save crifat/954a96ca1351d133a1d92f6fe0593fbc to your computer and use it in GitHub Desktop.
Revisions
-
brunofacca revised this gist
Oct 14, 2016 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,8 @@ This gist attempts to explain how to implement token authentication in Rails, using [Devise](https://github.com/plataformatec/devise) and [Tiddle](https://github.com/adamniedzielski/tiddle). Tiddle was designed to provide token auth for API-only Rails apps. However, the following instructions will enable you to use it in Rails apps which have both APIs **and** web views. ##Why Tiddle? -
brunofacca revised this gist
Oct 14, 2016 . 1 changed file with 105 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,38 @@ This gist attempts to explain how to implement token authentication in Rails, using [Devise](https://github.com/plataformatec/devise) and [Tiddle](https://github.com/adamniedzielski/tiddle). Tiddle was designed to provide token auth for API-only Rails apps. The following instructions will enable you to use it in Rails apps which have both APIs **and** web views. ##Why Tiddle? Devise is the obvious choice for authentication on Rails. However, token authentication was [deprecated in Devise 3.1](http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/). There are a few gems that provide token authentication for Rails apps with Devise: - [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication): Looks good if you need **very** simple token auth. However, it lacked some of the features my project required, such as supporting multiple tokens per user. - [Tiddle](https://github.com/adamniedzielski/tiddle/): Supports multiple tokens per user and its simple to implement. - [Devise Token Auth](https://github.com/lynndylanhurley/devise_token_auth): Looks a lot more robust than the above. If your project requires strong security and a full-featured token auth system, this is probably the best choice. However, the project I'm currently working on only requires simple token auth for a read-only API where security is not a major concern. I wanted to avoid unnecessary complexity, so this gem seemed like overkill. ##Prerequisites This tutorial assumes the following items: - You already have Devise configured to authenticate the web views of your Rails app. You want to use tokens to authenticate API (JSON) requests in the same Rails app. ## Installation and configuration 1. Add to the Gemfile: `gem 'tiddle'` @@ -13,7 +43,7 @@ If it's not, just replace User for your model name. 1. Generate the migration: `rails g model AuthenticationToken body:string user:references last_used_at:datetime ip_address:string user_agent:string` 2. Edit `app/models/user.rb` , so it looks similar to: ```` # app/models/user.rb @@ -25,88 +55,106 @@ If it's not, just replace User for your model name. end ```` Note that we have: - Added `has_many :authentication_tokens` - Added `:token_authenticatable` in the list of devise modules User is the default name for the model created by Devise to store it's users. You may have named it differently when installing Devise. 4. Customize the Sessions Controller to handle token authentication for the API: 1. Generate custom controllers: `rails generate devise:controllers User` Note that Devise controllers are contained inside the Devise gem by default. The above command will generate a set of Devise controller files in `app/controllers/users/` so we can customize them. 2. Edit `app/controllers/users/sessions_controller.rb`, so it looks like this: ```` module Api module V1 class Users::SessionsController < Devise::SessionsController skip_before_action :verify_signed_out_user def create user = warden.authenticate!(:scope => :user) token = Tiddle.create_and_return_token(user, request) render json: { authentication_token: token } end def destroy if current_user && Tiddle.expire_token(current_user, request) head :ok else # Client tried to expire an invalid token render json: { error: 'invalid token' }, status: 401 end end end end end ```` Our custom controller will have specific routes, which will only be used for JSON (API) requests. The default routes created by `devise_for` will remain unaltered, pointing to Devise's original session controller, which will keep being used by HTML requests (web view authentication). 5. Create routes to our custom controller by including the following lines in `config/routes.rb`: ```` # Devise routes for API clients (custom sessions controller) devise_scope :user do post 'api/v1/login', to: 'users/sessions#create' delete 'api/v1/logout', to: 'users/sessions#destroy' end # Devise routes for web clients (built-in sessions controller) devise_for :users ```` Note that `devise_for :users` (which should already exist in routes.rb) must be below our custom routes. 6. Include the following lines to `app/controllers/application_controller.rb.` ```` # Prevent CSRF attacks, except for JSON requests (API clients) protect_from_forgery unless: -> { request.format.json? } # Require authentication and do not set a session cookie for JSON requests (API clients) before_action :authenticate_user!, :do_not_set_cookie, if: -> { request.format.json? } private # Do not generate a session or session ID cookie # See https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L171 def do_not_set_cookie request.session_options[:skip] = true end ```` Make sure the `private` keyword (which is actually a method) and the `do_not_set_cookie` method definition are placed at the bottom of the controller to avoid making other (unrelated) methods private. ## Testing it with curl ```` # Get an auth token (send credentials in JSON format): curl -X POST "http://localhost:3000/api/v1/login.json" \ -H "Content-Type:application/json" \ -d '{ "user": { "email": "<email>", "password": "<password>" }}' # Use the token to authenticate a request (send credentials as headers) curl -X GET "http://localhost:3000/api/v1/<resource name>.json" \ -H "Content-type: application/json" \ -H "X-User-Email: <email>" \ -H "X-User-Token: <token>" # Log out (destroy the auth token): curl -X DELETE "http://localhost:3000/api/v1/sign_out.json" \ -H "Content-Type:application/json" \ -H "X-User-Email: <email>" \ -H "X-User-Token: <token>" ```` -
brunofacca created this gist
Oct 7, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,112 @@ This gist attempts to explain how to implement token authentication for APIs in Rails apps which also have web views, using the [Tiddle gem](https://github.com/adamniedzielski/tiddle). The following instructions assume that the Devise users model is called User. If it's not, just replace User for your model name. 1. Add to the Gemfile: `gem 'tiddle'` 2. Run `bundle install` 3. Set up a model to store the authentication tokens: 1. Generate the migration: `rails g model AuthenticationToken body:string user:references last_used_at:datetime ip_address:string user_agent:string` 2. Edit the User model so it looks similar to: ```` # app/models/user.rb class User < ActiveRecord::Base has_many :authentication_tokens devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :token_authenticatable end ```` Note that we have: - Included `has_many :authentication_tokens` - Included `:token_authenticatable` in the list of devise modules 4. Customize the Sessions Controller to handle token authentication for your API: 1. Generate custom controllers: `rails generate devise:controllers User` Note that Devise controllers are contained inside the Devise gem by default. The above command will generate a set of Devise controller files in app/controllers/users so we can customize them. 2. Edit `app/controllers/users/sessions_controller.rb` so it looks like this: ```` module Api module V1 class Users::SessionsController < Devise::SessionsController # API token authentication with the (Tiddle gem) # See ttps://github.com/adamniedzielski/tiddle/ def create user = warden.authenticate!(auth_options) token = Tiddle.create_and_return_token(user, request) render json: { authentication_token: token } end def destroy Tiddle.expire_token(current_user, request) if current_user render json: {} end private # this is invoked before destroy and we have to override it def verify_signed_out_user end end end end ```` Our custom controller will have specific routes, which will only be used for JSON (API) requests. The default routes created by `devise_for` will point to Devise's original controller, which will respond to HTML requests. 5. Create a route to our custom controller by including the following lines in `config/routes.rb`: ```` # Devise routes for API clients (custom sessions controller) devise_scope :user do post 'api/v1/sign_in', to: 'users/sessions#create' delete 'api/v1/sign_out', to: 'users/sessions#destroy' end # Devise routes for web clients (built-in sessions controller) devise_for :users ```` Note that `devise_for :users` must be below our custom routes. 6. Include the following code to `app/controllers/application_controller.rb`: ```` # Disable forgery protection for JSON requests (API clients) protect_from_forgery unless: -> { request.format.json? } # Require authentication for all JSON requests (API clients) before_action :authenticate_user!, if: lambda { |controller| controller.request.format.json? } ```` 6. Restart the rails server and test it with curl: ```` # Get an auth token (send credentials via POST, JSON): curl -X POST "http://localhost:3000/api/v1/sign_in.json" \ -H "Content-Type:application/json" \ -d '{ "user": { "email": "<email>", "password": "<password>" }}' # Use the token to authenticate a request curl -X GET "http://127.0.0.1:3000/masters.json" \ -H "Content-type: application/json" \ -H "X-User-Email: <email>" \ -H "X-User-Token: <token>" #Sign-out (destroy the auth token): curl -X DELETE "http://localhost:3000/api/v1/sign_out.json" \ -H "Content-Type:application/json" \ -H "X-User-Email: <email>" \ -H "X-User-Token: <token>" ````