Ruby on Rails

Sorcery gemでRailsに認証機能を実装する手順|Deviseとの違い・最新0.18.0対応

Sorceryは、Ruby on Railsに最小構成で認証機能を組み込む軽量なgemです。ログイン・ログアウトやパスワード管理といった基本フローだけを提供し、足りない部分は自分のコードで書き足す設計になっています。本記事はこのRails認証gem「Sorcery」の実装手順を扱います(英単語sorcery=魔術やゲームブック『ソーサリー』の解説ではありません)。最新は0.18.0で、Rails 7.1以上(railties 7.1+)が動作要件です。

まとめ:Sorceryを使う前に押さえる要点

  • Sorceryは「認証の土台だけ提供し、画面や制御は自作する」方針のgem。全部入りのDeviseより薄く、カスタマイズ前提の中小規模向け。
  • 最新は0.18.0(MITライセンス)。依存はbcrypt・oauth・oauth2で、Rails 7.1以上が必要。
  • 導入は「Gemfile追加 → rails g sorcery:installrails db:migrate → Userモデルにauthenticates_with_sorcery!」の4ステップ。
  • ログイン判定はloginlogoutlogged_in?require_loginで行う。未認証時の挙動はnot_authenticatedを上書きして制御する。
  • パスワードリセット・アカウントロック・メール認証・OAuthはsubmoduleを宣言して有効化する。外部連携はsubmodules = [:external]の宣言漏れが最頻の失敗。

Sorceryとは|Railsの軽量認証gem

Sorceryは、ユーザー認証に必要な「セッションの開始・終了」「パスワードのハッシュ化」「アクセス制限」といった機能をメソッドとして提供します。ビューやコントローラを自動生成しない点が特徴で、ログイン画面や遷移は開発者が自分で書きます。そのぶん生成物が少なく、アプリの挙動を把握しやすいのが利点です。パスワードのハッシュ化にはbcryptを使います。

DeviseとSorceryの違い

Railsの認証gemはDeviseが定番ですが、両者は設計思想が逆です。判断材料として主な違いを整理します。

観点 Sorcery Devise
提供範囲 認証メソッドのみ 画面・ルーティングまで一式
ビュー生成 なし(自作) あり(自動生成)
学習コスト 低め(構造が単純) 高め(規約が多い)
カスタマイズ コードを直接記述 設定・オーバーライド中心
向くケース 認証を自分で制御したい中小規模 標準機能を素早く揃えたい

Deviseは規約に沿えば短時間で認証一式が整いますが、標準から外れた要件では設定の把握に時間がかかります。Sorceryは書く量こそ増えますが、どこで何が起きているかがコード上に見えます。

Sorceryを避けるべきケース

認証まわりを「なるべく書かずに済ませたい」プロジェクトにSorceryは向きません。管理画面の即席構築、二要素認証やアカウント管理UIを標準で揃えたい場合は、画面まで生成するDeviseのほうが総工数は小さくなります。Sorceryが生きるのは、ログイン画面のデザインや遷移を自前で作り込みたい、余計な依存を増やしたくない、という要件が明確なときです。

Sorceryのインストールと初期設定

導入は4ステップで完了します。まずGemfileに追記します。バージョンを固定しておくと、後日のbundle updateで意図せず更新されるのを防げます。

gem 'sorcery', '~> 0.18.0'

bundle installの後、インストールジェネレータを実行します。rails generateジェネレータの使い方と同じ流れで、設定ファイルとマイグレーションが生成されます。

rails g sorcery:install
rails db:migrate

これで初期化ファイルconfig/initializers/sorcery.rb、Userモデル、usersテーブル(crypted_passwordsaltカラムを含む)が作られます。生成されたUserモデルにはauthenticates_with_sorcery!が入っており、これが認証の起点です。バリデーションは自動では付かないため、メールとパスワードの検証は自分で追加します。

class User < ApplicationRecord
  authenticates_with_sorcery!
  validates :email, presence: true, uniqueness: true,
            format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, length: { minimum: 8 }, 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] }
end

パスワードは平文カラムを持たず、代入時に自動でハッシュ化されてcrypted_passwordへ保存されます。そのためバリデーションは「新規作成時か、パスワード変更時のみ」に限定するのが定石です。

ログイン・ログアウトの実装

Sorceryは画面を生成しないので、ルーティングとコントローラを自分で用意します。まずconfig/routes.rbにセッション用のルートを追加します。

resources :user_sessions, only: [:new, :create, :destroy]
get  'login',  to: 'user_sessions#new'
delete 'logout', to: 'user_sessions#destroy'

コントローラでは、Sorceryのloginで認証し、logoutでセッションを破棄します。loginは成功時にユーザーオブジェクト、失敗時にnilを返すので、その真偽で分岐します。

class UserSessionsController < ApplicationController
  def new
  end

  def create
    if login(params[:email], params[:password])
      redirect_back_or_to(root_path, notice: 'ログインしました')
    else
      flash.now[:alert] = 'メールアドレスかパスワードが違います'
      render :new, status: :unprocessable_entity
    end
  end

  def destroy
    logout
    redirect_to root_path, notice: 'ログアウトしました'
  end
end

ログインフォームはform_withlogin_path宛てに作り、emailpasswordを送信します。認証に失敗したフォーム再描画では、Rails 7以降の作法にあわせてstatus: :unprocessable_entityを返しておくと、Turboでも正しく再表示されます。

Sorceryの主要ヘルパーメソッド

認証状態の判定やアクセス制限は、Sorceryが提供するヘルパーメソッドで行います。GSCでもnot_authenticatedredirect_back_or_to単体で検索されており、役割を押さえておくと実装が速くなります。

メソッド 役割
login(email, password) 認証してセッション開始。失敗時はnil
logout セッションを破棄
logged_in? ログイン中かを真偽で返す
current_user ログイン中のユーザーを返す
require_login 未ログインを弾くbefore_action用
not_authenticated 未認証時に呼ばれる。上書きで遷移先を制御
redirect_back_or_to(path) 元のページ、なければpathへ戻す

アクセス制限はrequire_loginをbefore_actionに指定し、未認証時の挙動はnot_authenticatedを上書きして決めます。両者はApplicationControllerに置くのが基本です。

class ApplicationController < ActionController::Base
  before_action :require_login

  private

  def not_authenticated
    redirect_to login_path, alert: 'ログインが必要です'
  end
end

公開ページを持つコントローラではskip_before_action :require_login, only: [:index, :show]のように除外します。ログイン後に元のページへ戻すには、認証成功時にredirect_back_or_toを使います。

submoduleで機能を追加する

パスワードリセットやアカウントロックといった追加機能は、Sorceryのsubmoduleとして個別に有効化します。必要なものだけ足す設計なので、使わない機能のコードは入りません。ジェネレータがマイグレーションと設定を生成します。有効化するsubmoduleは初期化ファイルでRails.application.config.sorcery.submodules = [...]にまとめて列挙します。複数使う場合も1つの配列にまとめること(別々に代入すると後の宣言が前を上書きし、片方しか効きません)。

パスワードリセット

rails g sorcery:install reset_password --only-submodulesを実行し、リセットトークンのカラムを追加します。設定ファイルでメール送信クラスを指定すると、リセットリンク付きメールが送られます。

Rails.application.config.sorcery.configure do |config|
  config.user_config do |user|
    user.reset_password_mailer = UserMailer
  end
end

アカウントロック(総当たり対策)

連続したログイン失敗でアカウントを一時ロックするには、brute_force_protection submoduleを追加します。失敗回数を数えるカラムが加わり、既定回数を超えるとロックされます。パスワード総当たり攻撃への基本的な防御になります。

rails g sorcery:install brute_force_protection --only-submodules
rails db:migrate

メール認証でアカウント有効化

登録直後のアカウントを「メール確認済みまで無効」にするには、user_activation submoduleを使います。登録時に確認メールを送り、リンクを踏むと有効化される流れです。捨てアドレスによる不正登録を減らせます。設定でメール送信クラスと、有効化前のログイン可否を指定します。

外部サービス連携(OAuth)でソーシャルログイン

Google・GitHub・LINEなどのアカウントでログインさせるには、:external submoduleを使います。ここで最も多いつまずきが、submodulesへの:external宣言忘れです。この宣言がないとexternal_providersを書いても機能しません。初期化ファイルの冒頭で必ず宣言します。

Rails.application.config.sorcery.submodules = [:external]

Rails.application.config.sorcery.configure do |config|
  config.external_providers = [:google]

  config.google.key          = ENV['GOOGLE_KEY']
  config.google.secret       = ENV['GOOGLE_SECRET']
  config.google.callback_url = 'http://localhost:3000/oauth/callback?provider=google'
  config.google.user_info_mapping = { email: 'email', username: 'name' }
end

APIキーとシークレットはソースに直書きせず、環境変数で管理します。プロバイダごとにuser_info_mappingで取得情報とカラムを対応づけます。0.18.0が対応する外部プロバイダにはGoogle・GitHub・LINE・Slack・Facebookなどがあります。

なお、旧来よく例に挙がるTwitter(現X)連携は、X側のAPI仕様・料金体系が2023年以降に変わっており、無償のログイン用途では使いにくくなりました。新規実装ではGoogleやGitHub、国内向けならLINEを主軸に選ぶのが現実的です。トークンを使ったAPI側の認証を組みたい場合は、Rails APIのJWT認証と実装手順もあわせて検討してください。

セキュリティ対策とよくあるつまずき

認証はアプリの中核なので、Sorceryの機能に加えてRails側の基本対策を必ず併用します。押さえるべきは次の4点です。

  • HTTPS強制config.force_ssl = trueで通信を暗号化し、パスワードやセッションの傍受を防ぐ。
  • パスワードのハッシュ化:Sorceryが依存するbcryptで自動処理される。最小文字数はモデルのバリデーションで担保する。
  • Strong Parametersparams.require(:user).permit(...)で受け取る値を限定し、想定外カラムの更新を防ぐ。
  • CSRF対策:Railsの既定保護を有効に保つ。外部認証のコールバックは検証対象を確認する。

実装でつまずいたときの切り分けは、症状ごとにほぼ決まっています。

症状 主な原因
ログインが常に失敗する authenticates_with_sorcery!漏れ/migration未実行
制限ページに素通りできる require_loginのbefore_action未設定
外部認証が動かない submodules = [:external]宣言漏れ/callback URL不一致
bundle installで衝突 Rails 7.1未満で利用(0.18.0は7.1以上が必要)

認証が動かないときは、まずrails consoleUser.firstを確認し、crypted_passwordが保存されているかを見ると原因を絞り込めます。Railsの開発環境そのものを見直す場合はVSCodeでのRails開発環境の構築も参考になります。

よくある質問

Sorceryとは何のgemですか?

Ruby on Railsに認証機能を組み込むための軽量gemです。ログイン・ログアウト・パスワード管理などの基本メソッドを提供し、画面や制御ロジックは開発者が自作します。英単語のsorcery(魔術)やゲームブックとは無関係です。

SorceryとDeviseはどちらを選ぶべきですか?

認証まわりを自分で制御したい、依存を増やしたくない中小規模ならSorcery、画面を含む標準機能を素早く揃えたいならDeviseが向きます。書く量はSorceryが多く、その代わり挙動が把握しやすくなります。

Sorceryの最新バージョンと対応Railsは?

最新は0.18.0(MITライセンス)で、railties 7.1以上、つまりRails 7.1以上に対応します。依存gemはbcrypt・oauth・oauth2です。導入前にプロジェクトのRailsバージョンを確認してください。

not_authenticatedメソッドは何をしますか?

未認証のユーザーがrequire_loginで保護されたページにアクセスしたときに呼ばれるメソッドです。既定ではルートパス(root_path)へリダイレクトしますが、コントローラで上書きしてログイン画面へ誘導するなど、遷移先を自由に決められます。

Googleログインが動かないときは何を確認しますか?

まず初期化ファイルでRails.application.config.sorcery.submodules = [:external]を宣言しているかを確認します。次にキー・シークレットの環境変数、コールバックURLがプロバイダ側の登録値と一致しているかを見ます。まずこの3点を確認すると、原因を絞り込めます。

関連記事

資料請求

RELATED POSTS 関連記事