
- ユーザー登録
- ログイン
- メールアクティベーション
- パスワードリセット
- ソーシャルログイン(Facebook、Twitterなど)
今回は簡単にRailsアプリにログイン機能を実装できるgem「sorcery」の使い方についてハンズオン形式で一つ一つ紹介していきたいと思います。
基本的なロジックについては公式ドキュメントに沿った形になっていますが、ファイル名やコードの書き方などはちょいちょい自分流のスタイルが入ってしまっています。あらかじめご了承ください。
環境
- Ruby: 2.6.3
- Rails: 5.2.4
sorceryとは
冒頭でもお話したように、sorceryはRailsで作ったアプリケーションに対して簡単にログイン機能を実装できるgemです。
sorceryという単語は「魔術」という意味らしく、文字通りまるで魔術のように一瞬で機能を実装できるという意味が込められているのでしょうか。
同じくRailsにログイン機能を実装できるgemとしては「devise」などが定番ですが、個人的にはsorceryの方が好みです。というのも、確かにdeviseも多機能で操作は簡単なのですが、不要な機能まで一気にまるっと実装されてしまうのが少しネックに感じました。メンテナンスもしづらそうですし…。
それに対し、sorceryの場合はまず非常に質素な初期状態からスタートし、必要に応じて適宜機能を追加していけるといった特徴があります。おかげで、特に必要の無いコードが混じって可読性が下がるといった事態を防ぐ事ができるのです。
この辺は各人の好みによる部分が大きいかと思いますが、僕はsorceryの方を好んで使っています。ただし、deviseに比べて日本語の情報がやや少ないかなといった印象も。そこで今回は自分用のメモといった意味合いも込めて記事に残したいと思います。
ユーザー登録&通常ログイン機能
とりあえず、まずはシンプルにユーザー登録と通常ログイン機能を実装してみます。
rails new
$ rails new sorcery_practice
とにもかくにもまずはプロジェクトを作らない事には始まらないので、適当な名前を付けて「rails new」を入力します。
$ cd sorcery_practice
ディレクトリ内へ移動。
$ rails s
サーバーを起動し、お馴染みの画面が表示されるかを確認しましょう。
gemをインストール
gem 'sorcery'
どこでも良いのでGemfile内に「gem 'sorcery'」を追記。
$ bundle install
準備ができたら「bundle install」でgemを導入します。
$ rails g sorcery:install
というコマンドを叩くと、こんな感じでUserモデルを作成するためのファイルがざざーっと作成されます。
Userモデルの作成
# db/migrate/xxxxxxx_sorcery_core.rb
class SorceryCore < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email, null: false
t.string :crypted_password
t.string :salt
t.timestamps null: false
end
add_index :users, :email, unique: true
end
end
念のため、マイグレーションファイルを確認。
$ rails db:migrate
問題無さそうであれば、データベースに反映させるため一旦「rails db:migrate」しましょう。
# app/models/user.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, length: { minimum: 4 }, if: -> { new_record? || changes[:crypted_password] }
validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }
validates :email, uniqueness: true
end
ユーザー登録を行う際の記入漏れなどを防ぐため、バリデーションの設定もしておきます。この辺の値については適宜変更してください。
一応、上記の設定では
- パスワードは4文字以上
- 確認用パスワードの入力が必須
- 重複するメールアドレスは登録できない
といった条件を付けてあります。
rails g scaffoldで土台を作成
$ rails g scaffold_controller user email:string crypted_password:string salt:string$ rails g scaffold
余計なファイルまで量産されてしまうので個人的にあまり好きではないのですが、今回は単に使い方を説明したいだけなので便利なコマンド「rails g scaffold」を使いアプリの土台を作っていきましょう。
こんな感じでユーザーのCRUD(Create(生成)、Read(読み取り)、Update(更新)、Delete(削除))を行うために必要なファイルを一気に生成してくれます。
この中でいじるべきファイルは以下の2つ。
- app/controllers/users_controller.rb
- app/views/users/_form.html.erb
# app/controllers/users_controller.rb(最下部)
#変更前
def user_params
params.require(:user).permit(:email, :crypted_password, :salt)
end
# 変更後
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
ストロングパラメータの値を変更し、「email」「password」「password_confirmation」のみ入力を受け付けるようにします。
# app/views/users/_form.html.erb(真ん中らへんの2つのブロック)
# 変更前
<div class="field">
<%= f.label :crypted_password %>
<%= f.text_field :crypted_password %>
</div>
<div class="field">
<%= f.label :salt %>
<%= f.text_field :salt %>
</div>
#変更後
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
ストロングパラメータの変更に伴いこちらも変更。
一旦、動作確認
# config/routes.rb
Rails.application.routes.draw do
resources :users
end
アプリにアクセスするため「config/routes.rb」の中に「resources :users」の一文を追加。サーバーを起動し「http://localhost:3000/users」へアクセスすると次のような操作ができるようになるはずです
このように、まだ非常に素っ気ないアプリではあるものの、しっかりとユーザー登録・編集・削除といった機能が備わりました。
ログイン機能の実装
さて、ここからいよいよ本題であるログイン機能の実装に入ります。
$ rails g controller UserSessions new create destroy
まずはログインするためのコントローラー「user_sessions_controller.rb」を作成しましょう。
# app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
@user = User.new
end
def create
@user = login(params[:email], params[:password])
if @user
redirect_back_or_to(:users, notice: 'Login successful')
else
flash.now[:alert] = 'Login failed'
render: :new
end
end
def destroy
logout
redirect_to(:users, notice: 'Logged out!')
end
end
大まかな内容はこんな感じです。flashに入るメッセージなどはお好みで変更してください。
# app/views/user_sessions/new.html.erb
<h1>Login</h1>
<%= form_with url: user_sessions_path, method: :post do |f| %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit "Login" %>
</div>
<% end %>
<%= link_to 'Back', users_path %>
肝心となるログイン用のページも作成。
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>SorceryPractice</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<div id="nav">
<% if current_user %>
<%= link_to "Edit Profile", edit_user_path(current_user.id) %>
<%= link_to "Logout", :logout, method: :delete %>
<% else %>
<%= link_to "Register", new_user_path %> |
<%= link_to "Login", :login %>
<% end %>
</div>
<%= yield %>
</body>
</html>
この辺は好みですが、今のままでは全体的に簡素すぎて逆にわかりにくいので「app/views/layouts/application.html.erb」を少しいじってみます。
# config/routes.rb
Rails.application.routes.draw do
get :login, to: 'user_sessions#new'
delete :logout, to: 'user_sessions#destroy'
resources :user_sessions, only: %i[create]
resources :users
end
最後に、ルーティングを編集して大部分の作業は終了です。
これまでに書いたコードを実際に動かしてみると、こんな感じになります。わかりにくいですが、上部のflashメッセージに表示されているようにログインに成功です。
あとは
before_action :require_login
などを各コントローラーに記述し、ログインしているかどうかでアクセス制限をかけたりといった方法も試してみると良いでしょう。
とにもかくにも、ここまでで超基礎的な部分については完了です。ここから先は必要に応じてといった感じになるので、最低限の機能で十分という方はお疲れ様でした!
メールアクティベーション
では次にメールアクティベーション機能を実装してみたいと思います。
会員制のサイトなどでは良く見かける機能ですね。登録したメールアドレス宛に本人確認用のURLが送信され、それを踏む事でアカウントの有効化がされるといった感じのヤツ。
先ほどまでの実装では本人確認のプロセスが無かったため、他人のメールアドレスなどを使って勝手にユーザー登録ができてしまう状態です。これはサービスのあり方としてはあまり望ましくありません。
letter_opener_webを追加
機能を実装していく前に、開発環境でメールを確認できるように準備しておきましょう。
# Gemfile
group :development do
gem 'letter_opener_web'
...
end
開発環境でしか使わないgemなので「group :development」内に記述します。
$ bundle install
こちらのコマンドも忘れずに。
# config/environments/development.rb
...
config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_mailer.delivery_method = :letter_opener
開発環境でもメールが送信されるようにするため、「config/environments/development.rb」内に上の2行を追加。
# config/routes.rb
Rails.application.routes.draw do
...
mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?
end
最後に「config/routes.rb」へルーティングを記述すれば準備完了です。
「localhost:3000/letter_opener」へアクセスしてみるとこんな感じの画面に切り替わり、メールの確認ができるようになります。
(※cloud9を使っている場合は「https://xxxxxxxxx.vfs.cloud9.ap-northeast-1.amazonaws.com/letter_opener」)
UserMailerを作成
では、実際にUserMailerを作成してメールを飛ばしていきたいと思います。
$ rails g sorcery:install user_activation --only-submodules
まずはサブモジュールをインストール。sorceryの機能を拡充していく場合はこのように随時サブモジュールをインストールし、そこからロジックを組み立てていくというのが基本的な流れになります。
# config/initializer/sorcery.rb
...
Rails.application.config.sorcery.submodules = [:remember_me, :user_activation]
Rails.application.config.sorcery.configure do |config|
config.user_config do |user|
user.user_activation_mailer = UserMailer
end
config.user_class = "User"
...
end
設定ファイルを上記のように記述。
# db/migrate/xxxxxxx_sorcery_user_activation.rb
class SorceryUserActivation < ActiveRecord::Migration[5.2]
def change
add_column :users, :activation_state, :string, default: nil
add_column :users, :activation_token, :string, default: nil
add_column :users, :activation_token_expires_at, :datetime, default: nil
add_index :users, :activation_token
end
end
$ rails db:migrate
「$ rails g sorcery:install user_activation --only-submodules」を入力した際、同時にマイグレーションファイルも生成されるので忘れずに「rails db:migrate」しましょう。
$ rails g mailer UserMailer activation_needed_email activation_success_email
上記のコマンドを入力するとこんな感じでメール機能一式が生成されます。
# app/mailers/user_mailer.rb
def activation_needed_email(user)
@user = user
@url = "http://localhost:3000/users/#{user.activation_token}/activate"
mail(to: user.email, subject: 'Welcome to My Awesome Site')
end
def activation_success_email(user)
@user = user
@url = "http://localhost:3000/login"
mail(to: user.email, subject: 'Your account is now activated')
end
実際にメールを送信するコントローラー的な部分。「subject」はいわゆる「件名」に該当します。
# app/views/user_mailer/activation_needed_email.text.erb
Welcome to example.com, <%= @user.email %>
===============================================
You have successfully signed up to example.com,
your username is: <%= @user.email %>.
To login to the site, just follow this link: <%= @url %> .
Thanks for joining and have a great day!
# app/views/user_mailer/activation_success_email.text.erb
Congratulations, <%= @user.email %>!
You have successfully activated your example.com account,
your username is: <%= @user.email %>.
To login to the site, just follow this link: <%= @url %>.
Thanks for joining and have a great day!
メール本文。上がアカウント有効化前の本人確認用メール、下がアカウント有効後に送信される完了メールです。具体的な文言については各自変更してみてください。
# app/controllers/users_controller.rb
...
def activate
if (@user = User.load_from_activation_token(params[:id]))
@user.activate!
redirect_to(login_path, :notice => 'User was successfully activated.')
else
not_authenticated
end
end
# config/routes.rb
...
resources :users do
member do
get :activate
end
end
あとは「app/controllers/users_controller.rb」内にアクティベーション用のメソッドを定義し、ルーティングを追加すればOK。
試しにユーザー登録を行った後、「localhost:3000/letter_opener」へアクセスするとメールが届いているはずです。
パスワードリセット
後ほど追記予定
ソーシャルログイン