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
  acts_as_token_authenticatable

  # 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]
  end

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

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))
    else
      render json: {error: ['Error occurred']}, status: :internal_server_error
    end
  end
end

Step 4

In config/environment.rb:

ActionMailer::Base.smtp_settings = {
  :user_name => ENV['SEND_GRID_USERNAME'],
  :password => ENV['SEND_GRID_PASSWORD'],
  :domain => 'mydomain.com',
  :address => 'smtp.sendgrid.net',
  :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 => 'mydomain.com',
  :address => 'smtp.sendgrid.net',
  :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 "email=bugs@rubyplus.com" 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: "bugs@rubyplus.com", 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 bugs@rubyplus.com (12.2ms)
Date: Fri, 09 Feb 2018 15:19:42 -0800
From: me@mydomain.com
Reply-To: me@mydomain.com
To: bugs@rubyplus.com
Message-ID: <5a7e2c8ebca19_6>
Subject: Reset password instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<p>Hello bugs@rubyplus.com!</p>

<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


Create your own user feedback survey