Railsチュートリアル「11.3.1 authenticated?メソッドの抽象化」の演習2の`authenticated?`メソッドが`true`にならず、`false`になる時の対処
事象
Railsチュートリアル「11.3.1 authenticated?メソッドの抽象化」の演習2のauthenticated?
メソッドがtrue
にならず、false
になる。
デバッグ
ソースコードにbinding.pry
を入れて、デバッグする。
(※ binding.pry
を利用するには、gem 'pry-rails'
が必要)
[3] pry(main)> user.authenticated?(:activation ,"$2a$12$aGjTyNj/2nIQmddu9Jpz2u7wtQU9mipnhftMoO/jcBV92A1B0ocNW")
From: /home/masa/environment/RailsTutorial/sample_app2/app/models/user.rb:39 User#authenticated?:
36: def authenticated?(attribute, token)
37: digest = send("#{attribute}_digest")
38: return false if digest.nil?
=> 39: binding.pry
40: BCrypt::Password.new(digest).is_password?(token)
41: end
[1] pry(#<User>)>
まずは、digest
とtoken
の値を確認。
[2] pry(#<User>)> digest
=> "$2a$12$aGjTyNj/2nIQmddu9Jpz2u7wtQU9mipnhftMoO/jcBV92A1B0ocNW"
[3] pry(#<User>)> token
=> "$2a$12$aGjTyNj/2nIQmddu9Jpz2u7wtQU9mipnhftMoO/jcBV92A1B0ocNW"
[4] pry(#<User>)> digest == token
=> true
digest
とtoken
は同等である。
次に、BCrypt::Password.new(digest).is_password?(token)
を確認
[5] pry(#<User>)> BCrypt::Password.new(digest).is_password?(token)
=> false
is_password?
は、==
のシュガーシンタックス(書き換え)なので、
BCrypt::Password.new(digest)
と(token)
の値が同等ではないということを示している。
そこで、BCrypt::Password.new(digest)
とdigest
を比較してみる。
[6] pry(#<User>)> BCrypt::Password.new(digest)
=> "$2a$12$aGjTyNj/2nIQmddu9Jpz2u7wtQU9mipnhftMoO/jcBV92A1B0ocNW"
[7] pry(#<User>)> digest
=> "$2a$12$aGjTyNj/2nIQmddu9Jpz2u7wtQU9mipnhftMoO/jcBV92A1B0ocNW"
[8] pry(#<User>)> BCrypt::Password.new(digest) == digest
=> false
見た目は同じように見えるが、同等ではない。
次に、BCrypt
がどんな処理をしているかを知るために、BCrypt
の公式リファレンスを確認してみる。
公式リファレンスの確認
ここで、BCryptの公式リファレンスを確認してみると、
クラスメソッドにcreate
があるので、そちらで試してみる。
[9] pry(#<User>)> BCrypt::Password.create(digest).is_password?(token)
=> true
true
が返ってきた。
クラスメソッドのcreate
と、インスタスメソッドinitialize
(newメソッドで呼び出されるメソッド)の違いを公式リファレンスから確認する。
Class Method Summary
.create(secret, options = {}) ⇒ Object
Hashes a secret, returning a BCrypt::Password instance.
Instance Method Summary
#initialize(raw_hash) ⇒ Password constructor
Initializes a BCrypt::Password instance with the data from a stored hash.
翻訳すると、
クラスメソッドの概要
.create(secret, options = {}) ⇒ オブジェクト
秘密をハッシュし、BCrypt::Password インスタンスを返します。
インスタンスメソッドの概要
#初期化(raw_hash) ⇒ パスワードのコンストラクタ
保存されたハッシュからのデータでBCrypt::Passwordインスタンスを初期化します。
create
メソッドは、引数をハッシュ化して、BCrypt::Password インスタンスを返す。
initialize
メソッドは、(ハッシュ化された)引数でBCrypt::Passwordインスタンスを初期化する。
なるほど、、、わからん。。
これ以上の詳細は理解が難しそうなので、詳しい人に聞けるときに聞くことにします。
以上、ありがとうございました。
参考
解決
こちらのサイトに回答が載っていたので、それを参考に実行してみたところ、
authenticated?
メソッドはtrue
となった。
以下、その手順。
db/seeds.rb
を参考にuser
オブジェクトを作成。
[2] pry(main)> user = User.new(name: 'kappy-',
[2] pry(main)* email: 'kappy-@nikoniko.com',
[2] pry(main)* password: 'kappy-',
[2] pry(main)* password_confirmation: 'kappy-',
[2] pry(main)* activated: true,
[2] pry(main)* activated_at: Time.zone.now)
=> #<User:0x000055f08655eca0
id: nil,
name: "kappy-",
email: "kappy-@nikoniko.com",
created_at: nil,
updated_at: nil,
password_digest: "[FILTERED]",
remember_digest: nil,
admin: false,
activation_digest: nil,
activated: true,
activated_at: Sun, 17 Jan 2021 00:51:53 UTC +00:00>
remember_token
に新しいトークンを作成。
[3] pry(main)> user.remember_token = User.new_token
=> "5T5kP80PvNUcHKNKVwu4Fw"
:remember_digest
を設定。
[4] pry(main)> user.update_attribute(:remember_digest, User.digest(user.remember_token))
(0.3ms) BEGIN
User Create (3.3ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest", "remember_digest", "activation_digest", "activated", "activated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id" [["name", "kappy-"], ["email", "kappy-@nikoniko.com"], ["created_at", "2021-01-17 01:02:09.022858"], ["updated_at", "2021-01-17 01:02:09.022858"], ["password_digest", "$2a$12$U8dlalylRNa67F2w7gMyW.XHBvR1XEe1htUU/WXwq1kxs90yQT.mG"], ["remember_digest", "$2a$12$C7f0BKY6CXjZ/tkKKRLBienkHBP20L8X5ComTnW.3ibI2.PNwRJ/y"], ["activation_digest", "$2a$12$FIKkeRneEDG7uBISj6.ebOLnzLOeh7IzKhWks3URS9cJQzEYfTRNG"], ["activated", true], ["activated_at", "2021-01-17 00:51:53.410158"]]
(1.3ms) COMMIT
=> true
設定された情報を確認。
[5] pry(main)> user
=> #<User:0x000055f08655eca0
id: 104,
name: "kappy-",
email: "kappy-@nikoniko.com",
created_at: Sun, 17 Jan 2021 01:02:09 UTC +00:00,
updated_at: Sun, 17 Jan 2021 01:02:09 UTC +00:00,
password_digest: "[FILTERED]",
remember_digest: "$2a$12$C7f0BKY6CXjZ/tkKKRLBienkHBP20L8X5ComTnW.3ibI2.PNwRJ/y",
admin: false,
activation_digest: "$2a$12$FIKkeRneEDG7uBISj6.ebOLnzLOeh7IzKhWks3URS9cJQzEYfTRNG",
activated: true,
activated_at: Sun, 17 Jan 2021 00:51:53 UTC +00:00>
[6] pry(main)> user.remember_token
=> "5T5kP80PvNUcHKNKVwu4Fw"
[7] pry(main)> user.remember_digest
=> "$2a$12$C7f0BKY6CXjZ/tkKKRLBienkHBP20L8X5ComTnW.3ibI2.PNwRJ/y"
authenticated?メソッドを使って、トークン/ダイジェストの組み合わせで認証が成功することを確認。
[8] pry(main)> user.authenticated?(:remember, "5T5kP80PvNUcHKNKVwu4Fw")
=> true
true
になりました。
自分がこれを確認できなかったのは、Railsチュートリアルの内容をちゃんと理解していないことが原因でした。
精進精進。