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.
Top comments (0)