Deviseを使用するためにセットアップされたRails 4アプリケーションがあり、パスワードリセットで問題が発生しています。メーラーがセットアップされ、パスワードリセットメールが正常に送信されます。そのデータベースで確認した正しいreset_password_tokenが割り当てられていますが、正しい形式のパスワードでフォームを送信すると、リセットトークンが無効であるというエラーが表示されます。
ただし、Rails s
を使用すると、まったく同じコードがローカルで正常に機能します。電子メールが送信され、実際にパスワードをリセットできます。私が使用するコードは標準のDeviseコードであり、オーバーライドしていません。
おそらくそれはApacheの何かでしょうか?私はそれにあまり詳しくありません。誰にもアイデアはありますか?
_app/views/devise/mailer/reset_password_instructions.html.erb
_のコードを確認します
リンクshouldは次のもので生成されます:
edit_password_url(@resource, :reset_password_token => @token)
ビューがまだこのコードを使用している場合、それが問題の原因になります。
edit_password_url(@resource, :reset_password_token => @resource.password_reset_token)
Deviseはトークンのハッシュの保存を開始したため、メールはデータベースに保存されたハッシュ値ではなく、実際のトークン(_@token
_)を使用してリンクを作成する必要があります。
この変更は、 143794d701 のDeviseで発生しました
Doctororangeの修正に加えて、resource.find_first_by_auth_conditions
を上書きする場合は、warden_conditions
にメールまたはユーザー名の代わりにreset_password_token
が含まれる場合を考慮する必要があります。
編集:詳しく説明するには:
Deviseは、「devise:registerable、:trackable、...」と言うと、モデルに機能を追加します。
ユーザーモデル(または管理者など)で、find_first_by_auth_conditionsという名前のDeviseメソッドを上書きできます。この特別なメソッドは、ログインを試行しているレコードを見つけるためにDeviseロジックによって使用されます。 Deviseは、warden_conditionsというパラメーターの情報を渡します。これには、電子メール、ユーザー名、reset_password_token、またはデバイスのログインフォームに追加したもの(アカウントIDなど)が含まれます。
たとえば、次のようなものがあります。
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
end
end
end
ただし、deviseはトークンを使用してレコードを検索するため、上記のコードはパスワードリセット機能を無効にします。ユーザーは電子メールを入力せず、URLのクエリ文字列を介してトークンを入力します。この文字列はこのメソッドに渡され、レコードを検索しようとします。
したがって、この特別な方法を上書きする場合は、パスワードリセットのケースを考慮して、より堅牢にする必要があります。
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
elsif conditions.has_key?(:reset_password_token)
where(reset_password_token: conditions[:reset_password_token]).first
end
end
end
ログからURLを取得している場合、次のように表示されます。
web_1 | <p><a href=3D"http://localhost:3000/admin/password/edit?reset_password_to=
web_1 | ken=3DJ5Z5g6QNVQb3ZXkiKjTx">Change password</a></p>
この場合、3DJ5Z5g6QNVQb3ZXkiKjTx
は実際にエンコードされた=3D
文字であるため、=
をトークンとして使用しても機能しません。
この場合、J5Z5g6QNVQb3ZXkiKjTx
を使用する必要があります(3D
は削除済み)
受け入れられた答えは正しいですが、なぜこれが起こっているのかを説明したいので、他のいくつかのケースでも使用できます。パスワードリセットトークンを生成しているメソッドを見ると:
def set_reset_password_token
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
self.save(validate: false)
raw
end
raw
が返され、enc
がデータベースに保存されていることがわかります。データベースの値を使用している場合-enc
を使用してpassword_reset_token
をフォームの非表示フィールドに入力すると、常にToken invalid
は暗号化されたトークンです。使用する必要があるのは、raw
トークンです。
これは、一部の管理者(またはハッカー)がデータベースにアクセスできる場合、管理者は暗号化されたトークンを使用するだけで簡単にリセットできるためです。
これに関するいくつかの情報とDeviseのその他の変更は、 deviseのchange-logブログの投稿 または deviseの問題の議論 で見つけることができます。
カスタム確認メーラービューを使用している場合は、(@ doctororangeの投稿に加えて)以下に注意する価値があるかもしれません。
ビューのリンクもここで変更されました。これは新しいリンクコードです。
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
これは古いリンクコードです。
<p><%= link_to 'Confirm my account', user_confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %></p>