概要
本記事では、Ruby on RailsのGem「devise」を用いて、ユーザー新規登録機能を実装していきます。
本番環境でメール認証が適用できない点で詰まったので、本番環境での設定方法まで解説していきます。
本記事でやること
- Gmail認証を使ったユーザー新規登録方法
- Gmail側の設定
- メール内容をカスタマイズ
- 本番環境でもGmail認証が使えるようにする
環境
-
バックエンド
-
Ruby 2.6.5
-
Rails 6.0.3
-
Gem
-
devise
-
devise-i18n
-
dotenv-rails
-
データベース
-
MySQL 5.7
参考にした記事
Deviseでログイン機能を追加・日本語化・Bootstrap4適用まで
deviseを使ったログイン機能を実装する!メール認証機能付き
【Rails】deviseでURL認証付きのメールを送信してみる
完成した後の動作
②入力したメールアドレス宛にメールが送信されるフラッシュが表示
③Gmailを確認して、メールが届いていることを確認、メール内リンクをクリック
補足
メール内のリンクをクリックしないと本人確認をするフラッシュが表示され、ログインができない仕様
アカウント確認メールを再送することも可能(本記事に実装方法記載していません)
前提
- RailsのMVCなど基本を理解されていること
- Railsアプリケーションを新規作成済みであること
- deviseがインストール済みであること
- Gmailアカウントを作成済みであること
実装(準備編)
マイグレーションを以下の様な構成にして、マイグレーションを実行してください
補足
- メール認証のみであれば、Confirmableの箇所のみコメントアウトすれば良いが、パスワードリセットやログイン情報保持をする場合は、以下のようにRecoverable、Rememberableの箇所もコメントアウトしてください。
# 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
deviseの機能が使えるようuser.rbに以下を記載
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :timeoutable, :trackable
新規登録時のエラーメッセージなどを日本語化させましょう
以下の様な構成になります
ja:
activerecord:
attributes:
user:
~~省略~~
devise:
confirmations:
confirmed: メールアドレスが確認できました。
new:
resend_confirmation_instructions: アカウント確認メール再送
send_instructions: アカウントの有効化について数分以内にメールでご連絡します。
send_paranoid_instructions: メールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます。
~~省略~~
mailer:
confirmation_instructions:
action: メールアドレスの確認
greeting: "%{recipient}様"
instruction: 以下のリンクをクリックし、メールアドレスの確認手続を完了させてください。
subject: "アカウントの有効化のご案内"
~~省略~~
shared:
links:
back: 戻る
didn_t_receive_confirmation_instructions: アカウント確認のメールを受け取っていませんか?
didn_t_receive_unlock_instructions: アカウントの凍結解除方法のメールを受け取っていませんか?
forgot_your_password: パスワードを忘れましたか?
sign_in: ログイン
sign_in_with_provider: "%{provider}でログイン"
sign_up: 新規登録
minimum_password_length: "(6~20桁の半角英数字)"
~~省略~~
Gmail側の設定をします。
①Gmailにログインし、2段階認証はオンにしてください
②アプリパスワードの生成
Gmail → 設定 → アカウント → セキュリティ → アプリパスワード → パスワードを入力
→ アプリを「その他」に設定し、「現在作成しているアプリケーション名」 → 生成
→ 以下の画面になると思いますので、必ずメモしてください!(アプリパスワードは再生成できません)
Gmailでメールを受け取れるようにする設定にします(開発環境)
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address:"smtp.gmail.com",
domain: 'gmail.com',
port:587,
user_name: Rails.application.credentials.gmail[:user_name],
password: Rails.application.credentials.gmail[:password],
authentication: :login
}
重要
GitHubにuser_nameとpasswordを記載するのは危険なため、credentialで保存してください!
以下のコマンドを実行
EDITOR=vim rails credentials:edit
INSERTにして、以下を記入したら:wqで抜ける
gmail:
user_name: Gmailアドレス
password: 先ほど生成したアプリパスワード
ここまできたら一度Githubにプッシュしましょう
実装(Mailer編)
デフォルトのMailer設定を変更,送信元メールアドレスを変更
以下の様な構成にします。
補足
config.confirm_withinでURLの有効期限が設定できます
Devise.setup do |config|
config.mailer = 'Users::Mailer'
config.mailer_sender = '認証メール<〇〇〇〇〇〇@gmail.com>'
require 'devise/orm/active_record'
config.reconfirmable = true
config.expire_all_remember_me_on_sign_out = true
config.password_length = 6..20
config.timeout_in = 1.day
config.confirm_within = 1.days
config.reset_password_within = 6.hours
config.scoped_views = true
config.sign_out_via = :delete
end
Mailer自体をオーバーライド
class Users::Mailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts={})
if record.unconfirmed_email != nil
opts[:subject] = "認証を行ってメールアドレス変更手続きを完了してください"
else
opts[:subject] = "認証を行ってユーザ登録を完了してください"
end
super
end
end
実装(MVC作成編)
Viewは以下の様な構成にします。
補足
- if文をフォームの下に設置することにより、バリデーションエラーが以下に表示される様になります。
- cssクラスはご自身で設定されているものに変更してください。
<section class="login">
<div class="login__imgBx">
<%= image_tag 'introduction.jpg' %>
</div>
<div class="login__contentBx">
<div class="login__formBx">
<div class="login__title">新規登録</div>
<%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>
<div class="login__inputBx">
<%= f.label :name, {class: 'login__label'} %>
<%= f.text_field :name, maxlength: '50' %>
<% if @user.errors.include?(:name) %>
<p class="login__form-error"><%= @user.errors.full_messages_for(:name).first %>
<% end %>
</div>
<div class="login__inputBx">
<%= f.label :email, {class: 'login__label'} %>
<%= f.email_field :email, maxlength: '100 '%>
<% if @user.errors.include?(:email) %>
<p class="login__form-error"><%= @user.errors.full_messages_for(:email).first %>
<% end %>
</div>
<div class="login__inputBx">
<%= f.label :password, {class: 'login__label'} %>
<%= f.password_field :password,
autocomplete: "off",
minlength: @minimum_password_length,
maxlength: '20'
%>
<% if @minimum_password_length %>
<small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small>
<% end %>
<% if @user.errors.include?(:password) %>
<p class="login__form-error"><%= @user.errors.full_messages_for(:password).first %>
<% end %>
</div>
<div class="login__inputBx">
<%= f.label :password_confirmation, {class: 'login__label'} %>
<%= f.password_field :password_confirmation,
autocomplete: "off",
minlength: @minimum_password_length,
maxlength: '20'
%>
<% if @user.errors.include?(:password_confirmation) %>
<p class="login__form-error"><%= @user.errors.full_messages_for(:password_confirmation).first %>
<% end %>
</div>
<div class="login__inputBx">
<%= f.submit "新規登録" %>
</div>
<% end %>
<%= render 'devise/shared/links' %>
</div>
</div>
</section>
controllerは以下の様な構成にします。(newとcreateアクションをオーバーライドします。)
# frozen_string_literal: true
class Users::RegistrationsController < Devise::RegistrationsController
def new
super
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
flash[:notice] = "ユーザー認証メールを送信いたしました。認証が完了しましたらログインをお願いいたします。"
redirect_to new_user_session_path
else
flash[:alert] = "ユーザー登録に失敗しました。"
render action: :new and return
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
メール内容のカスタマイズ
app/views/devise/mailer/confirmation_instructions.html.erb を作成
作成後以下の様な構成にします。
補足
登録済みの方が、再度アカウント確認をした場合を想定して、条件分岐で内容を切り替えています。
<div><%= @user.name %>様</div>
<div>〇〇〇〇〇〇〇をご利用いただきありがとうございます。</div>
<% if @user.try(:unconfirmed_email?) %>
<div>以下のリンクからユーザの認証を行ってメールアドレス変更手続きを完了してください。</div>
<div><%= link_to 'ユーザ認証を実行する', confirmation_url(@user,confirmation_token: @token) %></div>
<% else %>
<% if @user.confirmed_at != nil %>
<div>会員登録が完了済みです。</div>
<div>下記リンクからログインをお願いいたします。</div>
<p>http://localhost:3000/</p>
<% else %>
<div>以下のリンクからユーザの認証を行ってユーザ登録を完了してください。</div>
<div><%= link_to 'ユーザ認証を実行する', confirmation_url(@user,confirmation_token: @token) %></div>
<div> このURLの有効期限は24時間です。</div>
<% end %>
<% end %>
ここで開発環境でGmailに認証メールが届くか確認しましょう。
確認できたらGithubにプッシュしましょう
実装(本番環境編)
production.rbに認証メール送信で必要なことがあります。
以下の様な構成になります。
補足
ドメイン名を設定してあげないと、本番環境ではGmailに認証メールが届かないため設定しましょう。
環境変数でGmailアドレスやアプリパスワードを渡す必要があるため、dotenv-rails Gemをインストールし、環境変数を.envファイルに記載しましょう
Rails.application.configure do
~~省略~~
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: 'アプリケーションのドメイン名'}
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address:"smtp.gmail.com",
domain: 'gmail.com',
port:587,
user_name: ENV['SEND_MAIL'],
password: ENV['GMAIL_SPECIFIC_PASSWORD'],
authentication: :login,
openssl_verify_mode: 'none',
enable_starttls_auto: true
}
config.action_mailer.perform_caching = false
config.action_mailer.raise_delivery_errors = true
~~省略~~
end
環境変数の設定は以下の様になります。
SEND_MAIL="Gmailアドレス"
GMAIL_SPECIFIC_PASSWORD="生成したアプリパスワード"
環境変数はGithubにあげるのは危険なため、.gitignoreファイルに.envを追加しましょう
~~省略~~
.env
Githubにプッシュしします
これで、本番環境でメール認証が飛んでいるはずです。
苦労した点
Gmailアドレスとアプリパスワードの設定することに気づかず、詰まりました。
本番環境でメールが飛ばない事態が発生しており、本番環境用のMailer設定については記事が少なく大変でしたが、設定できました。
まとめ
deviseはブラックボックスなところが多く、大変ですが、Qiitaを参考に実際に手を動かして、試行錯誤すると、便利な部分はデフォルトでカスタマイズしたいところはカスタマイズできるのでこちらの記事を参考に皆さんのポートフォリオ作りの糧になればと思います。
ご質問などがありましたらコメントいただけると幸いです。