【Rails】deviseでユーザー登録ができない...「encrypted_password」の正体を暴いた!

朝活でカリキュラムを少しずつ進めておりますが、
実装課題でのユーザー登録のところで 、フォームに入力してもデータベースに保存されないエラーで躓きました。
自分の実装と解決後の学びをまとめようと思います。

やったこと

①deviseをインストール
②Userモデルを作成
マイグレーションファイルにカラムを追加
④form_withでf.labelとf.text_fieldにusersテーブルのカラム名を入れてフォームを作成
⑤deviseのコントローラーにストロングパラメーターを反映

ここまで実装して動かしてみると、なぜかユーザー登録ができない。

Processing by Devise::RegistrationsController#create as TURBO_STREAM
  Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録"}
Unpermitted parameter: :encrypted_password. Context: { controller: Devise::RegistrationsController, action: create, request: #<ActionDispatch::Request:0x0000000107068350>, params: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録", "controller"=>"devise/registrations", "action"=>"create"} }
  TRANSACTION (0.2ms)  BEGIN
  User Exists? (1.2ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'ddd@ddd' LIMIT 1
  TRANSACTION (0.2ms)  ROLLBACK

emailの後にROLLBACKされている様子。
"encrypted_password"=>"[FILTERED]"となっており、パスワードあたりが怪しいのかな?と疑う。

確認したこと

モデルで変な制約をかけていないか

設計書通り記載。

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name,  presence: true
  validates :profile, presence: true
  validates :occupation,  presence: true
  validates :position, presence: true
end

migrationファイルの制約

設計書通り記載。

2024XXXXXXXXXX_devise_create_users

class DeviseCreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :name,               null: false
      t.string :profile,            null: false
      t.text :occupation,           null: false
      t.text :position,             null: false
# 省略

ストロングパラメーター

deviseはストロングパラメーターが使用できないので、代わりにdevise_parameter_sanitizerを使用。

自分で新しく追加したカラムを指定。
emailとpasswordについてはdeviseのデフォルトであるため記載不要。

application_controller.rb

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile, :occupation, :position])
  end
end

passwordのカラム名

new.html.erb

<%= form_with model: @user, url: user_registration_path, local: true do |f| %>

#省略

<div class="field">
<%= f.label :encrypted_password, "パスワード(6文字以上)" %><br />
<%= f.password_field :encrypted_password, id:"user_password", autocomplete: "new-password" %>
</div>

<div class="field">
<%= f.label :password_confirmation, "パスワード再入力" %><br />
<%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %>
</div>

#省略

<% end %>

初めはストロングパラメーターで「:encrypted_password」も許可したらいいのか?
とかも思ったのですが、emailとpasswordについては元々deviseの機能として渡す機能はあるし、なんか気持ち悪いと思い・・・
deviseにおける新規登録やログイン機能について少し深掘りしてみることにしました。

encrypted_password

そもそも、encrypted_passwordとは、暗号化されたパスワードを意味しているらしい。
encrypted_passwordは、deviseを用いて作られるusersテーブルにあるカラムですが、
このencrypted_passwordはあくまで暗号化されたパスワードなので、暗号化されるまでは使えないカラム。

新規登録やログイン画面でパスワードはpasswordとして入力された値を受け取りdeviseへ送信する必要があるらしい。

passwordとpassword_confimationが同じかどうかをチェックし、
同じ(true)ならばencryped_passwordに暗号化した文字列を返すという機能がdeviseに実装されているそう。

なので、自分のコードを下記に書き換えてみました。

new.html.erb

<%= form_with model: @user, url: user_registration_path, local: true do |f| %>

#省略

<div class="field">
<%= f.label :password, "パスワード(6文字以上)" %><br />
<%= f.password_field :password, id:"user_password", autocomplete: "new-password" %>
</div>

<div class="field">
<%= f.label :password_confirmation, "パスワード再入力" %><br />
<%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %>
</div>

#省略

そうすると、データーベースにしっかりと保存ができるようになりました。

まとめ

上記より、今回私がdeviseを用いてユーザー登録の実装をする際にエラーになってしまった原因は、

deviseを用いて作られるカラム、「encrypted_password」が、暗号化されたパスワードであり、
formで入力したパスワードが暗号化されていなかったことが原因でした。
パスワードは新規登録やログイン画面でpasswordとして入力された値を受け取りdeviseへ送信します。
deviseには passwordとpassword_confimationが同じかどうかをチェックし、同じならばencryped_passwordに暗号化した文字列を返すという機能が実装されているので、
この機能を利用し、encrypted_passwordが暗号化され使用できるようになった感じと理解しました。

簡単に書きすぎてしまいましたが、
devise独特な?ルールやメソッドなどもあるので、しっかりと復習しようと思います。