1.はじめに
RailsチュートリアルやDeviseを使ったログイン機能の記事はよく見るけど、Sorceryの記事は比較的少ないと思ったので、この記事を書きました。
皆様の学習の一助になれれば幸いです🙏
目次
1. はじめに
2. 導入方法
3. ログイン機能の実装
4. 動作確認
5. 終わりに
6. 参考記事
開発環境
Ruby 3.2.3
Rails 7.1.3
Sorcery(gem)
jQuery 3.7.1
2. 導入方法
sorcery
sorceryの公式ドキュメントに沿って導入していきます。
gem 'sorcery'
今回はログイン情報を記憶する機能も実装するため、remember_me を付与しています。
$ rails generate sorcery:install remember_me
作成されたマイグレーションファイルは以下になります。
class SorceryCore < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :email, null: false, index: { unique: true }
t.string :crypted_password
t.string :salt
t.timestamps null: false
end
end
end
class SorceryRememberMe < ActiveRecord::Migration[7.0]
def change
add_column :users, :remember_me_token, :string, default: nil
add_column :users, :remember_me_token_expires_at, :datetime, default: nil
add_index :users, :remember_me_token
end
end
$ rails generate migration AddColumnUsers name:string
class AddColumnUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :name, :string
end
end
必要に応じて手動でカラムを追加してください。
上記の場合はnameカラムを追加しています。
Rails.application.config.sorcery.submodules = [:remember_me]
# Here you can configure each submodule's features.
Rails.application.config.sorcery.configure do |config|
config.user_config do |user|
user.stretches = 1 if Rails.env.test?
user.remember_me_for = 180 #変更
end
config.user_class = "User"
end
remember_me_tokenの有効期限を設定します。
期限は例として180秒で動作確認用に設定しています。
jQuery
flashメッセージを実装するためにjQueryを導入します。
以下はjQueryのCDN最新バージョン(2024年4月時点)を読み込む方法です。
Rails7ではHotwire(Turbo)がデフォルトで実装されているため、そちらを使用しています。
<html>
<head>
<title>minapp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <%#追加 %>
</head>
<body>
<div>
<% flash.each do |message_type, message| %>
<div class="flash-message <%= message_type %>"><%= message %></div>
<% end %>
</div>
<div id="nav">
<% if logged_in? %>
<%= link_to "ログアウト", logout_path, data: { turbo_method: :delete } %>
<% else %>
<%= link_to "新規登録", new_user_path %> |
<%= link_to "ログイン", login_path %>
<% end %>
</div>
<%= yield %>
</body>
</html>
FlashメッセージのCSS
.flash-message {
border-radius: 5px;
padding: 16px 24px;
margin: 16px 0px;
}
.flash-message.notice {
background-color: #bcdfff;
color: #0067C0;
border: solid 1px #0067C0;
}
.flash-message.alert {
background-color: #ffd4d1;
color: #930403;
border: solid 1px #930403;
}
import $ from 'jquery';
import "jquery-ujs";
import "@hotwired/turbo-rails";
import "controllers";
$(function() {
$('.flash-message').fadeOut(4000);
console.log($('.flash-message'));
});
//以下を追加
//= require jquery
//= require jquery_ujs
//= require_tree .
3. ログイン機能の実装
モデル
class User < ApplicationRecord
authenticates_with_sorcery!
validates :email, uniqueness: true, presence: true
validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
end
authenticates_with_sorcery!
でsorceryの認証を有効にしています。
ルーティング
Rails.application.routes.draw do
get 'login' => 'sessions#new', as: :login
post 'login' => "sessions#create"
delete 'logout' => 'sessions#destroy', as: :logout
root to: "samples#index"
resources :users, except: [:index]
end
コントローラー
class SessionsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
def create
@user = login(params[:email], params[:password], params[:remember_me])
if @user
redirect_back_or_to(root_path, notice: 'ログインに成功しました')
else
flash.now[:alert] = 'ログインに失敗しました'
render action: 'new'
end
end
def new
@user = User.new
end
def destroy
remember_me!
forget_me!
logout
redirect_to(login_path, notice: 'ログアウトしました')
end
end
- createアクションにて
params[:remember_me]
を追加することで、ログイン時にremember_me_tokenを発行し、ログイン情報を記憶できます。 - require_loginメソッドはloginしているかどうかを検証します。ログインを必要としないページには、このskip_before_actionを適用しています。
- destroyアクションにて
remember_me!
、forget_me!
を追加することでログアウト前にremember_meトークンを削除しています。
ビュー
<h1>ログイン</h1>
<%= form_with url: login_path, method: :post, data: { turbo: false } do |f| %>
<div class="field">
<%= f.label "メールアドレス" %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label "パスワード" %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= check_box_tag :remember_me, params[:remember_me], true %>
<%= label_tag :remember_me %>
</div>
<div class="actions">
<%= f.submit "ログイン", data: { turbo: false } %>
</div>
<% end %>
<%= link_to 'トップページへ戻る', root_path, data: { turbo: false } %>
ログインページのビューです。
remember_meのチェックボックスを追加しています(デフォルトでTrueにしています)
4. 動作確認
- http://localhost:3000/login でブラウザにアクセスします。
- デベロッパーツールを開く。
- メールアドレス、パスワード、remember_meのチェックボックスをONにしてログインする。
- ログイン成功し、Flashメッセージが表示される。この時、デベロッパーツールの「アプリケーションタブ」のCookie内を確認すると、session情報とremember_meトークンが発行されます。
- ログアウトする
5. 終わりに
- deviseは機能が豊富な反面、カスタマイズするためにはdevise内部の機能を追う必要があり、苦労しました。
- 対してsorceryは軽量で必要最低限の実装で実現できました。比較的シンプルにコードが書けて、認証に関する知識が必要になるものの、カスタマイズはdeviseよりも柔軟に行いやすいという印象でした。
- どちらもメリットデメリットがあり、プロジェクトに応じてライブラリを選定することが大事だと思いました。
6. 参考記事
Sorcery(公式ドキュメント)
[Ruby on Rails]sorceryによる認証 – (2)ユーザ名による認証とRemember-Me
devise(公式ドキュメント)