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
Are You Ready to Simplify File Uploads in Rails with Paperclip?

Transforming File Uploads in Ruby on Rails with the Magic of Paperclip

Blog Image
8 Essential Techniques for Building Responsive Rails Apps: Mobile-Friendly Web Development

Discover 8 effective techniques for building responsive and mobile-friendly web apps with Ruby on Rails. Learn fluid layouts, media queries, and performance optimization. Improve your Rails development skills today!

Blog Image
Mastering Rails Active Storage: Simplify File Uploads and Boost Your Web App

Rails Active Storage simplifies file uploads, integrating cloud services like AWS S3. It offers easy setup, direct uploads, image variants, and metadata handling, streamlining file management in web applications.

Blog Image
What's the Secret Sauce Behind Ruby's Object Model?

Unlock the Mysteries of Ruby's Object Model for Seamless Coding Adventures

Blog Image
Unleash Real-Time Magic: Master WebSockets in Rails for Instant, Interactive Apps

WebSockets in Rails enable real-time features through Action Cable. They allow bidirectional communication, enhancing user experience with instant updates, chat functionality, and collaborative tools. Proper setup and scaling considerations are crucial for implementation.

Blog Image
Advanced Rails Content Versioning: Track, Compare, and Restore Data Efficiently

Learn effective content versioning techniques in Rails to protect user data and enhance collaboration. Discover 8 implementation methods from basic PaperTrail setup to advanced Git-like branching for seamless version control in your applications.