DEV Community

Seamoon Pandey
Seamoon Pandey

Posted on

Reset password mailer implementation in rails 7 api, devise_token_auth, and sendgrid-ruby.

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' 
Enter fullscreen mode Exit fullscreen mode

Then install the dependencies:

bundle install 
Enter fullscreen mode Exit fullscreen mode

Or add each gem individually:

bundle add 'sendgrid-ruby' bundle add 'devise_token_auth' 
Enter fullscreen mode Exit fullscreen mode

Setup

To generate Devise token auth files, run:

rails g devise_token_auth:install User auth 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Run the migration:

bin/rails db:migrate 
Enter fullscreen mode Exit fullscreen mode

In your user.rb model file, add:

extend Devise::Models include DeviseTokenAuth::Concerns::User # Add :recoverable for password recovery devise :database_authenticatable, :registerable, :recoverable 
Enter fullscreen mode Exit fullscreen mode

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" 
Enter fullscreen mode Exit fullscreen mode

Routes Configuration

In config/routes.rb, add:

mount_devise_token_auth_for "User", at: "auth", controllers: { passwords: "user/passwords" } 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

Environment Setup

  1. Sign in to SendGrid, set up your API key, and verify your sender email.
  2. 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 
Enter fullscreen mode Exit fullscreen mode
  1. Configure SendGrid in your environment files (config/environments/development.rb and config/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 } 
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)