Mailer unable to access reset_token in User model
You're not storing the reset_token
in the database - you're storing the reset_digest
.
When you don't use workers, you're storing the reset_token
in the User instance, then passing that same User instance to your mailer - hence the reset_token
is still available.
When you use workers, your worker only has the User's ID, so it's reloading the User instance from the database. Because the reset_token
isn't being stored in the database, it's coming back nil.
Either you should be saving the reset_token
in the database, or your password email should be using reset_digest
in the URL.
ActionMailer can't find reset_token, reports missing :id key
I figured it out! I had the answer all along; I had to store my reset_token
in the database. I'll copy the answer from this Stack Overflow post below. Credit goes to sevenseacat for the answer.
When you don't use workers, you're storing the
reset_token
in the User
instance, then passing that same User instance to your mailer - hence
thereset_token
is still available.When you use workers, your worker only has the User's ID, so it's
reloading the User instance from the database. Because thereset_token
isn't being stored in the database, it's coming back nil.Either you should be saving the
reset_token
in the database, or your
password email should be usingreset_digest
in the URL
After I changed my reset_token
from a virtual attribute to a database column, the issue was resolved. My password reset emails are now being sent out.
EDIT (January 18, 2016)
I wanted to add a little extra information explaining why the reset_token
solved the issue even though the error message claimed that the id
was missing. In my password reset email, I generate the edit
password reset action URL as follows:
<%= edit_password_resets_path(@user.reset_token) %>
The route for my edit password reset action is as follows:
edit_password_resets GET /password_resets/:id/edit
When creating a URL, the first parameter that you specify fills in the :id
segment of the URL. In my case, @user.reset_token
was being filled in for the id
, causing the generated URL to be /password_resets/{reset token here}/edit
. When the async job was trying to generate the URL, it was expecting a value to be specified for the id
segment of the URL. I put my reset_token
in for the id
, and since reset_token
was a virtual attribute and was equal to nil
when ActiveJob
ran, it threw an error since it was missing a value.
Rails 4 + Devise: Password Reset is always giving a Token is invalid error on the production server, but works fine locally.
Check the code in app/views/devise/mailer/reset_password_instructions.html.erb
The link should be generated with:
edit_password_url(@resource, :reset_password_token => @token)
If your view still uses this code, that will be the cause of the issue:
edit_password_url(@resource, :reset_password_token => @resource.password_reset_token)
Devise started storing hashes of the token, so the email needs to create the link using the real token (@token
) rather than the hashed value stored in the database.
This change occurred in Devise in 143794d701
Why can't I use Related Model created with same request in Laravel?
You change a related model after loading the primary model, this will cause the relation of the primary model to be outdated, one could solve this by reloading the relationship.
In your case you could use:
$user->load('passwordResetLink');
before
Mail::to($user)->send(New \App\Mail\PasswordResetLink($user));
This will reload only the PasswordResetLink
and prevent having to reload the whole object.
Why do I get an error when I try to save mongoose model?
Probably, user is null or undefined, so you should handle the user null condition.
Also findOne and save returns promise, so you need to add await
keyword before them.
Also you have a typo in user schema password field, requires
should be required
.
let user = await User.findOne({
resetPasswordToken: resetPasswordToken,
resetPasswordExpire: { $gt: Date.now() },
});
if (user) {
// update password
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpire = undefined;
await user.save();
sendToken(user, 200, res);
} else {
res.status(400).send("No user found");
}
If you get the user null, you need to fix your query in findOne.
user.save() is not a function when i am trying to save the model
User.findOne will return a Promise. So you need to add await keyword.
const user = await User.findOne({email:req.body.email});
if(!user){
return next(new ErrorHander("User not found",404));
}
const resetToken = user.getResetPasswordToken
await user.save({ validateBeforeSave: false });
Password reset email using Rails devise is not changed
Follow the steps below to override Devise views. I would also recommend checking their documentation about overriding too.
Step 1
rails generate devise:views
Step 2 Add the following line under config/initializers/devise.rb
config.scoped_views = true
Step 3
Find the Devise views under devise/sessions/new
and override them.
Related Topics
Optimization for Finding Perfect-Square Algorithm
Good Cucumber Examples in the Wild
Imagemagick 7 with Rmagick 2.16 on MACos Sierra Can't Find Magickwand.H
Active Admin: Customize Only New Form
Phusion Passenger Error: You Have Activated Rack 1.2.1, But Your Gemfile Requires Rack 1.2.2
Is It Idiomatic Ruby to Add an Assert( ) Method to Ruby's Kernel Class
How to Install (Build) Ruby 1.9.3 on Osx Lion
Error Installing Pg Gem on Osx
Open_Id_Authentication - "Openidauthentication.Store Is Nil. Using In-Memory Store." Problem
Rails, How to Render a View/Partial in a Model
Ruby, Generate a Random Hex Color
How Much Performance Do You Get Out a Heroku Dynos/Workers
Rails: Hasmanythroughassociationnotfounderror
How to Write a Web Scraper in Ruby