Adding Password Confirmation to Rails 8 Authentication



This content originally appeared on DEV Community and was authored by Rails Designer

This article was originally published on Rails Designer’s Build a SaaS blog.

When users access sensitive areas of your application, like admin pages, billing settings, or personal data, it is good practice to ask for their password again. Even if they’re already logged in, requiring a password confirmation adds an extra security layer.

This article builds on top of basic Rails 8 authentication. See all the previous commits in this repo.

Here’s a quick example of how to use it:

class AdminController < ApplicationController
  include PasswordConfirmation

  confirm_password only: %w[index]

  def index
  end
end

That’s it! Now users will need to confirm their password before accessing the admin index page. The confirmation remains valid for 10 minutes by default, but you can adjust that:

class BillingController < ApplicationController
  include PasswordConfirmation

  confirm_password only: %w[update], every: 5.minutes
end

The magic happens in the PasswordConfirmation concern:

# app/controllers/concerns/password_confirmation.rb
module PasswordConfirmation
  extend ActiveSupport::Concern

  class_methods do
    def confirm_password(every: 10.minutes, **)
      before_action -> { require_password(every) }, **
    end
  end

  private

  def require_password(expiry_time)
    return if password_confirmed_within?(expiry_time)

    store_return_path

    redirect_to new_password_confirmation_path
  end

  def password_confirmed_within?(expiry)
    return false unless session[:password_confirmed_at]

    Time.at(session[:password_confirmed_at]).after?(expiry.ago)
  end
end

The confirmation form itself is straightforward:

<%# app/views/password_confirmations/new.html.erb %>
<%= form_with url: password_confirmations_path do |form| %>
  <div>
    <%= form.label :current_password %>

    <%= form.password_field :current_password, autocomplete: "current-password" %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

This view is intentionally minimal without any styling. You can easily enhance it using Rails Designer’s Form Builder to make it good-looking. For an even smoother user experience, consider using Rails Designer’s modal UI component to display the confirmation form in a modal overlay instead of a full page.

The controller handling the confirmation is equally simple:

# app/controllers/password_confirmations_controller.rb
class PasswordConfirmationsController < ApplicationController
  def new
  end

  def create
    if Current.user.authenticate(params[:current_password])
      session[:password_confirmed_at] = Time.current.to_i

      redirect_to(session.delete(:password_return_to) || root_path)
    else
      render :new, status: :unprocessable_entity
    end
  end
end

Don’t forget to add the routes:

# config/routes.rb
Rails.application.routes.draw do
+  resources :password_confirmations, only: %w[new create]
end

That’s all there is to it! You now have a simple yet effective way to add an extra security layer to sensitive operations in your Rails app. The implementation is straightforward, and easy to add to other parts of your app.


This content originally appeared on DEV Community and was authored by Rails Designer