# Devise Part 10: Devise Token Auth

> In part ten of the Devise series, we start a new project from scratch to explore how the devise-token-auth gem can be setup with a Rails 7 application.

- Canonical: https://dennisokeeffe.com/blog/2022-03-16-part-10-devise-token-auth
- Published: 2022-03-16
- Updated: 2022-03-16
- Tags: ruby, ruby-on-rails

This post will explore how we can use [`devise_token_auth`](https://github.com/lynndylanhurley/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](https://github.com/doorkeeper-gem/doorkeeper) to enable you to share a public API for your users.

Final source code can be found [here](https://github.com/okeeffed/devise-token-auth-example).

## Prerequisites

1. Basic familiarity with setting up a [new Rails project](https://guides.rubyonrails.org/getting_started.html).

## Getting started

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

```s
# 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`.

```rb
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`.

```s
# 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`](https://www.postman.com/) for demonstration purposes, we will update our settings to skip forgery.

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

```rb
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.

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

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

```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:

```rb
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:

```txt
        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](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/1-sign-up-and-response.webp)

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

1. `client`
2. `uid`
3. `access-token`

![Response headers](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/2-headers-response.webp)

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](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/3-response-with-no-token-provided.webp)

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

![Providing the token](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/4-providing-the-token.webp)

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](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/5-new-token-returned-from-request.webp)

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

![Expired token being used](https://cdn.dennisokeeffe.com/assets/2022-03-16-part-10-devise-token-auth/6-expired-token-being-used.webp)

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](https://github.com/doorkeeper-gem/doorkeeper) to create a v2 of the API that will be for public usage.

## Resources and further reading

- [Rails getting started](https://guides.rubyonrails.org/getting_started.html)
- [Source code](https://github.com/okeeffed/devise-token-auth-example)
- [`devise_token_auth`](https://github.com/lynndylanhurley/devise_token_auth)

_Photo credit: [`tods`](https://unsplash.com/@tods)_
