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