Reset password mailer implementation in rails 7 api, devise_token_auth, and sendgrid-ruby.
Seamoon Pandey
Posted on October 28, 2024
In this guide, we’ll walk through setting up password reset functionality in a Rails app using devise_token_auth
for authentication and SendGrid for email delivery. With this, users can request password reset links sent directly to their emails.
Dependencies
Add devise_token_auth
and sendgrid-ruby
to your Gemfile
:
# Sendgrid for sending emails
gem 'sendgrid-ruby'
# Devise for authentication
gem 'devise_token_auth'
Then install the dependencies:
bundle install
Or add each gem individually:
bundle add 'sendgrid-ruby'
bundle add 'devise_token_auth'
Setup
To generate Devise token auth files, run:
rails g devise_token_auth:install User auth
Now, open the generated migration file ...devise_token_auth_create_users.rb
, and uncomment these lines:
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email
Run the migration:
bin/rails db:migrate
In your user.rb
model file, add:
extend Devise::Models
include DeviseTokenAuth::Concerns::User
# Add :recoverable for password recovery
devise :database_authenticatable, :registerable, :recoverable
In config/initializers/devise_token_auth.rb
, configure password reset URL:
config.change_headers_on_each_request = false
config.default_password_reset_url = "#{ENV['FRONTEND_URL']}/reset-password"
Routes Configuration
In config/routes.rb
, add:
mount_devise_token_auth_for "User", at: "auth", controllers: {
passwords: "user/passwords"
}
Controller Setup
Create controllers/user/passwords_controller.rb
for handling password reset actions:
# frozen_string_literal: true
class User::PasswordsController < DeviseTokenAuth::PasswordsController
# POST /auth/password
def create
email = resource_params[:email]
if email.blank?
return render json: { success: false, errors: ["Email cannot be blank"] }, status: :unprocessable_entity
end
resource_class.send_reset_password_instructions(email: email)
render json: { success: true, message: "If the email exists, password reset instructions have been sent." }, status: :ok
end
# PUT /auth/password
def update
if password_update_params[:reset_password_token].blank?
authenticate_user!
unless current_user.valid_password?(password_update_params[:old_password])
return render json: { success: false, errors: ["Old password is incorrect"] }, status: :unprocessable_entity
end
if current_user.update(password: password_update_params[:password])
render json: { success: true, message: "Password updated successfully for authenticated user" }
else
render json: { success: false, errors: current_user.errors.full_messages }, status: :unprocessable_entity
end
else
resource = resource_class.reset_password_by_token(password_update_params)
if resource.nil?
return render json: { success: true, message: "If the email exists, password reset instructions have been sent." }
end
if resource.errors.present?
return render json: { success: false, errors: resource.errors.full_messages }, status: :unprocessable_entity
end
resource.update(password: password_update_params[:password])
render json: { success: true, message: "Password updated successfully" }
end
rescue StandardError => e
render json: { success: false, errors: ["An unexpected error occurred: #{e.message}"] }, status: :internal_server_error
end
private
def password_update_params
params.permit(:reset_password_token, :password, :password_confirmation, :old_password)
end
def resource_params
params.permit(:email)
end
end
Now, users can initiate a password reset by sending a POST request to /auth/password
.
Customizing the Email Template
To modify the reset email, update views/devise/mailer/reset_password_instructions.html.erb
:
<p>Hello <%= @resource.email %>!</p>
<p>Someone requested a password reset. You can reset it via the link below.</p>
<% reset_url = DeviseTokenAuth.default_password_reset_url + "?reset_password_token=#{@token}" %>
<p><%= link_to 'Change my password', reset_url %></p>
<p>If you didn’t request this, please ignore this email.</p>
<p>Your password won’t change until you create a new one.</p>
Environment Setup
- Sign in to SendGrid, set up your API key, and verify your sender email.
- Use
dotenv
or Rails credentials to securely store the following in your.env
file:
SENDGRID_API_KEY=SG.******************
SENDER_EMAIL=your_verified_email@domain.com
- Configure
SendGrid
in your environment files (config/environments/development.rb
andconfig/environments/production.rb
):
config.action_mailer.perform_deliveries = true
config.action_mailer.smtp_settings = {
address: "smtp.sendgrid.net",
port: 587,
authentication: :plain,
user_name: "apikey", # Keep as "apikey"
password: ENV["SENDGRID_API_KEY"],
domain: ENV["BASE_URL"],
enable_starttls_auto: true
}
Wrapping Up
And that’s it! Now, users can reset their passwords by requesting a link through your Rails API with Devise Token Auth and SendGrid.
References
Posted on October 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 28, 2024