この記事でわかる事
  • ユーザー登録
  • ログイン
  • メールアクティベーション
  • パスワードリセット
  • ソーシャルログイン(Facebook、Twitterなど)

 

今回は簡単にRailsアプリにログイン機能を実装できるgem「sorcery」の使い方についてハンズオン形式で一つ一つ紹介していきたいと思います。

基本的なロジックについては公式ドキュメントに沿った形になっていますが、ファイル名やコードの書き方などはちょいちょい自分流のスタイルが入ってしまっています。あらかじめご了承ください。

環境

  • Ruby: 2.6.3
  • Rails: 5.2.4

sorceryとは

冒頭でもお話したように、sorceryはRailsで作ったアプリケーションに対して簡単にログイン機能を実装できるgemです。

「sorcery」公式ページはこちら

 

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」へアクセスするとメールが届いているはずです。

パスワードリセット

後ほど追記予定

ソーシャルログイン

 

おすすめの記事