railsのdeviseを使ってユーザー認証機能を実装する一連の流れの備忘録

#deviseとは
この記事では、railsでのdeviseを使ったログイン認証機能の実装について、備忘録として書き残すことにする。
なお、下記のページを参考にさせていただきました。

- [Rails deviseによるユーザー認証 メールによる認証、emailとusernameのどちらでもログイン可能にするまで](http://qiita.com/shizuma/items/c8c2e71af8c1dcf3d1c2)

- [How-Tos](https://github.com/plataformatec/devise/wiki/How-Tos)
#実行環境
ruby 2.3.0p0
rails 5.1.4 
devise 4.3.0

#とりあえず動くようにする
はじめにgemをインストール。

```ruby:Gemfile
# 以下を追加
gem 'devise'
```
```bash
bundle install
```

一通り機能をインストールする。

```bash
rails g devise:install #全体のインストール
rails g devise user #ユーザーモデルを作成
rake db:migrate #userテーブルを作成
rails g devise:views #ビューの作成
```

なお、devise:installした時に、いくつか指示が出ている。まず下記をconfigファイルに追記。自分の環境では大体30行目くらいに入れました。

```ruby:/config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```

この時点でルーティングまで自動で行われているが、ルートの設定ができていることを確認しておく。


```ruby:/config/routes.rb
devise_for :users #自動で挿入されている
root 'blogs#index' #それぞれのパスに設定
```

下記のflashメッセージも追加。とりあえずbodyの真下としておく。

```ruby:/app/views/layouts/application.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
```

#望み通りの仕様にカスタマイズする
このままだとまだまだ実用に程遠いので、カスタマイズしていく。
ユーザー認証にすると同時に自動的にやっておいた方がいいよねこれっていう機能は割と多いように思う。
本記事の中では、デザインの部分(CSS周り)はとりあえず無視します。

### ユーザーのログイン状態を判定し、ルーティング先を変更
たいていの場合、ページに飛んできた人がすでにログイン状態かどうかを判定して、
- ログイン状態であればリクエストのコンテンツを表示
- ログイン状態でなければログインページ、もしくはサインアップのページに飛ぶ
という操作となると思う。それの実装。と言ってもdeviseにすでにそれ用のヘルパーが定義されている。


```ruby:/app/controllers/application_controller.rb
before_action :authenticate_user! #protect_from_forgery with: :exceptionより後におく。
```

アプリ全体の仕様にしたかったのでapplicationに記載。そうでなければ、それぞれのコントローラに仕込む。

実装の確認のために、ログアウトのリンクを仕込んでおく。

```ruby:/app/views/layouts/application.html.erb
#以下を適当なところに追記
<% if user_signed_in? %>
<p><%= link_to "ログアウト", destroy_user_session_path, method: :delete %></p>
<% end %>
```

これでログイン時はリクエストしたページへ通し、ログオフ時はログイン画面に弾くようになった。そしてそれを確認。

### メール認証
サインアップしたユーザーアカウントは、一旦仮登録としてメールを飛ばし、返信を持って本登録とするあれ。
ステップは次の通り。

Userモデルにconfirmableを追加。これで各種認証時に「確認作業」が定義できるようになる。Eメール認証はそのやり方のうちの一つ、くらいで認識している。

```ruby:/app/models/user.rb
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable #最後のやつを追加
```

Userテーブルに確認にまつわる項目を追加しなければいけないようだ。マイグレーションファイルを作成。

```bash
rails g migration add_confirmable_to_devise
```

マイグレーションファイルを次のようにする。ここは、上記参考ページのままコピペ。reconfirmbleも設定したい人は、コメント外した方がいい行がいくつかある。関係ないのでこのままでいく。

```ruby:/db/migrate/yyyymmddxxxxxx_add_confirmable_to_devise.rb
class AddConfirmableToDevise < ActiveRecord::Migration[5.1]
  # Note: You can't use change, as User.update_all will fail in the down migration
  def up
    add_column :users, :confirmation_token, :string
    add_column :users, :confirmed_at, :datetime
    add_column :users, :confirmation_sent_at, :datetime
    # add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
    add_index :users, :confirmation_token, unique: true
    # User.reset_column_information # Need for some types of updates, but not for update_all.
    # To avoid a short time window between running the migration and updating all existing
    # users as confirmed, do the following
    User.all.update_all confirmed_at: DateTime.now
    # All existing user accounts should be able to log in after this.
  end

  def down
    remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
    # remove_columns :users, :unconfirmed_email # Only if using reconfirmable
  end
end
```

```bash
rake db:migrate
```

これで、Eメール送信のための設定は済んだわけですが、肝心のSMTPサーバの設定が必要。うっかりgithubにアップしたくはないから、letter-opener_webを使うことにする。まずはgemをインストール。

```ruby:Gemfile
group :development do #開発環境のみ
  gem 'letter_opener_web'
end
```

```bash
bundle install
```

以下をルーティング設定に追加。

```ruby:/config/routes.rb
if Rails.env.development? #開発環境の場合
  mount LetterOpenerWeb::Engine, at: "/letter_opener"
end
```

メールの送信にletter_opener_webを使用する。

```ruby:/config/environments/development.rb
#以下を追記（config.action_mailer.default_url_optionsの直下あたりがしっくりくるか）
config.action_mailer.delivery_method = :letter_opener_web
```

これでメール送信の設定は全て終了。メール認証が実装できていることを確認できると思いきや、エラーが発生した。原因はテーブルでコメントのままだった下記の2行のようだ。アンコメントして、migrationし直したらEメール認証できるようになった。

```ruby:/db/migrate/yyyymmddxxxxxx_add_confirmable_to_devise.rb
class AddConfirmableToDevise < ActiveRecord::Migration[5.1]
  # Note: You can't use change, as User.update_all will fail in the down migration
  def up
    #（省略）
    add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
    #（省略）
  end

  def down
    #（省略）
    remove_columns :users, :unconfirmed_email # Only if using reconfirmable
  end
end
```

### 管理者権限の設定
複数のユーザーがサービスを用いる場合、普通は管理者権限を設定し、特殊な操作は管理者のみ実行可としておく。

```bash
rails generate migration add_admin_to_users admin:boolean
```

```ruby:/db/migrate/yyyymmddxxxxxx_add_confirmable_to_devise.rb
add_column :users, :admin, :boolean, default: false
```

デフォルト値は当然のごとくfalse。trueだと基本みんな管理者になってしまう。

あとは次のように管理者しかできないアクションを定義してやれば良い。

```ruby
if current_user.admin? # current_userメソッドがnilとなるページも考慮し、current_user.try(:admin?)でもいいらしい
#do something
end
```

#終わりに
ということで、deviseの導入によるユーザー認証機能とそれに伴う設定をいくつかやりましたが、ルーティングの設定などは細かく定義できますし、facebookアカウントでの認証などの機能もありますね。やろうと思えば山積みです。
もっと簡単にできるとかそもそも間違っているとかがありましたら、ご指摘願えれば幸いです。