password-reset-flow

rbarazi's avatarfrom rbarazi

Implement secure password reset with Rails 8's built-in token generation. Use when building "forgot password" functionality with email verification and time-limited reset tokens.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 7, 2026

When & Why to Use This Skill

This Claude skill provides a standardized, secure pattern for implementing password reset flows using Rails 8's native features. It streamlines the creation of 'forgot password' functionality by leveraging built-in signed token generation, ensuring cryptographically secure, time-limited, and single-use recovery links without the need for external dependencies.

Use Cases

  • Implementing a native 'forgot password' system in Rails 8 applications to minimize external gem dependencies.
  • Adding secure, time-limited email verification and password recovery tokens that expire automatically after 15 minutes.
  • Enhancing application security by adopting cryptographically signed, single-use tokens that invalidate immediately after a password change.
  • Building privacy-conscious authentication flows that prevent user enumeration by providing uniform feedback for both valid and invalid email addresses.
namepassword-reset-flow
descriptionImplement secure password reset with Rails 8's built-in token generation. Use when building "forgot password" functionality with email verification and time-limited reset tokens.

Password Reset Flow Pattern

Secure password reset using Rails 8's built-in token generation. No external gems required.

When to Use

  • Building "forgot password" functionality
  • Implementing email-based password recovery
  • Adding secure token-based password reset

Security Principles

  1. Don't reveal user existence - Same message for valid/invalid emails
  2. Time-limited tokens - 15-minute expiry by default
  3. Single-use tokens - Token invalidated after password change
  4. Signed tokens - Cryptographically secure, tamper-proof

Quick Start

1. User Model

Rails 8's has_secure_password provides built-in support:

class User < ApplicationRecord
  has_secure_password
  # Automatically provides:
  # - password_reset_token (generates signed token)
  # - find_by_password_reset_token!(token)
end

2. Passwords Controller

# app/controllers/passwords_controller.rb
class PasswordsController < ApplicationController
  allow_unauthenticated_access
  before_action :set_user_by_token, only: %i[edit update]

  def new
  end

  def create
    if user = User.find_by(email_address: params[:email_address])
      PasswordsMailer.reset(user).deliver_later
    end
    # Always show success - don't reveal if user exists
    redirect_to new_session_path, notice: t("passwords.flash.create.success")
  end

  def edit
  end

  def update
    if @user.update(password_params)
      redirect_to new_session_path, notice: t("passwords.flash.update.success")
    else
      render :edit, status: :unprocessable_content
    end
  end

  private

  def password_params
    params.permit(:password, :password_confirmation)
  end

  def set_user_by_token
    @user = User.find_by_password_reset_token!(params[:token])
  rescue ActiveSupport::MessageVerifier::InvalidSignature
    redirect_to new_password_path, alert: t("passwords.flash.invalid_token")
  end
end

3. Routes

resources :passwords, param: :token

4. Mailer

class PasswordsMailer < ApplicationMailer
  def reset(user)
    @user = user
    mail subject: t('passwords.mailer.reset.subject'), to: user.email_address
  end
end

Token Mechanics

# Generate token
user.password_reset_token
# => "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik..."

# Find user by token
User.find_by_password_reset_token!(token)
# => #<User id: 1, ...>

# Default expiry: 15 minutes
# Customize in: config/initializers/active_record.rb
Rails.application.config.active_record.password_reset_token_in = 1.hour

Reference Files

For complete implementation details: