基本機能だけのシンプルなdevise_token_authを使ったAPIを試してみる（初心者向け）

フロントエンドをreactなどでSPA的な実装をしたいときにバックエンドのAPIを最低限のセキュリティにも配慮した形で実装したいと思い、devise_token_authでトークンベースの認証方法を採用したいと考えて、devise_token_authについて試行錯誤した結果を整理した記事です。

初心者ゆえ、できる限りシンプルな形での実装から練習を始めたかったのですが、発展的な記事はよく見かけるものの「基本のき」から説明している記事はあまり見かけなかったので、Qiitaに覚書きがてら書いている次第です。

変な部分がありましたら、ぜひご指摘ください。


# 実行環境と要件

実行環境は以下の通り

- Rails 7.0.4.3
- devise 4.9.2
- devise_token_auth 1.2.1

要件としては、とにかくシンプルなAPIで練習するために

- User認証機能（新規登録、ログイン）
- deviseのデフォルトであるメールアドレスとパスワードでの登録およびログイン
- おまけでAPIのバージョン管理をできるように

といったところを実現することを目指します。

# 実装フロー

以下の通り、実行していきます。

1. 新しいRailsプロジェクトを作成。
    
    ```
    
    rails new my_api --api
    
    ```
    
2. Gemfileにて`rack-cors`を有効化し、`devise`と`devise_token_auth`を追加。
    
    ```ruby
    gem 'rack-cors' #コメントアウトを外す    

    gem 'devise'
    gem 'devise_token_auth'
    
    ```
    
3. Gemのインストールを実行。
    
    ```
    
    bundle install
    
    ```
    
4. Devise のインストールジェネレータを実行したのちに、Devise Token Authのインストールジェネレータを実行。（devise_token_authの公式ドキュメントには載っていないが、先にdeviseをインストールしておかないとエラーを吐く）
    
    ```ruby
    #先にdeviseをインストール
    rails g devise:install

    #続いてdevise_token_authをインストール（ここでUserモデルやマイグレーションファイルなどが生成）
    rails g devise_token_auth:install User auth
    
    ```
    
    
5. マイグレーションを実行してデータベースに必要なテーブルを作成。
    
    ```
    
    rails db:migrate
    
    ```

6. devise_token_authの設定を変更。

    initializer/devise_token_auth.rbにて、以下の３つを設定する。
    
    
    ```ruby
    # frozen_string_literal: true
    
    DeviseTokenAuth.setup do |config|
      
        config.change_headers_on_each_request = false#コメントアウト外してfalseに変更

        config.token_lifespan = 2.weeks#コメントアウト外す
    
    	config.headers_names = {:'access-token' => 'access-token',#コメントアウト外す
                             :'client' => 'client',
                             :'expiry' => 'expiry',
                             :'uid' => 'uid',
                             :'token-type' => 'token-type'}
    ```
    
    さらに同じinitialize/devise_token_auth.rbファイルにて、実はconfig.headers_namesの設定で、以下のauthorizationの設定を追加しないとエラーを吐く。
    
    ```ruby
    config.headers_names = {:'access-token' => 'access-token',
                             :'client' => 'client',
                             :'expiry' => 'expiry',
                             :'uid' => 'uid',
                             :'token-type' => 'token-type',
                             :'authorization' => 'authorization'#この1行を追加
                            }
    ```
    
    表示されるエラー内容としては、以下の通り。
    
    ```ruby
    NoMethodError: undefined method `downcase' for nil:NilClass
    ```
    
7. ルーティングを設定。
    
    ```ruby
    
    # config/routes.rb
    
    Rails.application.routes.draw do
      mount_devise_token_auth_for 'User', at: 'auth'
    end
    
    ```
    
    
8. `User`モデルで`devise`と`devise_token_auth`を設定します。
    
    ```ruby
    
    # app/models/user.rb
    
    class User < ApplicationRecord
      # Include default devise modules.
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :validatable
    
      # Include additional devise modules.
      include DeviseTokenAuth::Concerns::User
    end
    
    ```
    
9. サーバーを起動します。
    
    ```
    
    rails s
    
    ```
    
以上で、最もシンプルな形のdevise_token_authを使ったAPIはこれで実装できるはず。
    
この場合は、ユーザー登録とログインのためのエンドポイントは以下の通りとなる。
    
- 新規登録: `POST /auth`
- ログイン: `POST /auth/sign_in`
    
これらのエンドポイントに必要なパラメータを送信し、ユーザーの作成とログインを行うことができます。


# おまけ

APIに関してはバージョン管理するのが一般的なようで、「v1」「v2」などの形で名前空間を作って管理しているのをよく見かけるので、その実装手順もメモしておきます。

１〜６までの手順は同じ。

### 7.ルーティング設定

バージョン管理するとなると、新規登録のエンドポイントが

`/v1/auth`

に変わるので、まずはルーティングの変更が必要。
    
```ruby
    
# config/routes.rb
    
Rails.application.routes.draw do
    namespace :v1 do
        mount_devise_token_auth_for 'User', at: 'auth'
    end
end
    
```

### 8.コントローラの作成と設定

ルーティングが変わったことで、コントローラの読み込み先が変わったので、その変更を加えます。

`devise_token_auth`はデフォルトでは`application_controller.rb`のコントローラしか生成しません。

その中身は以下の通り。

```ruby:application_controller.rb
class ApplicationController < ActionController::API
        include DeviseTokenAuth::Concerns::SetUserByToken
end

```

ユーザーの新規登録などのコントローラはクラスを継承することで動作するようになっていることがわかります。

なので、新しく今のルーティングに合わせたコントローラを作成し、そこに必要なクラスを継承させることが必要です。

まずはコントローラを作成するために以下のコマンドを実行します。

```
rails g controller v1/auth/registrations
```

当初私が参考にしていたサイトではここのコントローラファイルの作成ディレクトリを`v1/registrations`で指定していたので、うまく読み込めずにエラーが出て、半日ぐらいハマってしまいました。。。きっとあれば誤植。。。

ルーティングの通りのディレクトリを指定しないとダメなので一応要確認です。

これが済んだら、ディレクトリ構成は以下の感じになっているかと思います。

![スクリーンショット 2023-05-20 11.17.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073877/9ee94792-e908-4575-e265-c98656550d7f.png)

続いてregistrationsコントローラの中身を整えます。

たぶんデフォルトでは以下のクラスが設定されているはずです。

```ruby:registrations_controller.rb

class V1::Auth::RegistrationsController < ActionController::API
end

```

これを以下のように`DeviseTokenAuth::RegistrationsController`クラスを継承させるように変更することで、ルーティングに応じたコントローラの設定になります。

```ruby

class V1::Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController
end

```

### 9.ルーティングの修正

このようにコントローラの設定をいじったことで、ルーティングを再度修正する必要がある場合があります。

コントローラの読み込み先を明示してあげる必要があるみたいです。（これは環境によるのかちょっと不明）

```ruby:config/routes.rb
    
Rails.application.routes.draw do
    namespace :v1 do
        mount_devise_token_auth_for 'User', at: 'auth', controllers: {
            registrations: 'v1/auth/registrations'
        }
    end
end
    
```

ここまでの設定ができたら、apiがきちんと動くかpostmanなどで確認してみましょう。


![スクリーンショット 2023-05-20 11.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073877/fc04c63c-3cb9-03a9-884a-01a371155834.png)

ちゃんと`status: 200 OK`が返ってきていますので、無事実装完了です！


# 調べるのにかなり時間かかった


ということで初心者が試しに動かしてみる、というレベルの記事がなかなか見当たらなかったので書いてみました。

ここまで調べて安定して実装できるようになるまでにかなり時間かかってしまいました。

しかしいい勉強になりました。



# 参考
    
https://qiita.com/ginger-yell/items/d69469f51c28bb05b03a