Rails 6+Devise+AWS SESで簡単なログイン認証を実装する手順
TL:DR
Webサービスを実装にするにあたり、必須機能になる認証機能をRails 6 + devise + AWS SESを用いて作成する
前提バージョン
- ruby 3.0.1
- rails 6.1.3.1
- devise 4.7.3
- aws-sdk-rails 3.6.0
手順概要
- Railsプロジェクトの新規作成
- 認証ライブラリDeviseのインストール
- 認証用のModelであるUserクラスを作成
- AWS SESの設定
- RailsとAWS SESとのつなぎこみ
- メール文言の編集
- Web UIの編集
- 動作確認
Railsプロジェクトの新規作成
Railsプロジェクトを作成します。まずはこんな感じ
rails new rails-devise-ses-auth --skip-system-test --skip-turbolinks -S -C
github上ににpushできるように、git remoteでoriginを追加
git remote add origin git@github.com:yohira0616/rails-devise-ses-auth.git
Rails newするときのオプションの詳細についてはこちら
いったんサーバーを立ち上げ動作確認。
bundle exec rails s
localhost:3000にアクセスして、railsのデフォルト画面が表示されればOKです
動作確認をしたらfirst commitをした後にgithubにpushします。
git add -A
git commit -m "first commit"
git push origin master
認証ライブラリDeviseのインストール
Deviseは、Railsで認証周りを扱うgemで最もよく使われているライブラリです。
devise標準でできることは大まかに以下のとおりです。
- ユーザーの登録・退会・ユーザー情報の更新
- ログイン・ログアウト
- ログインパスワードの変更・再発行
他、モジュールを追加することで以下のことができるようになります。
- メールアドレスの存在確認
- アカウントロック
- タイムアウトの設定
- ユーザーのログイン履歴を記録
- Twitter,FacebookなどからのOAuth認証
Gemfileにdeviseを追加
bundle add devise
rails gでdevise関連のファイルを自動作成
deviseには専用のジェネレータが用意されていて、これを使用することで簡単にdevise用の設定ファイルを作成することができます。
bundle exec rails g devise:install
手動でセットアップしないといけない箇所を設定する
先ほどのコマンドでコンソールに以下のようなメッセージが出ると思います。
===============================================================================
Depending on your application's configuration some manual setup may be required:
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.
* Required for all applications. *
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
* Not required for API-only Applications *
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>
* Not required for API-only Applications *
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
* Not required *
===============================================================================
これらについて設定してきます。
1.default_url_optionsに以下を追記
メッセージに書いてあるとおりに、development.rbに以下を追記します
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
2.トップ画面の作成
Railsデフォルトではないトップ画面を一枚作成します。
bundle exec rails g controller home index
これでindexメソッドと持ったHomeControllerと、views/home/index.html.erb が自動生成されます。
routes.rbのブロックの末尾でroot toを設定してデフォルトのホーム画面を追加します。
Rails.application.routes.draw do
root to: 'home#index'
end
3.エラーメッセージを表示できるようにapplication.html.erbの修正
application.html.erbのbody部分を修正します。
<body>
<div class="container">
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
</div>
<%= yield %>
</body>
4.については次の節で対応します。
参考:ほかの認証の選択肢
認証系のライブラリとしては他にはsorceryなどがあります。
sorcery
認証を完全に外出ししてSaaSを利用するならば、FirebaseやAuth0などが有力です。
firebase authentication
auth0
認証用のModelであるUserクラスを作成
具体的に、認証に利用するModelクラスを作成します。
ここではUserクラスを認証用のモデルとして作成します。
deviseのジェネレータに認証用のモデルを作成するコマンドがあるのでそれを利用します。
bundle exec rails g devise User
Userモデルとマイグレーションファイルが作成されますが、confirmable(メールアドレスの存在確認ができるかどうか)は有効にしたいので、以下のように修正します。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
end
migration
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.1]
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モデルのマイグレーションを行うため、db:migrateを行ってUsersテーブルを作りましょう
ユーザーの新規登録画面の作成
Deviseでは、実はviewやcontrollerを自分で作成しなくても(deviseのレールに乗るという前提で)認証画面一式を用意してくれます。
とはいえ、このままだとカスタマイズできないので、deviseのscaffoldを使ってボイラープレートを作成し、修正を加えていきます。
bundle exec rails g devise:controllers users
でControllerのボイラープレートを作成することができます。
AWS SESの設定
Amazon Web Service Simple Email Service(AWS SES)はメール送信を扱うAWSのフルマネージドサービスです。
フルマネージドサービスを利用することによって、自前でメールサーバーを立てなくてもメール送信処理が実装できるようになります。
AWS SESを利用するために必要な条件は以下の通りです。
- メール送信元として使用するための検証済のドメインが必要
- サンドボックス環境(本番用の審査を受けていない状態。)では、送信先も検証済のメールアドレスである必要がある
AWS Route 53を利用してドメインを取得
メール送信元として使用するためのドメインはRoute 53を利用して取得すると楽です(有料)
AWS Route53のダッシュボードの「ドメインの登録」欄の右下の「ドメイン」欄からドメインの登録が行えます。
ハマりポイントとして、住所に日本語が含まれているとドメインの取得に失敗します...ので英語で記述する必要があります。
画面上での購入手続きが完了とするとメールアドレスに確認用メールが届くので、記載されたリンクをクリックすると正式にドメインが取得できます。
ドメインの検証
AWS SESの画面に戻ります。
Domains -> Verify a New Domainをクリックし、先程取得したドメインを入力。
「Generate DKIM Settings」にチェックを入れて「Verify This Domain」から手続きを進めます。
Route 53を利用していると、ネームサーバーの設定を自動でやってくれるので楽です。
メールアドレスの検証
AWS SESでは、本番環境の申請を通していないと、予め設定されたメールアドレスにしかメールを送信することはできません。(メール送信スパムの防止のためかと思われる)
検証済メールアドレスの追加は、Email Addresses -> Verify a New Email Addressから新規に追加することができます。
追加するとそのメールアドレスに対して確認メールが送信されるので、確認メールのリンクをクリックするとVerify済のメールアドレスとして認識されます。
なので自分のメールアドレスを検証済メールアドレスとして登録しておきます。
テスト送信
Domains -> Send a Test Emailから、検証済のメールアドレスに対してメールのテスト送信を実行することができます。
RailsとAWS SESとのつなぎこみ
credentialの仕組みを使ってAWSのアクセストークンを秘匿しながら記述
AWSのAPIを利用するためには、access_key_idとsecret_access_keyを指定してあげる必要があります。
これらは秘匿情報であるので、Railsのcredential機能を使って
EDITOR="vi" bin/rails credentials:edit
このコマンドを実行すると、vimでcredentialの編集画面が開くので、自身のAWSキー情報を入力したら保存して終了します。
作成されるcredentials.yml.encはバージョン管理に含めても良いですが、master.keyはバージョン管理に含めないように注意してください。
参考:
require_master_keyオプションをtrueにする
require_master_keyをtrueにすると、master.keyがない環境ではRails起動時にエラーになるようになります。
development.rbのブロックの末尾に以下を追記します
config.require_master_key = true
RailsでAWS SESの設定をする
RailsでSESの設定をするには、gemはaws-sesではなくaws-sdk-railsを使う方が簡単です。
bundle add aws-sdk-rails
initializersでawsの設定を記述
config/initializers/aws.rbを作成し、以下の記述を追加します。
Aws::Rails.add_action_mailer_delivery_method(
:ses,
credentials: Aws::Credentials.new(Rails.application.credentials.aws[:access_key_id],
Rails.application.credentials.aws[:secret_access_key]),
region: 'us-west-2'
) # オレゴン
development.rbのaction_mailer.delivery_methodを書き換えます。yourdomain.com
と書いてあるところは自分の取得したドメインを記述します。
config.action_mailer.delivery_method = :ses
app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: 'noreply@yourdomain.com'
layout 'mailer'
end
config/initializers/devise.rbを書き換えます。
config.mailer_sender = 'MyService <noreply@yourdomain.com>'
これで、ActionMailerでSESを利用する設定の定義ができました。
参考:メール送信SaaSの別の選択肢
メール送信SaaSとしては、AWS SESの他には、SendGridやMailgunなどを使うこともできます。
メール文言の編集
deviseは、特に設定をしなくても組み込みのテンプレートでメールを送信してくれますが、実際問題としてメール文言が編集できないと不便です。
# 247行目くらい
config.scoped_views = true
bundle exec rails g devise:views users
でメールのテンプレートを出力することができます。
例えば、メールアドレス認証の文言を以下のようにすると
<p>Welcome <%= @email %>!</p>
<p>Hello!下のリンクから登録を完了してください</p>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
(gmailのスクリーンショット)
こんな感じでメールが届きます。
Web UIの編集
CSSを当てていきます。本記事ではいったんJavaScriptを利用せずに構成するので、cssを取り出せるようにwebpackerの設定を変更します。
extract_css: true
app/javascript/application.scssを作成し、application.html.erbの
<%= stylesheet_link_tag 'application', media: 'all' %>
を
<%= stylesheet_pack_tag 'application', media: 'all' %>
に変更します。これでrailsから、webpackerでトランスパイルされたcssを読み込めるようになります。
※ webpackerを利用する場合、app/assets/stylesheets/ は削除してOKです
reset.cssの適用
とりあえずreset.cssを適用します。reset.cssは数種類あって迷うのですが今回はmodern-css-resetを使用します。
参考:
yarn add modern-css-reset
したあとに、application.scssから参照するには以下のように書きます。
@import '~modern-css-reset/dist/reset.min';
node_modules下のライブラリの中のパスを通すには先頭に ~
をつけたパスを記述します。
CSSフレームワークの使用
CSS書くのめんどくさいのでCSSフレームワークを導入します。
参考:
これもまたいろいろなCSSフレームワークが乱立していますが、今回はJavaScriptを使わないpure cssを使いたいので、Bulmaを採用します。
yarn add bulma
した後、application.scssに @import '~bulma/css/bulma.min';
を追記します。
UIを調整
手順をいちいち書いていくと長くなってしまうのでここの /viewsを参照してください(一部割と適当)
動作確認
この時点で、認証周りの基本的なバックロジックは完成しているので、rails sでサーバーを立ち上げて
localhost:3000/users/sign_up からアカウント登録をすることができます。
- 新規ユーザーが作成できるか
- メールアドレスの確認メールが飛ぶか
- パスワードを変更したりリセットできるか
- 退会できるか
などをブラウザ上から動作確認することができます。
一連の機能はdeviseが用意してくれている(localhost:3000/rails/info/routes
を見ると、deviseが設定してくれているパスがわかります)ので実際に手を動かしてみてください。
まとめ
今回は、Ruby on Rails + DeviseでWebサービスには欠かせない認証を最小限の構成で構築しました。
さらにAWS SESを使用することによってメールサーバーを構築しなくてもメールが送れることを確認できました。
自分の既存記事のリライトのつもりでやったらawsライブラリ周りの仕様が変わっていて思ったより時間がかかったのは内緒。
Discussion