Sending Password Reset Email using Sendgrid and Devise in Rails 5.1 API only App

Step 1

User model must have recoverable module.

class User < ApplicationRecord
  extend Devise::Models

  # Include devise modules as needed:
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable

  def self.valid_login?(email, password)
    user = where(email: email).first
    [user&.valid_password?(password), user]

  def reset_authentication_token!
    update_column(:authentication_token, Devise.friendly_token)

Step 2

Routes will use customized passwords controller for API endpoint.

devise_for :users, skip: :sessions, :controllers => { :passwords => "passwords" }

Step 3

Create passwords controller with create action that extends from Devise passwords controller.

class PasswordsController < Devise::PasswordsController
  def create
    self.resource = resource_class.send_reset_password_instructions(resource_params)

    if successfully_sent?(resource)
      render json: {status: 'ok'}, status: :ok, location: after_sending_reset_password_instructions_path_for(resource_name))
      render json: {error: ['Error occurred']}, status: :internal_server_error

Step 4

In config/environment.rb:

ActionMailer::Base.smtp_settings = {
  :user_name => ENV['SEND_GRID_USERNAME'],
  :password => ENV['SEND_GRID_PASSWORD'],
  :domain => '',
  :address => '',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true

Wait, we can do better:

ActionMailer::Base.smtp_settings = {
  :user_name => Credential.send_grid_username,
  :password => Credential.send_grid_password,
  :domain => '',
  :address => '',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true

Now the credential class can use ENV or the latest and greatest Rails way of using secrets. We don't care anymore. We can now curl this endpoint the email in the database to trigger sending password reset email.

Step 5

curl -X POST --data "" http://localhost:3000/users/password

In the Rails console:

 > a = User.first
  User Load (0.5ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
 => #<User id: 1, email: "", first_name: "", last_name: "", created_at: "2018-02-01 22:27:35", updated_at: "2018-02-10 00:35:47", authentication_token: "mHJJTpcGQpeTaMnZfUNR"> 
 > a.reset_password_token
 => "848abd85221d477af41b7ce242161ae76d7d5bc85be2a44f855e7dc17b5aeeea" 

In the rails log:

Sent mail to (12.2ms)
Date: Fri, 09 Feb 2018 15:19:42 -0800
Message-ID: <5a7e2c8ebca19_6>
Subject: Reset password instructions
Mime-Version: 1.0
Content-Type: text/html;
Content-Transfer-Encoding: 7bit


<p>Someone has requested a link to change your password. You can do this through the link below.</p>

<p><a href="http://localhost:3000/users/password/edit?reset_password_token=YPXV3RDJb3Ehjcowdbon">Change my password</a></p>

<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>

The reset_password_token field is encrypted in the database by Devise.

Related Articles

Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.