Sorceryメモ
認証機能を追加するGem。Sorcery 0.16.0 で確認。
以下のgemを追加する。
gem 'sorcery'
bin/rails g sorcery:install
で以下のファイルが作成される。
app/models/user.rb
config/initializers/sorcery.rb
db/migrate/20210310200552_sorcery_core.rb
spec/factories/users.rb
spec/models/user_spec.rb
マイグレーションファイルはusers
テーブルを作成する。デフォルトのテーブル名がusersではなくUserになってしまうバグがあるので、create_table
とadd_index
の:User
は:users
に直す必要がある。マージはされているので、Sorcery 0.16.1以上では解消されるはず。
class SorceryCore < ActiveRecord::Migration[6.1]
def change
create_table :User do |t|
t.string :email, null: false
t.string :crypted_password
t.string :salt
t.timestamps null: false
end
add_index :User, :email, unique: true
end
end
サブモジュールを追加したい場合はbin/rails g sorcery:install remember_me --only-submodules
のコマンドを実行する。bin/rails g sorcery:install
を実行する前だったらbin/rails g sorcery:install remember_me
のようにすれば一緒に追加される。
モデルをUser
以外にしたい場合はbin/rails g sorcery:install --model Account
のようにする。
Core機能
require_login
before_action :require_login
の形で使い、ログインしていない場合はroot_path
にリダイレクトさせる。リダイレクト先を変えたい場合はnot_authenticated
メソッドをオーバーライドする。
ログイン画面などログインしていないことが前提の画面はskip_before_action
を設定しておく。
class ApplicationController < ActionController::Base
before_action :require_login
private
# https://github.com/Sorcery/sorcery/blob/43efa3329e61d3fff8a5233d9efdc90db6820362/lib/sorcery/controller.rb#L108
# 以下はデフォルトと同じ
def not_authenticated
redirect_to root_path
end
end
class UserSessionsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
login
認証処理を行うメソッド。login(メールアドレス, パスワード)
の形で使う。認証に成功すればそのUserモデルのインスタンス、失敗すればnilが返る。sessionにも自動的に情報が追加される。
class UserSessionsController < ApplicationController
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 action: 'new'
end
end
end
logout
ログアウトを行うメソッド。
class UserSessionsController < ApplicationController
def destroy
logout
redirect_to(:users, notice: 'Logged out!')
end
end
auto_login
引数で指定したユーザーにログインする。引数はModelのインスタンスでメールアドレスではない。
auto_login(User.first)
logged_in?
ログインしているかどうか。ログインしていればtrue
を返す。
current_user
ログイン中のユーザーを返す。
redirect_back_or_to
「ログアウト状態でログインが必要な画面にアクセス=>ログイン画面にリダイレクト=>ログイン=>表示しようとしていた画面を表示する」という流れを実現するメソッド。
redirect_back_or_to(default_url, flash_messages)
という形で実行する。もし、ログアウト状態でログインが必要な画面にアクセスせずにログインした場合は第一引数で指定したURLにリダイレクトする。redirect_to
メソッドに渡しているのでredirect_to
メソッドに指定できる形ならどのような形でも良い。flash_messages
も同様でredirect_to
メソッドのflash
引数に指定しているだけである。
User#external?
FacebookやTwitterなど外部認証を利用していればtrueを返す。実際のところは、users.crypted_password
カラムが空でないかチェックしている。
User#active_for_authentication?
ユーザーの凍結機能などを実現したい場合に定義する。このメソッドがfalseを返すとそのユーザーはログインができない。デフォルトでは未定義のメソッドで定義すると呼ばれるようになる。
class User < ApplicationRecord
authenticates_with_sorcery!
def active_for_authentication?
!account_frozen?
end
end
User#valid_password?
引数のパスワードが正しいパスワードならtrueを返す。User.first.valid_password?('password')
User.authenticates_with_sorcery!
Sorceryの機能をUserモデルに読み込む。
class User < ApplicationRecord
authenticates_with_sorcery!
end
HTTP Basic Auth機能
BASIC認証でログイン機能を提供するサブモジュール。
HTTP Basic Auth機能を利用するにはbin/rails g sorcery:install http_basic_auth --only-submodules
を実行してサブモジュールを追加する必要がある。
config/initializers/sorcery.rb
ファイルにハッシュを指定するconfig.controller_to_realm_map
オプションがある。WWW-Authenticate
ヘッダに指定される情報で、BASIC認証のダイアログにメッセージとして表示される。
# config/initializers/sorcery.rb
config.controller_to_realm_map = {
'スネークケースのコントローラー名' => 'メッセージ',
'user_sessions' => 'Please enter your email and password.'
}
デフォルトでは、{"application" => "Application"}
が指定されていてすべてのコントローラーでApplicationというメッセージが表示される。
以下のようにbefore_action
を指定するとログイン画面代わりにBASIC認証が使えるようになる。アカウントはUserテーブルのものと照合される。
before_action :require_login_from_http_basic
Remember Me機能
ブラウザを閉じてもログインを継続させるサブモジュール。
Remember Me機能を利用するにはbin/rails g sorcery:install remember_me --only-submodules
を実行してサブモジュールを追加する必要がある。また、マイグレーションファイルが作成されるので、bin/rails db:migrate
を実行しておく。
設定
user.remember_me_for
に何日間Remember Meを有効にするか指定する。デフォルトでは60 * 60 * 24 * 7
が指定されている。
# config/initializers/sorcery.rb
user.remember_me_for = 2.weeks
user.remember_me_token_persist_globally
に複数ブラウザでRemember Meを有効にするか指定する。false
だとusers.remember_me_token
がログインするたびに変更されるがtrue
だと変更されなくなる。結果として、同じトークンが複数ブラウザで共有されることになる。デフォルトはfalse
。
# config/initializers/sorcery.rb
user.remember_me_token_persist_globally = true
login
Remember Me機能を有効にするには、login(email, password, true)
のようにlogin
メソッドの第3引数にtrueを指定する。画面上にチェックボックスを用意してユーザーに選択させる方法もある。
auto_login
Remember Me機能を有効にするには、auto_login(User.first, true)
のようにlogin
メソッドの第3引数にtrueを指定する。画面上にチェックボックスを用意してユーザーに選択させる方法もある。
remember_me!
トークンと有効期限をリセットするメソッド。ログイン中の時に使わなければならない。
forget_me!
トークンと有効期限を削除するメソッド。user.remember_me_token_persist_globally
がtrue
だと削除しない。
force_forget_me!
トークンと有効期限を削除するメソッド。forget_me!
と違って常に削除される。
モデルのメソッドについて
いくつかモデルに呼び出せるメソッドが存在するが、通常はコントローラーのメソッド以外は呼び出す必要がないはずである。
Reset Password機能
パスワードを忘れた場合に利用するリセット機能を提供するサブモジュール。
Reset Password機能を利用するにはbin/rails g sorcery:install reset_password --only-submodules
を実行してサブモジュールを追加する必要がある。また、マイグレーションファイルが作成されるので、bin/rails db:migrate
を実行しておく。
bin/rails g mailer UserMailer reset_password_email
を実行し、メーラーを追加する。メーラーのクラス名もメソッドも設定で指定できるのでなんでも良い。
基本的な使い方は以下の通り。
- メールアドレスを入力するリセット画面を用意(
new
アクション) - メールアドレスでユーザーを特定し
User#deliver_reset_password_instructions!
メソッドを呼び出す(create
アクション) - メーラーに
users.reset_password_token
カラムの内容つきのedit
アクションへのURLを埋め込む User.load_from_reset_password_token(トークン)
を呼び出してユーザーを特定し、新しいパスワードを入力する画面を用意(edit
アクション)User.load_from_reset_password_token(トークン)
を呼び出してユーザーを特定し、入力されたパスワードでUser#change_password
を呼び出す(update
アクション)
詳細はReset password · Sorcery/sorcery Wiki · GitHubを参照。
設定
user.reset_password_mailer
にメーラーのクラスを指定する。デフォルトはnil
なので必ず指定する必要がある。user.reset_password_email_method_name
にメーラーのメソッドを指定する。デフォルトは:reset_password_email
。
user.reset_password_mailer = UserMailer
user.reset_password_email_method_name = :reset_password_email
user.reset_password_mailer_disabled
にtrue
を設定するとUser#generate_reset_password_token!
を読んだ時に自動的なメール送信をしなくなる。デフォルトはfalse
。
user.reset_password_mailer_disabled = false
user.reset_password_expiration_period
にトークンが有効な時間を指定する。デフォルトはnil
で無期限。
user.reset_password_expiration_period = 1.day
user.reset_password_time_between_emails
に前回の送信から再送信可能までの時間を指定する。デフォルトは5 * 60
。
user.reset_password_time_between_emails = 5.minutes
user.email_delivery_method
にメーラーのデリバリメソッドを指定する。:deliver_later
か:deliver_now
が指定可能(Rails 4.2より前なら:deliver
)。デフォルトは:deliver_now
。Reset Password機能、User Activation機能、Brute force protection機能で共通の設定。
user.email_delivery_method = :deliver_now
User#deliver_reset_password_instructions!
リセットトークンなどの情報をUserテーブルに保存して、パスワードリセットのメールを送信する。
User#generate_reset_password_token!
リセットトークンなどの情報をUserテーブルに保存する。User#deliver_reset_password_instructions!
メソッドの中から呼び出されている。
User.load_from_reset_password_token
引数にパスワードリセットトークンを指定してユーザーを取得する。User.load_from_reset_password_token(User.first.reset_password_token)
という感じ。
User#change_password
パスワードを設定してリセットトークンなどの情報を無効化する。User.first.change_password('password')
という感じ。User#change_password!
というメソッドもあり、User#change_password
はsave
メソッドで、User#change_password!
はsave!
メソッドで保存しようとする。
User#increment_password_reset_page_access_counter
users.access_count_to_reset_password_page
カラムをインクリメントする。このカラムは少なくとも現行バージョン(2021/3/13 Sorcery 0.16.0)ではどこでも使われていないカラムである。
名前の通り、パスワードリセット画面にアクセスした回数を記録してN回までアクセス可能というような制御を想定している。しかし、想定しているだけでSorceryとしては何も機能を提供していないので、そういった制御をしたいなら自分で面倒を見る必要がある。
また、User#generate_reset_password_token!
を読んだところでカウントがリセットされるわけでもないのでUser#reset_password_reset_page_access_counter
メソッドを読んでリセットする必要がある。
Session Timeout機能
指定した時間でセッションを無効にする機能。
Session Timeout機能を利用するにはbin/rails g sorcery:install session_timeout --only-submodules
を実行してサブモジュールを追加する必要がある。
設定
config.session_timeout
にセッションが有効な時間を秒数で指定する。デフォルトは3600
。
config.session_timeout_from_last_action
にtrue
を指定するとアクセスのたびにセッションタイムアウトの時間がリセットされる(false
ならログイン時点からconfig.session_timeout
秒経過したらセッションタイムアウトする)。デフォルトはfalse
。
config.session_timeout_invalidate_active_sessions_enabled
にtrue
を設定し、Userモデルにinvalidate_sessions_before
タイムスタンプカラムを追加するとinvalidate_active_sessions!
メソッドが使えるようになる。強制的なセッションの無効化をする。
# config/initializers/sorcery.rb
config.session_timeout = 3.hours
config.session_timeout_from_last_action = true
config.session_timeout_invalidate_active_sessions_enabled = true
invalidate_active_sessions!
このメソッドを使うにはconfig.session_timeout_invalidate_active_sessions_enabled
にtrue
を指定し、Userモデルにinvalidate_sessions_before
タイムスタンプカラムを追加しておく必要がある。
このメソッドを使うとinvalidate_sessions_before
カラムに現在時刻が保存され、invalidate_sessions_before
カラムより前に有効になっているセッションを無効化する。つまり、すべてのブラウザから一律で強制ログアウトさせる機能。
User Activation機能
新規登録時に指定されたメールアドレスにメールを送って有効なメールアドレスが登録されているか確認する機能。コールバックを追加すればメールアドレスの変更時にも対応できる。
User Activation機能を利用するにはbin/rails g sorcery:install user_activation --only-submodules
を実行してサブモジュールを追加する必要がある。また、マイグレーションファイルが作成されるので、bin/rails db:migrate
を実行しておく。
bin/rails g mailer UserMailer activation_needed_email activation_success_email
を実行し、メーラーを追加する。メーラーのクラス名もメソッドも設定で指定できるのでなんでも良い。
設定
user.user_activation_mailer
にメーラーのクラスを指定する。デフォルトはnil
なので必ず指定する必要がある。user.activation_needed_email_method_name
にメールアドレス確認用のメーラーのメソッドを指定する。デフォルトは:activation_needed_email
。user.activation_success_email_method_name
にメールアドレスの確認が成功した時用のメーラーのメソッドを指定する。デフォルトは:activation_success_email
。
user.user_activation_mailer = UserMailer
user.activation_needed_email_method_name = :activation_needed_email
user.activation_success_email_method_name = :activation_success_email
user.activation_token_expiration_period
にトークンが有効な時間を指定する。デフォルトはnil
で無期限。
user.activation_token_expiration_period = 1.day
user.activation_mailer_disabled
にtrue
を指定すると自動的なメールの送信を行わなくなる。デフォルトはfalse
。truthyな値、falsyな値ではなく厳密にtrue
、false
で判定しているので注意。
user.activation_mailer_disabled = false
user.email_delivery_method
にメーラーのデリバリメソッドを指定する。:deliver_later
か:deliver_now
が指定可能(Rails 4.2より前なら:deliver
)。デフォルトは:deliver_now
。Reset Password機能、User Activation機能、Brute force protection機能で共通の設定。
user.email_delivery_method = :deliver_now
user.prevent_non_active_users_to_login
にtrue
を指定するとアクティベーションを行っていないユーザーのログインをできないようにする。デフォルトはtrue
。
user.prevent_non_active_users_to_login = true
User.load_from_activation_token
引数にアクティベーショントークンを指定してユーザーを取得する。User.load_from_activation_token(User.first.activation_token)
という感じ。
User#setup_activation
アクティベーショントークンなどの情報をUserモデルのインスタンスにセットアップする。保存はしないので、setup_activation
メソッドを読んだ後にsave
メソッドを呼ぶ必要がある。
Userモデルの新規登録時はコールバックで自動的に呼ばれる。
User#activate!
アクティベーションが成功した状態にする。アクティベーションに成功したメールの送信も行う。
Brute force protection機能
設定
user.consecutive_login_retries_amount_limit
には何回ログインに失敗したらアカウントをロックするか指定する。デフォルトは50
。
user.consecutive_login_retries_amount_limit = 50
user.login_lock_time_period
にはアカウントロック後に何秒間アカウントロックを継続するかを指定する。デフォルトは60 * 60
。
user.login_lock_time_period = 1.hour
user.unlock_token_mailer
にはアカウントロックされたことを通知するメーラーのクラスを指定する。デフォルトはnil
。user.unlock_token_email_method_name
にはメーラーのメソッド名を指定する。デフォルトは:send_unlock_token_email
。user.unlock_token_mailer_disabled
にtrue
を指定するとメールを送信しなくなる。デフォルトはfalse
。
user.unlock_token_mailer = UserMailer
user.unlock_token_email_method_name = :send_unlock_token_email
user.unlock_token_mailer_disabled = false
user.login_lock_time_period
を0
にしてuser.unlock_token_mailer
などの設定をしないとロックの解除手段がなくなるので注意。
User#register_failed_login!
ログイン失敗を記録する。ログイン失敗回数のカウントアップと指定回数を超えた場合にアカウントロックを行うメソッド。ログイン失敗時に自動的に呼ばれるので、通常は意識してこのメソッドを呼び出すことはないはず。
User#login_unlock!
アカウントロック情報をリセットしてロックを解除する。user.login_lock_time_period
が1
以上なら指定時間経過後のログインでこのメソッドが呼ばれる。ロック解除メールを飛ばす場合はコントローラーでこのメソッドを呼ぶ。
User#login_locked?
アカウントがロックされていればtrue
を返す。
User.load_from_unlock_token
引数にロック解除トークンを指定してユーザーを取得する。User.load_from_unlock_token(User.first.unlock_token)
という感じ。
Activity logging機能
ログイン時刻などを記録するサブモジュール。参照:Activity logging · Sorcery/sorcery Wiki · GitHub
External機能
Twitterなどによるログイン機能を提供するサブモジュール。参照:External · Sorcery/sorcery Wiki · GitHub
Magic login機能
簡単に言えばパスワード抜きでメールだけでログインができるようにする機能。
日本の人が作成した機能のようなので、そちらの記事を参照。【Rails × sorcery】Slackみたいなパスワードなしのメールだけログイン機能を実装してみる
なお、公式ドキュメントはなさそう。