ruby

How to Implement Two-Factor Authentication in Ruby on Rails: Complete Guide 2024

Learn how to implement secure two-factor authentication (2FA) in Ruby on Rails. Discover code examples for TOTP, SMS verification, backup codes, and security best practices to protect your web application.

How to Implement Two-Factor Authentication in Ruby on Rails: Complete Guide 2024

Two-factor authentication (2FA) has become essential for modern web applications, providing an additional security layer beyond traditional password-based authentication. I’ll share my experience implementing various 2FA methods in Ruby on Rails applications.

Authentication Foundation

The core of 2FA implementation starts with a solid authentication system. Rails provides excellent tools through libraries like Devise, but we need to extend them for 2FA support. Here’s the basic setup:

class User < ApplicationRecord
  devise :two_factor_authenticatable,
         :two_factor_backupable

  encrypts :otp_secret
  validates :phone_number, presence: true, if: :sms_enabled?
end

TOTP Implementation

Time-based One-Time Passwords (TOTP) offer reliable security. The implementation requires generating and storing a secret key:

class TotpService
  def initialize(user)
    @user = user
    @totp = ROTP::TOTP.new(@user.otp_secret)
  end

  def generate_secret
    ROTP::Base32.random
  end

  def verify_code(code)
    @totp.verify(code, drift_behind: 15)
  end

  def provisioning_uri
    @totp.provisioning_uri(@user.email)
  end
end

SMS Verification

SMS verification provides familiar security for users. Here’s how to implement it using Twilio:

class SmsVerification
  def initialize(user)
    @user = user
    @client = Twilio::REST::Client.new
  end

  def send_code
    code = generate_code
    @user.update(sms_code: code, code_sent_at: Time.current)
    
    @client.messages.create(
      from: Rails.application.credentials.twilio_phone,
      to: @user.phone_number,
      body: "Your verification code is: #{code}"
    )
  end

  private

  def generate_code
    SecureRandom.random_number(100000..999999).to_s
  end
end

Backup Codes Generation

Users need backup codes for account recovery. Generate them securely:

class BackupCodesGenerator
  def generate
    Array.new(10) do
      SecureRandom.hex(4)
    end
  end

  def hash_codes(codes)
    codes.map { |code| BCrypt::Password.create(code) }
  end

  def verify_code(input, hashed_codes)
    hashed_codes.any? do |stored_code|
      BCrypt::Password.new(stored_code) == input
    end
  end
end

Rate Limiting

Protect against brute force attacks with rate limiting:

class RateLimiter
  def initialize(user)
    @user = user
    @redis = Redis.new
  end

  def within_limit?
    attempts = @redis.get(cache_key).to_i
    attempts < max_attempts
  end

  def increment
    @redis.multi do |multi|
      multi.incr(cache_key)
      multi.expire(cache_key, timeout)
    end
  end

  private

  def cache_key
    "2fa_attempts:#{@user.id}"
  end

  def max_attempts
    5
  end

  def timeout
    1.hour.to_i
  end
end

Recovery Process

Implement a secure recovery workflow:

class RecoveryProcess
  def initialize(user)
    @user = user
  end

  def start_recovery
    token = generate_recovery_token
    send_recovery_email(token)
    @user.update(recovery_token: token)
  end

  def verify_token(token)
    return false unless @user.recovery_token_valid?
    @user.recovery_token == token
  end

  private

  def generate_recovery_token
    SecureRandom.urlsafe_base64(32)
  end

  def send_recovery_email(token)
    RecoveryMailer.send_instructions(@user, token).deliver_later
  end
end

Session Management

Maintain secure sessions during 2FA:

class TwoFactorSession
  def initialize(session)
    @session = session
  end

  def mark_as_pending(user_id)
    @session[:pending_2fa_user_id] = user_id
    @session[:pending_2fa_timestamp] = Time.current.to_i
  end

  def complete_authentication
    @session.delete(:pending_2fa_user_id)
    @session.delete(:pending_2fa_timestamp)
    @session[:authenticated_at] = Time.current.to_i
  end

  def expired?
    return true unless @session[:pending_2fa_timestamp]
    Time.current.to_i - @session[:pending_2fa_timestamp] > 10.minutes.to_i
  end
end

Security Logging

Track authentication attempts and security events:

class SecurityLogger
  def initialize(user)
    @user = user
  end

  def log_2fa_attempt(success:, method:, ip_address:)
    SecurityLog.create!(
      user: @user,
      event_type: '2fa_attempt',
      success: success,
      method: method,
      ip_address: ip_address,
      metadata: {
        user_agent: Current.user_agent,
        location: geolocate(ip_address)
      }
    )
  end

  private

  def geolocate(ip_address)
    Geocoder.search(ip_address).first&.country
  end
end

QR Code Generation

Generate QR codes for TOTP setup:

class QrCodeGenerator
  def initialize(user)
    @user = user
    @totp = ROTP::TOTP.new(@user.otp_secret)
  end

  def generate
    RQRCode::QRCode.new(
      @totp.provisioning_uri(@user.email)
    ).as_png(
      size: 300,
      border_modules: 2
    )
  end
end

Controller Integration

Tie everything together in the controller:

class TwoFactorAuthenticationController < ApplicationController
  before_action :require_login
  before_action :check_rate_limit, only: [:verify]

  def setup
    service = TotpService.new(current_user)
    @secret = service.generate_secret
    @qr_code = QrCodeGenerator.new(current_user).generate
  end

  def verify
    result = verify_2fa_code(params[:code])
    
    if result
      session_manager.complete_authentication
      redirect_to dashboard_path
    else
      rate_limiter.increment
      flash.now[:error] = 'Invalid code'
      render :verify
    end
  end

  private

  def verify_2fa_code(code)
    service = TotpService.new(current_user)
    result = service.verify_code(code)
    
    SecurityLogger.new(current_user).log_2fa_attempt(
      success: result,
      method: 'totp',
      ip_address: request.remote_ip
    )
    
    result
  end

  def session_manager
    @session_manager ||= TwoFactorSession.new(session)
  end

  def rate_limiter
    @rate_limiter ||= RateLimiter.new(current_user)
  end

  def check_rate_limit
    unless rate_limiter.within_limit?
      redirect_to lockout_path
    end
  end
end

The implementation of 2FA requires careful consideration of security, user experience, and edge cases. Regular security audits and updates are essential to maintain strong protection. Testing various scenarios, including backup code usage and rate limiting, ensures robust implementation.

Remember to implement proper error handling, validation, and user feedback throughout the authentication flow. Consider implementing progressive security measures based on user behavior and risk assessment.

Monitor authentication attempts and analyze patterns to detect potential security threats. Regular backups of 2FA-related data and proper encryption of sensitive information are crucial for maintaining security standards.

Keywords: two factor authentication rails, rails 2fa implementation, ruby on rails authentication security, TOTP rails, SMS verification rails, two factor auth devise, rails secure authentication, backup codes rails, rate limiting authentication rails, 2fa recovery rails, rails session security, QR code authentication rails, rails authentication best practices, secure user authentication rails, multi factor authentication ruby, rails authentication controller, rails security logging, devise two factor setup, rails OTP implementation, authentication monitoring rails, devise SMS verification, rails authentication flow, 2fa security measures rails, two step verification rails, ROTP implementation rails, rails auth token security, twilio rails authentication, rails password security, rails secure session management, rails authentication testing, 2fa backup solutions rails



Similar Posts
Blog Image
Advanced Rails Document Management: Best Practices and Implementation Guide 2024

Learn how to build a robust document management system in Ruby on Rails. Discover practical code examples for version control, search, access control, and workflow automation. Enhance your Rails app with secure file handling. #Rails #Ruby

Blog Image
8 Powerful CI/CD Techniques for Streamlined Rails Deployment

Discover 8 powerful CI/CD techniques for Rails developers. Learn how to automate testing, implement safer deployments, and create robust rollback strategies to ship high-quality code faster. #RubyonRails #DevOps

Blog Image
How Can You Transform Your Rails App with a Killer Admin Panel?

Crafting Sleek Admin Dashboards: Supercharging Your Rails App with Rails Admin Gems

Blog Image
6 Advanced Ruby on Rails Techniques for Optimizing Database Migrations and Schema Management

Optimize Rails database migrations: Zero-downtime, reversible changes, data updates, versioning, background jobs, and constraints. Enhance app scalability and maintenance. Learn advanced techniques now.

Blog Image
Is Active Admin the Key to Effortless Admin Panels in Ruby on Rails?

Crafting Sleek and Powerful Admin Panels in Ruby on Rails with Active Admin

Blog Image
7 Proven Techniques for Database Connection Pooling in Rails

Learn how to optimize Rails database connection pooling for faster apps. Discover proven techniques to reduce overhead, prevent timeouts, and scale efficiently by properly configuring ActiveRecord pools. Improve response times by 40%+ with these expert strategies.