はじめに
Rails6での再再検証版です。
以下のRails5のときのものをRails6で実行し、一部変更になっている部分を反映したバージョンです。
[Rails] deviseの使い方(rails5版)
【前回】
rails : 5.0.0.1
ruby : 2.3.1
devise: 4.2.0
↓
【今回】
rails : 6.0.2.2
ruby : 2.7.1
devise: 4.7.1
deviseとは
ユーザー登録して、送られてきたメールのリンクをクリックして本登録して、ログインして、パスワード忘れたら再設定して、何回もログインミスったらアカウントロックして…などといった認証系アプリに必要な機能を簡単に追加できる便利なgemです。
1. gemのインストール
1. プロジェクトの作成
新しいプロジェクトを作ります。
$ rails new devise_rails6
$ cd devise_rails6/
2. Gemfileの編集とインストール
以下ファイルにdevise
とomniauth-twitter
を追加します。
source 'https://rubygems.org'
# (省略)...
# Devise
gem 'devise'
gem 'omniauth-twitter'
gemをインストール。
$ bundle install
2. deviseの設定
devise関連ファイルを追加。
すると以下のような英文が表示されます。1から4まで順番に見ていきます。
$ rails generate devise:install
Running via Spring preloader in process 30440
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Some setup you must do manually if you haven't yet:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===============================================================================
1. デフォルトURLの指定
英文の例に書いてあった config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
を以下のファイルに追加しました。
config.action_mailer.default_url_options = { host: 'localhost:3000' }
でもOKです。
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# (省略)...
# mailer setting
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# config.action_mailer.default_url_options = { host: 'localhost:3000' }
end
2. root_urlの指定
1番で指定した http://localhost:3000/
にアクセスした際に表示されるページを指定します。
現状ページは1つも作っていないため、先に追加します。
Pagesコントローラーと、indexページとshowページを追加してみます。
$ rails g controller Pages index show
routes.rb
に以下を指定します。
Rails.application.routes.draw do
root 'pages#index'
get 'pages/show'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
3. flashメッセージの設定
ログインした時などに上の方に「ログインしました」みたいなメッセージが出るようにします。
以下のファイルの <body>
タグのすぐ下に指定されたタグを挿入します。
<!DOCTYPE html>
<html>
<head>
<title>DeviseRails6</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<!-- ここに追加 -->
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body>
</html>
4. deviseのViewを生成
deviseの導入で追加されるViewは、以下のコマンドを実行しなければデザインを変更できないので、デザインをカスタマイズするためにも実行します。
$ rails g devise:views
すると以下の様なファイルが生成されます。
app/views/devise/shared/_error_messages.html.erb (エラーメッセージ用パーシャル)
app/views/devise/shared/_links.html.erb (リンク用パーシャル)
app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
app/views/devise/passwords/edit.html.erb (パスワード変更画面)
app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
app/views/devise/registrations/new.html.erb (ユーザー登録画面)
app/views/devise/sessions/new.html.erb (ログイン画面)
app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
app/views/devise/mailer/email_changed.html.erb (メール用メールアドレス変更完了文)
app/views/devise/mailer/password_change.html.erb (メール用パスワード変更完了文)
app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)
3. Userモデルの設定
1. Userモデルの作成
以下を実行。
$ rails g devise User
マイグレーションファイルができます。
デフォルトではこんな感じになってます。
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
Userモデルはこんな感じになっています。
デフォルトでは database_authenticatable
、 registerable
、 recoverable
、 rememberable
、 validatable
が使えるようになっています。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
2. deviseモジュール概要
各モジュールについて以下に紹介します。
機能 | 概要 |
---|---|
database_authenticatable | サインイン時にユーザーの正当性を検証するためにパスワードをハッシュ化してDBに登録します。認証方法としてはPOSTリクエストかHTTP Basic認証が使えます。 |
registerable | 登録処理を通してユーザーをサインアップします。また、ユーザーに自身のアカウントを編集したり削除できるようにします。 |
recoverable | パスワードをリセットし、それを通知します。 |
rememberable | 保存されたcookieから、ユーザーを記憶するためのトークンを生成・削除します。 |
trackable | サインイン回数や、サインイン時間、IPアドレスを記録します。 |
validatable | Emailやパスワードのバリデーションを提供します。独自に定義したバリデーションを追加することもできます。 |
confirmable | メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を提供します。また、サインイン中にアカウントが認証済みかどうかを検証します。 |
lockable | 一定回数サインインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。 |
timeoutable | 一定時間活動していないアカウントのセッションを破棄します。 |
omniauthable | intridea/omniauthをサポートします。TwitterやFacebookなどの認証を追加したい場合はこれを使用します。 |
3. Userモデルの編集
今回はデフォルトではないものも触ってみたいと思うので全部入れてみます。
Twitter認証を使うのでTwitterを指定しています。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter]
end
4. マイグレーションファイルの編集
上に合わせて使用モジュールに対応する部分のコメントアウトを外します。今回は全部使うので全部外します。
全部を使用しない場合は 3. で使うことにしたものに関連する項目だけコメントアウトを外してください。
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
add_index :users, :confirmation_token, unique: true
add_index :users, :unlock_token, unique: true
end
end
5. omniauth-twitter用カラムの追加
ついでに omniauth-twitter
で使う provider
と uid
、それとTwitter認証の場合はアカウント名を保存しておきたいので username
もUserテーブルに追加します。
その他、保存したい項目があったら arunagw / omniauth-twitter の認証時のハッシュ情報を参考に保存用カラムを追加してください。
$ rails g migration add_columns_to_users provider uid username
以下のようなマイグレーションファイルができます。
class AddColumnsToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
add_column :users, :username, :string
end
end
ここまで出来たら以下を実行します。
$ rake db:migrate
4. viewの編集
以下のファイルを編集して、ページ上部にメニューが出るようにします。
user_signed_in?
はdeviseのHelperメソッドです。
ログインしているかしてないかで上部のメニューの表示が変わるようになります。
current_user
で現在サインインしているユーザーの情報を取得できます。
<!DOCTYPE html>
<html>
<head>
<title>DeviseRails6</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<header>
<nav>
<% if user_signed_in? %>
<strong><%= link_to current_user.username, pages_show_path %></strong>
<%= link_to 'プロフィール変更', edit_user_registration_path %>
<%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to 'サインアップ', new_user_registration_path %>
<%= link_to 'ログイン', new_user_session_path %>
<% end %>
</nav>
</header>
<!-- ここに追加 -->
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body>
</html>
以下の2ページも修正。
indexの方がトップページ、showの方がログインしているユーザー用のページになる予定です。
<h1>ようこそ</h1>
<p>トップページです。</p>
<h1>こんにちは、<%= current_user.username %>さん</h1>
<p>ユーザー用ページです。</p>
サーバーを立ち上げて、「サインアップ」を押下すると、 app/models/user.rb
で omniauthable
を設定しているのに何の設定もしていないので現状ではエラーになります。
$ brew install yarn
$ rails webpacker:install
$ rails s


omniauthable
を設定していない場合はサインアップページが表示されます。
Twitterで認証する
1. 設定
1. Twitter Developerの登録
Twitter Developerにアクセスし、 Create an app
をクリックし(ツイッターにログインしてないとボタンが出ません)、情報を入力にします。
審査がいるようになってしまったので、私がやったときは何週間かかかった気がします。
私は今回、rails5版のこの記事を書いたときのものを使用しています。

新規申請する場合は、申請時に Enable Sign in with Twitter
にチェックを入れておいてください。

2. 設定ファイルの編集
Keys and tokens
タブを開き、 API key
と API secret key
を以下の該当箇所にコピーして貼り付けます。

callback_url
を http://127.0.0.1:3000/users/auth/twitter/callback
にし、Twitter Developer側のコールバックの設定も変更する必要があるようです。
なので、以下の用に修正しました。

Devise.setup do |config|
# (省略)...
config.omniauth :twitter, 'API Key', 'API Secret', callback_url: 'http://127.0.0.1:3000/users/auth/twitter/callback'
end
2. 動作確認
サーバーを立ち上げてサインアップページの下の方にあるSign in with Twitterというリンクをクリックします。
すると以下の様な画面が開くのでログインして 連携アプリを認証
をクリックします。

こんな画面が出るはずです。

3. コールバック用コントローラーの作成
Twitter認証後適切に画面が遷移するように以下を実行してコントローラーを作ります。
$ rails g controller omniauth_callbacks
作成したコントローラーの中身を以下のように修正します。
継承するのが Devise::OmniauthCallbacksController
になっていることに注意です。
omniauth.auth
という環境変数に認証に関する情報が入っています。
その情報を使ってユーザーが登録されているかを検証し、登録してる場合はログイン、登録されてない場合は登録用ページに遷移します。
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def twitter
@user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))
if @user.persisted?
sign_in_and_redirect @user
else
session["devise.user_attributes"] = @user.attributes
redirect_to new_user_registration_url
end
end
end
ちなみに omniauth.auth
の中にはこんなものが入ってます。(一部の値は適当に変えてあります。)
{
"provider"=>"twitter",
"uid"=>"0123456789",
"info"=>{
"nickname"=>"manycicadas",
"name"=>"芭蕉",
"location"=>"関東",
"image"=>"http://pbs.twimg.com/profile_images/483964583371997185/2ZqzhzKV_normal.png",
"description"=>"JavaEE/Ruby(Rails)/HTML/CSS/JavaScript/Raspberry Pi などなどが好き。",
"urls"=>{
"Website"=>nil,
"Twitter"=>"https://twitter.com/manycicadas"
}
},
"credentials"=>{
"token"=>"xxx",
"secret"=>"xxxxxx"
}
(省略)...
}
Userモデルに self.from_omniauth
と self.new_with_session
を作ります。
self.from_omniauth
では uid
と provider
で検索してあったらそれを、無かったらレコードを作ります。
self.new_with_session
については、もしこのメソッドを追加しておかなければ、Twitter認証後サインアップページで登録を行っても、認証情報として取ってきたuidやproviderなどが登録されません。それらが登録されないのでTwitterで認証しても登録されてないユーザーとして毎回サインアップページに飛ばされます。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter]
def self.from_omniauth(auth)
find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.username = auth["info"]["nickname"]
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"]) do |user|
user.attributes = params
end
else
super
end
end
end
以下ファイルを編集して、コールバック用のコントローラーとしてさっき作ったコントローラーが呼ばれるようにします。これを書かないとdevise側のコントローラーが呼ばれます。
Rails.application.routes.draw do
devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" }
root 'pages#index'
get 'pages/show'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
これでTwitter認証ができるようになりました。
初回、Twitter認証を行うと、サインアップページに飛ばされ、そこでメールアドレスやパスワードを入力して登録するとユーザー情報が登録されます。
今回は comfirmable
機能を入れているので、登録したら確認メッセージを送ったとのメッセージが出て、そのままログインすることはできません。
この機能を入れてなかった場合、登録すると即ログインします。
アカウント登録確認メールを送る
1. comfirmable概要
多くの登録系サイトで採用されている、登録すると仮登録状態になり、届いたメールのリンクをクリックするとログイン可能になるという仕組みを追加できるのがcomfirmableです。
今回はGmailのアカウントを使って実際にメールが届くように設定します。
2. ログを見る
Devise::Mailer#confirmation_instructions: processed outbound mail in 8.8ms
Delivered mail 5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail (22.3ms)
Date: Sat, 04 Apr 2020 14:37:59 +0900
From: please-change-me-at-config-initializers-devise@example.com
Reply-To: please-change-me-at-config-initializers-devise@example.com
To: sample@example.com
Message-ID: <5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Welcome sample@example.com!</p>
<p>You can confirm your account email through the link below:</p>
<p><a href="http://localhost:3000/users/confirmation?confirmation_token=1vus7S8UesPoay5yz2je">Confirm my account</a></p>
3. メールが実際に届くようにする
1. 設定ファイルの編集
今回はgmailを使うのでメールアドレスにはgmailを設定します。
デフォルトの設定があると思うので、それを変更してください。
Devise.setup do |config|
# (省略)...
config.mailer_sender = "メールアドレス"
end
gmailの場合はGメールアドレスとGメールパスワードの部分を自分のアカウントのものに変更します。
Rails.application.configure do
# (省略)...
# mailer setting
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => 'smtp.gmail.com',
:port => 587,
:user_name => 'Gメールアドレス',
:password => 'Gメールパスワード',
:authentication => :plain,
:enable_starttls_auto => true
}
end
2. Gmailの設定
1. とりあえず試したい場合
デフォルトでは安全性の低いアプリケーションからGmailへのアクセスが制限されています。
よってその制限を解除することで動くようにできます。
ただ、安全性の低いアプリからのアクセスを許可するということはセキュリティ的にはいまいちなのであくまで動作確認ように利用することをおすすめします。
安全性の低いアプリがアカウントにアクセスするのを許可する の安全性の低いアプリを許可というリンクをクリックして、以下のように設定します。

2. 2段階認証がオンの場合
2段階認証をオンにしている場合、1の設定はできないはずなのでこちらをやってください。
また、1のセキュリティ的にイマイチな方をやりたくない方もこの際2段階認証をオンにしてこの設定をやってください。
アカウントへのアクセスとセキュリティ設定の管理を開きます。
2段階認証がオフの場合は2段階認証プロセスから2段階認証の設定をしてください。(2 段階認証プロセスを有効にする)
2段階認証設定を行っている場合はアプリパスワードを開きます。

アプリを選択
で その他
を選択します。

適当に名前をつけます。

パスワードが生成されます。

生成されたパスワードを、1の設定の :password => "Gメールパスワード",
のGメールのパスワードの部分に設定します。
3. 届いたメールを確認
これでアプリ側からサインアップすると、以下の様なメールが届くようになります。 Confirm my account
をクリックするログイン画面からログインが可能になります。

メールで送られる文章は以下のファイルを編集することで可能です。
app/views/devise/confirmation_instructions.html.erb
アカウントをロックする
1. lockable概要
アカウントの認証を一定回数間違うと、アカウントをロックするようにする機能です。
2. 設定
1. 設定ファイルの編集
以下ファイルを修正して、アカウントの認証を5回失敗します。
Devise.setup do |config|
# (省略)...
# lock sessings
config.unlock_strategy = :email
config.maximum_attempts = 4
end
するとアカウントがロックされてこのようなメールが届きます。

メールの中身は app/views/devise/mailer/unlock_instructions.rb
ファイルを修正すれば変わります。
2. 設定値について
- lock_strategy(ロック方法)
属性 | 説明 |
---|---|
:failed_attempts | 失敗回数によってロック。 |
:none | ロックしない。 |
- unlock_strategy(ロック解除方法)
属性 | 説明 |
---|---|
:time | 指定時間でロックを解除する。 |
メールでロックを解除する。 | |
:both | :timeと:emailの両方。 |
:none | 解除させない。 |
:none
を指定した場合、ユーザーのレコードのlocked_at
カラムをnil
にアップデートしたらロックが解除できます。
- unlock_in(ロック解除時間)
2時間で解除するならconfig.unlock_in = 2.hours
といった具合に指定。
- unlock_keys
アカウントをロックまたは解除するときに使用するキーを定義するらしいです。
config.unlock_keys = [:username]
という感じで指定できます。
- maximum_attempts(失敗可能回数)
アカウントの認証を失敗して良い回数を指定します。
config.maximum_attempts = 4
と指定した場合、4回目までは失敗しても大丈夫ですが、5回目を失敗した時点でアカウントがロックされます。
セッションをタイムアウトする
1. timeoutable概要
一定時間活動がない場合にセッションをタイムアウトさせるのがtimeoutableです。
2. 設定
以下ファイルを修正するとタイムアウトまでの時間を指定できます。
以下の場合だと3分後にセッションがタイムアウトします。
デフォルトは30分だそうです。
Devise.setup do |config|
(省略)...
# timeout setting
config.timeout_in = 3.minutes
end
その他の設定
1. ログイン後のページを変更する
ログインすると、デフォルトでは root_url
に飛ばされます。
これを app/views/pages/show.html.erb
になるように修正します。
after_sign_in_path_for
メソッドを追加します。ここにログイン後に遷移したいページを指定します。
あと sign_in_required
も追加します。showページはログインしているユーザーだけにアクセスさせ、ログインしてない場合はログインページに遷移させます。
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
pages_show_path
end
private
def sign_in_required
redirect_to new_user_session_url unless user_signed_in?
end
end
Pagesコントローラーに before_action
を追加します。
class PagesController < ApplicationController
before_action :sign_in_required, only: [:show]
def index
end
def show
end
end
これでログイン後showページに遷移するようになります。
2. サインアップする際に登録するパラメーターを増やす
ユーザーを登録するときにデフォルトではEmailとパスワードだけですが、
ユーザー名も登録させたい、などの場合があると思います。
まずはサインアップページにユーザー名を入力するエリアを追加します。
ユーザーのプロフィール変更ページ(views/devise/registrations/edit.html.erb
)にも同様に追加しときます。
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
# (省略)...
<!-- ユーザー名を追加 -->
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
# (省略)...
<%= render "devise/shared/links" %>
次に ApplicationController
に以下を追加します。
テンプレートを変えて、ユーザー名を入力するようにしただけでは実際に登録されないからです。
詳しくは strong_parameters
について調べてください。
簡単に言えばよく分からんパラメーターは渡せないようになってるので渡せるようにします。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
# (省略)...
protected
def configure_permitted_parameters
devise_parameer_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
end
最後に
作ったプロジェクト全体は以下です。
cigalecigales/devise_rails6
