class PasswordResetsController < ApplicationController
before_action :find_user_by_token, only: [:edit, :update]
def create
user = User.find_by(email: params[:email]&.downcase)
if user
token = user.signed_id(purpose: :password_reset, expires_in: 1.hour)
UserMailer.password_reset(user, token).deliver_later
end
# Always show success to prevent email enumeration
render json: { message: 'If that email exists, a reset link has been sent' }, status: :ok
end
def update
if @user.update(password: params[:password], password_confirmation: params[:password_confirmation])
# Invalidate all sessions
@user.update_column(:auth_token, SecureRandom.hex(32))
render json: { message: 'Password reset successfully' }, status: :ok
else
render json: { errors: @user.errors }, status: :unprocessable_entity
end
end
private
def find_user_by_token
@user = User.find_signed(params[:token], purpose: :password_reset)
unless @user
render json: { error: 'INVALID_OR_EXPIRED_TOKEN' }, status: :unauthorized
end
end
end
Password reset workflows require careful security design to prevent account takeover. I generate time-limited, single-use tokens using Rails' signed_id feature which creates tamper-proof tokens without database storage. The token expires after a short window (1-2 hours) and includes the user's password_digest in the signature, so it automatically invalidates when the password changes. I send reset links via email to the address on file, never to user-provided addresses. The reset form requires the new password twice and doesn't echo the current password. Rate limiting prevents brute force token guessing. After successful reset, I invalidate all active sessions to protect against session fixation. This flow balances security and user experience.
Related snips
class CommentsController < ApplicationController
before_action :set_post
def create
@comment = @post.comments.build(comment_params)
System test: asserting Turbo Stream responses
class Post < ApplicationRecord
belongs_to :author, class_name: 'User'
has_many :comments, dependent: :destroy
scope :published, -> { where.not(published_at: nil).where('published_at <= ?', Time.current) }
scope :draft, -> { where(published_at: nil) }
ActiveRecord scopes for reusable query logic
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
class PostsController < ApplicationController
def index
@posts = Post.includes(:author)
.order(created_at: :desc)
.page(params[:page])
.per(10)
Turbo Frames: infinite scroll with lazy-loading frame
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
Share this code
Here's the card — post it anywhere.