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.
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
From the response, we need to grab three of the headers for our authentication.
client
uid
access-token
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
We can remedy this by putting in all three required headers:
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
After usage of a token, it will expire soon-after.
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.