Creating “Forgot password” feature on Rails API
Just a small note for myself…
Hello, today I want to share about common feature that always can be found on a login page, a “Forgot password” feature. The flow is simple, user that forgot his password clicks the “forgot password” link, then the user should input email address he was registered with. After that a token is sent to the email address. User then sends the token along with the new password, after it is successfuly sent, the password then resetted.
okay, first thing first, let’s generate migration to add reset_password_token and reset_password_sent_at column to your user table
rails g migration AddPasswordResetColumnsToUser
add_column :users, :reset_password_token, :string
add_column :users, :reset_password_sent_at, :datetime
then we should create password controller for our forgot password methods.
rails g controller passwords
add endpoints for forgot password and reset password in our routes.rb file
config/routes.rb:post 'password/forgot', to: 'password#forgot'
post 'password/reset', to: 'password#reset'
let’s add these codes to our passwords_controller
controllers/passwords_controller.rb:...
def forgot
if params[:email].blank? # check if email is present
return render json: {error: 'Email not present’}
end
user = User.find_by(email: params[:email]) # if present find user by email
if user.present?
user.generate_password_token! #generate pass token
# SEND EMAIL HERE
render json: {status: 'ok’}, status: :ok
else
render json: {error: [’Email address not found. Please check and try again.’]}, status: :not_found
end
end
def reset
token = params[:token].to_s
if params[:email].blank?
return render json: {error: 'Token not present’}
end
user = User.find_by(reset_password_token: token)
if user.present? && user.password_token_valid?
if user.reset_password!(params[:password])
render json: {status: 'ok’}, status: :ok
else
render json: {error: user.errors.full_messages}, status: :unprocessable_entity
end
else
render json: {error: [’Link not valid or expired. Try generating a new link.’]}, status: :not_found
end
end
...
I skip the email generation section, you can find how to send the email on my previous article.
okay, does the codes work ? not yet… let’s add some methods to our user model.
models/user.rb...
def generate_password_token!
self.reset_password_token = generate_token
self.reset_password_sent_at = Time.now.utc
save!
end
def password_token_valid?
(self.reset_password_sent_at + 4.hours) > Time.now.utc
end
def reset_password!(password)
self.reset_password_token = nil
self.password = password
save!
end
private
def generate_token
SecureRandom.hex(10)
end
...
That’s it, you can test it via postman:
If you have any suggestion or correction, please feel free to put it in response bellow, have a nice day….