## Linear:
- https://github.com/chatwoot/chatwoot/issues/486

## Description
This PR implements Multi-Factor Authentication (MFA) support for user
accounts, enhancing security by requiring a second form of verification
during login. The feature adds TOTP (Time-based One-Time Password)
authentication with QR code generation and backup codes for account
recovery.

## Type of change

- [ ] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Added comprehensive RSpec tests for MFA controller functionality
- Tested MFA setup flow with QR code generation
- Verified OTP validation and backup code generation
- Tested login flow with MFA enabled/disabled

## Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Tanmay Deep Sharma
2025-09-18 16:49:24 +02:00
committed by GitHub
parent f03a52bd77
commit 239c4dcb91
33 changed files with 1345 additions and 37 deletions

View File

@@ -68,6 +68,16 @@ module Chatwoot
# Disable PDF/video preview generation as we don't use them
config.active_storage.previewers = []
# Active Record Encryption configuration
# Required for MFA/2FA features - skip if not using encryption
if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
config.active_record.encryption.primary_key = ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY']
config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY', nil)
config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT', nil)
config.active_record.encryption.support_unencrypted_data = true
config.active_record.encryption.store_key_references = true
end
end
def self.config
@@ -82,4 +92,16 @@ module Chatwoot
# ref: https://www.rubydoc.info/stdlib/openssl/OpenSSL/SSL/SSLContext#DEFAULT_PARAMS-constant
ENV['REDIS_OPENSSL_VERIFY_MODE'] == 'none' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
end
def self.encryption_configured?
# Check if proper encryption keys are configured
# MFA/2FA features should only be enabled when proper keys are set
ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present? &&
ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'].present? &&
ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT'].present?
end
def self.mfa_enabled?
encryption_configured?
end
end