ローカル devise 認証の作成: 小林研 Rails Tips (75)

このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。

https://github.com/hkob/hkob_blog

はじめに

Rails Tips の 75 回目です。昨日は、OAuth2 の導入に失敗しました。一度、ローカルの認証で正しく動くようになってから、OAuth2 は再挑戦しようと思います。期待していた人すみません。

Rails をはじめよう - Railsガイド

git revert

昨日の作業は事前にコミットしてしまったので、git revert で打ち消しコミットを作成しました。こういうこともたまにやつておかないと忘れるので、これも記録しておきます。

git revert f10c24e48e481e1a2676746b6e86ef748fe491ea
[main 06398ee] Revert "OAuth2 認証の実装: 小林研 Rails Tips (74)"
 17 files changed, 1 insertion(+), 793 deletions(-)
 delete mode 100644 app/controllers/users/omniauth_callbacks_controller.rb
 delete mode 100644 app/models/user.rb
 delete mode 100644 config/credentials/development.yml.enc
 delete mode 100644 config/initializers/devise.rb
 delete mode 100644 config/initializers/omniauth.rb
 delete mode 100644 config/locales/devise.en.yml
 delete mode 100644 db/migrate/20240211070843_devise_create_users.rb
 delete mode 100644 lib/custom_authentication_failure.rb
 delete mode 100644 spec/models/user_spec.rb
 delete mode 100644 spec/requests/users/omniauth_callbacks_spec.rb

とりあえずまず、devise でローカルのログインをしっかり実装してから、OAuth2 に挑戦することにしましょう。せっかくこれまで i18n も使っているので、i18n 版も入れておきましょう。記述したら bundle しておきます。

gem "devise"
gem "devise-i18n"

devise をインストールします。

$ bin/rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"
     
     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views
       
     * Not required *

===============================================================================

devise.en.yml は作られていますが、ja 版がありません。以下のコマンドで作っておきます。ちょっとファイル名と中身が違うようですね。日本語しか使わないので、en はそのままでいいでしょう。

$ bin/rails g devise:i18n:locale ja
      create  config/locales/devise.views.ja.yml

リベンジで User を再度作成します。

$ bin/rails g devise User
      invoke  active_record
      create    db/migrate/20240212112549_devise_create_users.rb
      create    app/models/user.rb
      invoke    rspec
      create      spec/models/user_spec.rb
      insert    app/models/user.rb
       route  devise_for :users

app/models/user.rb に使用する機能を登録します。デフォルトのものに trackable を追加してみました。

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :trackable
end

trackable を追加したので、それに関するものを追加しました。また、name を表示したいので email の前に登録しています。name にも index を振っておきます。

# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,               null: false, default: ""
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      t.timestamps null: false
    end

    add_index :users, :name,                 unique: true
    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

migration しておきます。一度ファイルを戻してしまったので、データベースを一度削除してから migrate しました。

$ bin/rails db:drop
Dropped database 'hkob_blog_development'
Dropped database 'hkob_blog_test'

$ bin/rails db:create
Created database 'hkob_blog_development'
Created database 'hkob_blog_test'

$ bin/rails db:migrate
== 20240122091842 CreateArticles: migrating ===================================
-- create_table(:articles)
   -> 0.0079s
== 20240122091842 CreateArticles: migrated (0.0079s) ==========================

== 20240131125824 CreateComments: migrating ===================================
-- create_table(:comments)
   -> 0.0081s
== 20240131125824 CreateComments: migrated (0.0081s) ==========================

== 20240208115326 AddStatusToArticle: migrating ===============================
-- add_column(:articles, :status, :string, {:null=>false})
   -> 0.0015s
== 20240208115326 AddStatusToArticle: migrated (0.0016s) ======================

== 20240208115356 AddStatusToComment: migrating ===============================
-- add_column(:comments, :status, :string, {:null=>false})
   -> 0.0135s
== 20240208115356 AddStatusToComment: migrated (0.0136s) ======================

== 20240212112549 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0084s
-- add_index(:users, :name, {:unique=>true})
   -> 0.0012s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0036s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0018s
== 20240212112549 DeviseCreateUsers: migrated (0.0151s) =======================

通常であれば、views は作らなくてもいいのですが、今回 name を追加したため、これをフォームに追加する必要があります。user と admin など異なるモデルごとにログイン画面を変更したい場合には、そのモデル名を記述します。今回は user のみとする予定なので、devise 共通の views を作ります。

$ bin/rails g devise:views 
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      create    app/views/devise/shared/_error_messages.html.erb
      create    app/views/devise/shared/_links.html.erb
      invoke  form_for
      create    app/views/devise/confirmations
      create    app/views/devise/confirmations/new.html.erb
      create    app/views/devise/passwords
      create    app/views/devise/passwords/edit.html.erb
      create    app/views/devise/passwords/new.html.erb
      create    app/views/devise/registrations
      create    app/views/devise/registrations/edit.html.erb
      create    app/views/devise/registrations/new.html.erb
      create    app/views/devise/sessions
      create    app/views/devise/sessions/new.html.erb
      create    app/views/devise/unlocks
      create    app/views/devise/unlocks/new.html.erb
      invoke  erb
      create    app/views/devise/mailer
      create    app/views/devise/mailer/confirmation_instructions.html.erb
      create    app/views/devise/mailer/email_changed.html.erb
      create    app/views/devise/mailer/password_change.html.erb
      create    app/views/devise/mailer/reset_password_instructions.html.erb
      create    app/views/devise/mailer/unlock_instructions.html.erb

それぞれの view が devise ネームスペースの下に作成されました。ただし、erb になっています。以前使った erb2haml で haml に変更しておきましょう。

> bin/rails haml:erb2haml
--------------------------------------------------------------------------------
Generating HAML for app/views/devise/confirmations/new.html.erb...
Generating HAML for app/views/devise/mailer/confirmation_instructions.html.erb...
Generating HAML for app/views/devise/mailer/email_changed.html.erb...
Generating HAML for app/views/devise/mailer/password_change.html.erb...
Generating HAML for app/views/devise/mailer/reset_password_instructions.html.erb...
Generating HAML for app/views/devise/mailer/unlock_instructions.html.erb...
Generating HAML for app/views/devise/passwords/edit.html.erb...
Generating HAML for app/views/devise/passwords/new.html.erb...
Generating HAML for app/views/devise/registrations/edit.html.erb...
Generating HAML for app/views/devise/registrations/new.html.erb...
Generating HAML for app/views/devise/sessions/new.html.erb...
Generating HAML for app/views/devise/shared/_error_messages.html.erb...
Generating HAML for app/views/devise/shared/_links.html.erb...
Generating HAML for app/views/devise/unlocks/new.html.erb...
--------------------------------------------------------------------------------
HAML generated for the following files:
        app/views/devise/confirmations/new.html.erb
        app/views/devise/mailer/confirmation_instructions.html.erb
        app/views/devise/mailer/email_changed.html.erb
        app/views/devise/mailer/password_change.html.erb
        app/views/devise/mailer/reset_password_instructions.html.erb
        app/views/devise/mailer/unlock_instructions.html.erb
        app/views/devise/passwords/edit.html.erb
        app/views/devise/passwords/new.html.erb
        app/views/devise/registrations/edit.html.erb
        app/views/devise/registrations/new.html.erb
        app/views/devise/sessions/new.html.erb
        app/views/devise/shared/_error_messages.html.erb
        app/views/devise/shared/_links.html.erb
        app/views/devise/unlocks/new.html.erb
--------------------------------------------------------------------------------
Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)
y
Deleting original .erb files.
--------------------------------------------------------------------------------
Task complete!

おわりに

ちょっと長くなってしまったので、この続きは明日に回します。ログイン自体はあっという間に終わると思うので、その後、記事やコメントにユーザへの関連を追加してみます。