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)