Rails deviseの導入方法と仕様・仕組み

# deviseとは

ユーザー管理機能の実装を補助してくれるRubyのgem(ライブラリ)です。
新規登録・ログイン・ログアウトやメール認証・SNS認証等の機能を実装でき、セキュリティ的にも安全なものが作れます。

# devise導入

Railsのアプリケーションディレクトリ直下にある`Gemfile`の末尾に以下を追記します。

```Gemfile
gem 'devise'
```

ターミナルでRailsのアプリケーションディレクトリに移動し、以下コマンドを実行してインストールします。

```zsh
bundle install # gemインストール
rails g devise:install # deviseの設定ファイル生成
```

# 認証用のモデルを作成

認証用のモデルを作成します。以下ではuserモデルを作成していますが`customer`や`mail_user`、`admin`等好きなモデル名でOKです。

```zsh
rails g devise user
```

migrationファイルやmodelファイルが生成されます。
`rails g model user`との違いは以下3点です。

- migrationファイルに`email`カラムや`encrypted_password`カラム等の記述が最初からされているものが生成される
- modelファイルに`devise :database_authenticatable, ~ ::validatable`の記述が最初からされているものが生成される
- `routes.rb`に`devise_for :モデル名`の記述が最初からされているものが生成される

# migrationファイルの編集・実行

migrationファイルを編集します。
必要に応じて`name`カラム等追加したいカラムを追加します。
また以下機能のカラムを必要に応じてコメントアウト・アンコメントします。

| 機能          | 概要         | デフォルト             |
|:-------------|:------------|:------------:|
| Recoverable  | パスワードリセット機能 | 有効 |
| Rememberable | ログイン状態を保持する機能 | 有効 |
| Trackable    | ログイン回数や最終ログイン日時等の追跡をする機能 | 無効
| Confirmable  | メール認証機能 | 無効 |
| Lockable     | アカウントロック機能(指定回数連続で認証失敗するとアカウントをロックする機能) | 無効 |

`rails g db:migrate`を実行してDBのテーブルを生成します。

# コントローラーの作成

deviseのデフォルトの動作を改変する場合は`rails g devise:controllers users`でコントローラーを作成します。
`app/models/users/`ディレクトリ以下に複数のコントローラーが生成されるので、必要なもの(改変したいコントローラー)のみ`routes.rb`に以下のように記述して読み込ませます。

```ruby:routes.rb
      devise_for :users, controllers: {
        sessions: 'users/sessions'
      }
```

# ビューの生成

deviseのデフォルトのビューを改変する場合は`rails g devise:views users`でビューを作成します。
`app/views/users/`ディレクトリ以下にビューファイルが生成されるので、改変したい箇所を編集します。

# モデルの設定

modelファイルに記述されている`devise :database_authenticatable, ~ ::validatable`を必要に応じて削除・追記します。以下各説明です。
これらの設定はdeviseのmodel作成コマンドではなく通常の`rails g model ~`コマンドで生成したモデルにも設定できます。

## `:database_authenticatable`

メールとパスワードを使った認証をしたい場合に設定します。
パスワード保存時に自動でハッシュ化してくれたり、ログイン・ログアウトのセッション管理もしてくれます。
これを設定したモデルに`email`と`encrypted_password`カラムが無いとエラーになります。
`routes.rb`で`devise_for`にこれを設定したモデルを指定すると、ログイン・ログアウト用のルーティングが設定されます。

- `session#new`
- `session#create`
- `session#destroy`
モデルにクラスメソッドやインスタンスメソッドを追加します。詳細は以下のドキュメントに記載されています。
[https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/DatabaseAuthenticatable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/DatabaseAuthenticatable)

## `:authenticatable`

メールとパスワードを使った認証以外の認証をしたい場合に設定します。
SNS認証用のモデルにはこちらを設定するのが良さそうです。
`:database_authenticatable`と異なり、ログイン・ログアウト用のルーティングは追加されません。
モデルにクラスメソッドやインスタンスメソッドを追加します。詳細は以下のドキュメントに記載されています。
[https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Authenticatable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Authenticatable)

## `:registerable`

新規登録機能を実装したい場合に設定します。
SNS認証用のモデルには必要ありません。
`routes.rb`で`devise_for`にこれを設定したモデルを指定すると、新規登録用のルーティングが設定されます。

- `registrations#cancel`
- `registrations#new`
- `registrations#edit`
- `registrations#update`
- `registrations#update`
- `registrations#destroy`
- `registrations#create`

## `:validatable`

メールとパスワードのバリデーションを追加します。
自分でバリデーションを設定する場合は必要ありません。
追加されるバリデーションは以下の通りです。

- メールアドレスの入力が必須であり、有効な形式であり、一意であること(登録済みでないこと)
  - 有効な形式は正規表現で`/\A[^@\s]+@[^@\s]+\z/`とされています。
  - `@`が1個含まれで、かつ、その前後に`@`とスペース以外の文字が1文字以上必要って意味...だと思います。
- パスワードの入力が必須であり、文字数が6~128文字の範囲であり、パスワードと確認用パスワードが一致していること

- [https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Validatable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Validatable)
- [https://github.com/heartcombo/devise/blob/master/lib/devise.rb#L116](https://github.com/heartcombo/devise/blob/master/lib/devise.rb#L116)

## `:confirmable`

メール認証機能を実装する場合に設定します。
`routes.rb`で`devise_for`にこれを設定したモデルを指定すると、新規登録用のルーティングが設定されます。

- `confirmations#new`
- `confirmations#show`
- `confirmations#create`

アカウント登録時に認証用トークンを生成し、認証用トークンを付与した本登録用URLを記載したメールをユーザーに送信してくれます。
(`confirmed_at`が空の状態で保存されると、メールが送信されます。)

これを利用する時はRailsの送信用メールサーバーの設定をする必要があります。

- [https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Confirmable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Confirmable)
- [https://github.com/heartcombo/devise/blob/master/lib/devise/models/confirmable.rb](https://github.com/heartcombo/devise/blob/master/lib/devise/models/confirmable.rb)

## `:omniauthable`

OAuth認証(SNS認証等)を実装する場合に設定します。
別途gemをインストールする必要があります。
`routes.rb`で`devise_for`にこれを設定したモデルを指定すると、OAuth認証のコールバックURLのルーティングが設定されます。

## `:recoverable`

パスワードリセット機能を実装する場合に設定します。

## `:rememberable`

ログイン状態を保持する機能を実装する場合に設定します。

## `:lockable`

アカウントロック機能を実装する場合に設定します。
指定回数ログインに失敗するとアカウントをロックします。
アカウントロックの解除方法はメールでの解除・時間経過での解除の２つ用意されており、両方を設定することも可能です。
[https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Lockable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Lockable)

## `:timeoutable`

ログインセッションの有効期限機能を実装する場合に設定します。
設定された有効期限を過ぎるとログインページへリダイレクトされます。
[https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Timeoutable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Timeoutable)

## `:trackable`

ログイン回数やログイン日時・IPアドレスを記録する機能を実装する場合に設定します。

- `sign_in_count`      : ログイン回数
- `current_sign_in_at` : 最終ログイン日時
- `last_sign_in_at`    : その前のログイン日時
- `current_sign_in_ip` : 最終ログイン時のIPアドレス
- `last_sign_in_ip`    : その前のログイン時のIPアドレス
[https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Trackable](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models/Trackable)

# ルーティングの設定

`devise_for`や`devise_scope`でdeviseのルーティングを追加します。
複数の認証用モデルが存在する場合はそれぞれの認証用モデルに設定することが可能です。

`devise_for :モデル名(複数形)`ではそのモデルにdeviseメソッドで設定されてる内容を読み取ってそれに対応したルーティングを追加します。
deviseのデフォルトのコントローラーを改変する場合はコントローラーを指定します。
以下はセッション管理コントローラーと新規登録用コントローラーとメール認証のコントローラーを設定している例です。

```ruby:routes.rb
      devise_for :users, controllers: {
        sessions: 'users/sessions',
        registrations: 'users/registrations',
        confirmations: 'users/confirmations'
      }
```

独自のルーティングを追加する場合は`devise_scope`を利用します。
こちらは`devise_scope :モデル名(単数形)`にします。以下は例です。

```ruby:routes.rb
      devise_scope :user do 
        patch 'users/confirmation',   to: 'users/confirmations#confirm'
      end
```

# deviseのヘルパーメソッド

deviseは以下のような便利なヘルパーメソッドを用意してくれています。
以下`user`モデルの例です。`user`以外の名前のモデル名を付けている場合はメソッド名もモデル名に対応して変わります。
(`admin`モデルなら`admin_sign_in?`等)

- `user_sign_in?`: ユーザーがログインしているか`true`/`false`で返します。
- `current_user`: 現在ログイン中のユーザーのモデルインスタンスを返します。ログインしていなければ`nil`を返します。
- `before_action :authenticate_user!`: ログインしていなければログインページへ飛ばします。
  - ApplicationControllerまたは個別のコントローラーに設定します。
  - 特定のページに設定したい場合は`only`や`except`オプションを利用します。

`user_sign_in?`メソッドは全てのコントローラー・ビュー内で利用できます。
`current_user`メソッドは`devise_for`が呼び出された際に指定したモデル名に合わせて、その`devise_for`で設定されるコントローラーのアクション・及びそのビューでのみ利用できます。

# ストロングパラメーター

`:database_authenticatable`を設定しているモデルにカラムを追加した場合は以下のようにストロングパラメーターを設定します。
以下の例では`last_name`カラムと`first_name`カラムを設定しています。
`user_details`等の別のモデルに追加カラムを分離している場合は以下の設定は不要です。

```ruby:application_controller.rb
  before_action :configure_permitted_parameters, if: :devise_controller?

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:last_name, :first_name])
  end
```

# 参考

- 【Rails】deviseを導入してみる
  - [https://qiita.com/Hal_mai/items/350c400e8763ce0487a3](https://qiita.com/Hal_mai/items/350c400e8763ce0487a3)
- Rails deviseで使えるようになるヘルパーメソッド一覧s
  - [https://qiita.com/tobita0000/items/866de191635e6d74e392](https://qiita.com/tobita0000/items/866de191635e6d74e392)
- 【Rails】 deviseの使い方をマスターしてログイン認証機能を実装しよう！
  - [https://pikawaka.com/rails/devise](https://pikawaka.com/rails/devise)
- Deviseのモヤモヤを解消して快適なRailsライフを送ろう！
  - [https://zenn.dev/kitabatake/articles/start-to-like-the-devise](https://zenn.dev/kitabatake/articles/start-to-like-the-devise)
- Document Module: Devise::Models
  - [https://www.rubydoc.info/github/plataformatec/devise/Devise/Models](https://www.rubydoc.info/github/plataformatec/devise/Devise/Models)

# 最後に

間違い等あれば指摘いただけると幸いです。