Back to home

Devise Part 10: Devise Token Auth main image

Devise Part 10: Devise Token Auth

This post will explore how we can use devise_token_auth to setup opaque tokens with our applications.

This is a great solution for SPAs or mobile apps that require authentication.

Each request will refresh the token and expires them in a short time. It also maintains sessions for each client which enables us to have multi-session capabilities for the same user on their different devices.

Part 10 will naturally move onto part 11 which will be around setting up simple authentication tokens with Doorkeeper to enable you to share a public API for your users.

Final source code can be found here.

Prerequisites

  1. Basic familiarity with setting up a new Rails project.

Getting started

We will use Rails to initialize the project devise-token-auth-example:

# Create a new rails project $ rails new devise-token-auth-example $ cd devise-token-auth-example

We need to add a number of gems for us to use devise_token_auth.

gem 'devise' gem 'devise_token_auth', '>= 1.2.0', git: "https://github.com/lynndylanhurley/devise_token_auth" gem 'omniauth'

The latest version on Git plays well with Rails 7, but at the time of writing a new version of the gem is yet to be released.

In the terminal, we now need to install these gems as well as generate the install helpers given from devise and devise_token_auth.

# Install gems $ bundle # Generate and setup files $ bin/rails g devise:install $ bin/rails g devise_token_auth:install User auth # Create and migrate DBs $ bin/rails db:create db:migrate

At this point, we have created a new User model and set it so the /auth endpoints will be where we authenticate.

Updating our application controller

As I will be using Postman for demonstration purposes, we will update our settings to skip forgery.

Update app/controllers/application_controller.rb to the following:

class ApplicationController < ActionController::Base skip_forgery_protection include DeviseTokenAuth::Concerns::SetUserByToken end

Creating a new controller to test against

We will be requiring a user authentication for the /api/v1/home route, so let's create a controller to demonstrate this.

# Generate controller $ bin/rails g controller api/v1/home

Update app/controllers/api/v1/home_controller.rb:

class Api::V1::HomeController < ApplicationController before_action :authenticate_user! def index render json: { message: 'Welcome to the API' } end end

Updating our routes

We need to update our config/routes.rb file to include our home controller as well as setting up devise_token_auth.

Update the file to the following:

Rails.application.routes.draw do mount_devise_token_auth_for 'User', at: 'auth' # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Defines the root path route ("/") # root "articles#index" namespace :api do namespace :v1 do resources :home, only: [:index] end end end

Setting up the mount_devise_token_auth_for method will provide us with a number of useful endpoint.

If you look through bin/rails routes, you'll find the following related to the /auth endpoints:

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 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 user_password PATCH /auth/password(.:format) devise_token_auth/passwords#update PUT /auth/password(.:format) devise_token_auth/passwords#update POST /auth/password(.:format) devise_token_auth/passwords#create cancel_user_registration GET /auth/cancel(.:format) devise_token_auth/registrations#cancel 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 user_registration PATCH /auth(.:format) devise_token_auth/registrations#update PUT /auth(.:format) devise_token_auth/registrations#update DELETE /auth(.:format) devise_token_auth/registrations#destroy POST /auth(.:format) devise_token_auth/registrations#create auth_validate_token GET /auth/validate_token(.:format) devise_token_auth/token_validations#validate_token auth_failure GET /auth/failure(.:format) devise_token_auth/omniauth_callbacks#omniauth_failure GET /auth/:provider/callback(.:format) devise_token_auth/omniauth_callbacks

We can use the POST /auth/sign_in route to authenticate our user in our clients, as well as a POST request to /auth to sign up new users.

Signing our can be done DELETE /auth/sign_out.

Testing our authentication with Postman

We can sign up a new user by create a POST request to /auth with email, password and password_confirmation fields:

Signing up

Signing up

From the response, we need to grab three of the headers for our authentication.

  1. client
  2. uid
  3. access-token
Response headers

Response headers

If we make a request to our /api/v1/home route without providing the required headers, you will receive an unauthorized response.

No token provided

No token provided

We can remedy this by putting in all three required headers:

Providing the token

Providing the token

After using the token in a request, a new one will be given back and you need to track it between requests.

New token returned after a request

New token returned after a request

After usage of a token, it will expire soon-after.

Expired token being used

Expired token being used

Amazing! We have gone through a workflow of what accessing protected endpoints will look like.

Summary

Today's post demonstrated how to authenticate a user using devise_token_auth. This method makes use of opaque tokens and can be highly secure in how it works.

In the next part, we will extend this application by using Doorkeeper to create a v2 of the API that will be for public usage.

Resources and further reading

Photo credit: tods

Personal image

Dennis O'Keeffe

@dennisokeeffe92
  • Melbourne, Australia

Hi, I am a professional Software Engineer. Formerly of Culture Amp, UsabilityHub, Present Company and NightGuru.
I am currently working on workingoutloud.dev, Den Dribbles and LandPad .

1,200+ PEOPLE ALREADY JOINED ❤️️

Get fresh posts + news direct to your inbox.

No spam. We only send you relevant content.