Sorceryを使用したログイン・ログアウト機能の実装手順

# Sorceryを使用したログイン・ログアウト機能の実装手順

## はじめに

本記事では、Rails 7でSorceryを使用したログイン・ログアウト機能の実装手順を、初学者向けに詳しく解説します。
現在RUNTEQで学習中であり、実際にDockerを使って初めてのアプリ制作での備忘録として記事を作成することにしました。
わかりにくい点などあるかと思いますが、ご了承くださいませ。

:::note warn
ユーザー登録については扱いません
Sorceryは現状開発が止まっておりメンテナンスも終了してます
責任は負いかねます
:::

## 想定読者
- Rails初学者の方
- ログイン機能の実装に悩んでいる方
- Sorceryの基本を学びたい方

## 環境
```
Ruby: 3.3.6
Rails: 7.2.2.1
MySQL: 8.0
Docker使用
```

## なぜSorceryを使うのか？

### Devise vs Sorcery
認証機能の実装において、多くの場合DeviseとSorceryの2択で悩みましたが学習済みであるSorceryを採用しました。その他Sorceryには下記メリットがあります。

**Sorceryのメリット：**
1. シンプルさ
   - 最小限の機能から始められる
   - コードの理解がしやすい
   - カスタマイズが直感的

2. 学習効果
   - 認証の仕組みが理解しやすい
   - 段階的な機能追加が可能
   - MVC（Model-View-Controller）の理解が深まる

3. 柔軟性
   - 必要な機能だけを実装可能
   - 独自の要件に対応しやすい

## 1. Sorceryのインストール手順

### Step 1: Gemfileの設定
```ruby
# Gemfileに追加
gem 'sorcery', '0.16.3'
```

```bash
# インストールの実行
docker compose exec web bundle install
```

**なぜバージョンを指定するのか？**
- 予期せぬアップデートによるエラーを防ぐ
- チーム開発での環境統一
- 動作の安定性を確保

### Step 2: Sorceryの初期設定
```bash
docker compose exec web rails generate sorcery:install
```

**生成されるファイルと役割：**
1. `config/initializers/sorcery.rb`
   - Sorceryの設定ファイル
   - 認証方法のカスタマイズ
   - セキュリティ設定の管理

2. マイグレーションファイル
   - ユーザー認証に必要なテーブルを作成
   - 重要なカラム：
     - `email`: ログイン識別子
     - `crypted_password`: 暗号化されたパスワード
     - `salt`: パスワードの強度を高めるための文字列


:::note info
この記事では扱いませんがユーザー登録には
このタイミングでマイグレーションファイルの生成と編集
docker compose exec web rails db:migrate コマンドの実行
モデルの編集などを行います
:::


## 2. ルーティング設定

### routes.rbの設定
```ruby
Rails.application.routes.draw do
  root "static_pages#top"
  
  # ユーザー登録
  resources :users, only: %i[new create]
  
  # ログイン機能
  get 'login', to: 'user_sessions#new'
  post 'login', to: 'user_sessions#create'
  delete 'logout', to: 'user_sessions#destroy'
end
```

**各ルートの詳細な説明：**

1.**ログインフォーム表示**
```ruby
get 'login', to: 'user_sessions#new'
```
- HTTP GETメソッドを使用
- ログインページを表示するだけの役割
- フォームの入力情報は送信しない

2.**ログイン処理**
```ruby
post 'login', to: 'user_sessions#create'
```
- HTTP POSTメソッドを使用
- フォームから送信された認証情報を処理
- セキュリティ上、必ずPOSTを使用

3.**ログアウト処理**
```ruby
delete 'logout', to: 'user_sessions#destroy'
```
- HTTP DELETEメソッドを使用
- セッション情報を安全に削除
- RESTfulな設計原則に従う


## 3. コントローラーの実装

### ApplicationControllerの設定
```ruby
class ApplicationController < ActionController::Base
  add_flash_types :success, :info, :warning, :danger
  before_action :require_login

  private

  def not_authenticated
    flash[:warning] = 'ログインしてください'
    redirect_to login_path
  end
end
```

**重要な設定ポイント：**
1. `add_flash_types`
   - フラッシュメッセージのタイプを追加
   - ユーザーへの適切なフィードバック表示に必要

2. `before_action :require_login`
   - すべてのアクションで認証を要求
   - 未ログインユーザーのアクセスを制御

### UserSessionsControllerの実装
```ruby
class UserSessionsController < ApplicationController
  skip_before_action :require_login, only: %i[new create]

  def new; end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_to root_path, success: 'ログインしました'
    else
      flash.now[:danger] = 'ログインに失敗しました'
      render :new, status: :unprocessable_entity
    end
  end

  def destroy
    logout
    redirect_to root_path, status: :see_other, success: 'ログアウトしました'
  end
end
```

**各メソッドの役割：**
1. `new`アクション
   - ログインフォームを表示
   - 単純なビュー表示のみ

2. `create`アクション
   - ログイン処理の実行
   - 成功時：ルートページへリダイレクト
   - 失敗時：ログインフォームを再表示

3. `destroy`アクション
   - ログアウト処理
   - セッション情報の安全な削除

## 4. ビューの実装

### ログインフォーム（app/views/user_sessions/new.html.erb）
```erb
<div class="container mx-auto max-w-xl px-4">
  <h1 style="color: #073472;" class="text-2xl font-bold text-center mb-8">ログイン</h1>

  <%= form_with url: login_path, local: true do |f| %>
    <div class="space-y-6">
      <div class="mx-auto w-3/5">
        <%= f.label :email, "メールアドレス", class: "block mb-2", style: "color: #073472;" %>
        <%= f.email_field :email, class: "input input-bordered w-full", style: "background-color: #FFFFFF;" %>
      </div>

      <div class="mx-auto w-3/5">
        <%= f.label :password, "パスワード", class: "block mb-2", style: "color: #073472;" %>
        <%= f.password_field :password, class: "input input-bordered w-full", style: "background-color: #FFFFFF;" %>
      </div>

      <div class="text-center mt-8">
        <%= f.submit "ログイン", class: "btn custom-text border-none hover:opacity-80", style: "background-color: #4981CF;" %>
      </div>
    </div>
  <% end %>
</div>
```
:::note warn
このままではフラシュメッセージは反映されません。
フラッシュメッセージの反映には
ビューファイルの編集が必要ですがここでは扱いません。
:::


## 5. Turboとの連携

### ログアウトリンクの実装
```erb
<%= link_to "ログアウト", logout_path, data: { turbo_method: :delete }, class: "custom-text hover:opacity-80" %>
```

**重要な設定ポイント：**
- `data: { turbo_method: :delete }`の指定が必須
- Turboを使用したAjaxリクエストの実現
- RESTfulなHTTPメソッドの維持

## 動作確認方法

### Railsコンソールでの確認
```ruby
# ユーザーの存在確認
User.all
User.count

# 特定ユーザーの認証情報確認
user = User.first
user.crypted_password.present?  # => true/false
user.salt.present?  # => true/false
```

## よくある実装ミス

1. **セキュリティ関連**
   - パスワードの平文保存
   - CSRF対策の無効化
   - 適切なHTTPメソッドの未使用

2. **ユーザー体験**
   - フラッシュメッセージの未実装
   - エラーメッセージの不適切な表示
   - リダイレクト先の誤り

3. **実装上の注意**
   - `require_login`のスキップ忘れ
   - Turboメソッドの指定漏れ
   - 不適切なルーティング設定

## まとめ

Sorceryを使用したログイン機能の実装は、以下の点に注意して進めることで、安全で使いやすい認証システムを構築できます：

1. 適切なルーティング設定
2. 正しいHTTPメソッドの使用
3. セキュリティを考慮した実装
4. ユーザーフレンドリーなUIの提供

## 参考資料
- [Sorcery公式ドキュメント](https://github.com/Sorcery/sorcery)
- [Rails公式ガイド](https://guides.rubyonrails.org/)
- [Turbo Handbook](https://turbo.hotwired.dev/)
- https://zenn.dev/prevent/articles/db14cd42b18912

以上で、Sorceryを使用したログイン・ログアウト機能の基本的な実装は完了です。
フラッシュメッセージは反映させるにはビューファイルの編集が必要となります
今後は、パスワードリセット機能など、より高度な機能を追加していくことができます。