Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
23
research/chatwoot/app/services/mfa/authentication_service.rb
Normal file
23
research/chatwoot/app/services/mfa/authentication_service.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
class Mfa::AuthenticationService
|
||||
pattr_initialize [:user!, :otp_code, :backup_code]
|
||||
|
||||
def authenticate
|
||||
return false unless user
|
||||
|
||||
return authenticate_with_otp if otp_code.present?
|
||||
return authenticate_with_backup_code if backup_code.present?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate_with_otp
|
||||
user.validate_and_consume_otp!(otp_code)
|
||||
end
|
||||
|
||||
def authenticate_with_backup_code
|
||||
mfa_service = Mfa::ManagementService.new(user: user)
|
||||
mfa_service.validate_backup_code!(backup_code)
|
||||
end
|
||||
end
|
||||
88
research/chatwoot/app/services/mfa/management_service.rb
Normal file
88
research/chatwoot/app/services/mfa/management_service.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
class Mfa::ManagementService
|
||||
pattr_initialize [:user!]
|
||||
|
||||
def enable_two_factor!
|
||||
user.otp_secret = User.generate_otp_secret
|
||||
user.save!
|
||||
end
|
||||
|
||||
def disable_two_factor!
|
||||
user.otp_secret = nil
|
||||
user.otp_required_for_login = false
|
||||
user.otp_backup_codes = nil
|
||||
user.save!
|
||||
end
|
||||
|
||||
def verify_and_activate!
|
||||
ActiveRecord::Base.transaction do
|
||||
user.update!(otp_required_for_login: true)
|
||||
backup_codes_generated? ? nil : generate_backup_codes!
|
||||
end
|
||||
end
|
||||
|
||||
def two_factor_provisioning_uri
|
||||
return nil if user.otp_secret.blank?
|
||||
|
||||
issuer = 'Chatwoot'
|
||||
label = user.email
|
||||
user.otp_provisioning_uri(label, issuer: issuer)
|
||||
end
|
||||
|
||||
def generate_backup_codes!
|
||||
codes = Array.new(10) { SecureRandom.hex(4).upcase }
|
||||
user.otp_backup_codes = codes
|
||||
user.save!
|
||||
codes
|
||||
end
|
||||
|
||||
def validate_backup_code!(code)
|
||||
return false unless valid_backup_code_input?(code)
|
||||
|
||||
codes = user.otp_backup_codes
|
||||
found_index = find_matching_code_index(codes, code)
|
||||
|
||||
return false if found_index.nil?
|
||||
|
||||
mark_code_as_used(codes, found_index)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_backup_code_input?(code)
|
||||
user.otp_backup_codes.present? && code.present?
|
||||
end
|
||||
|
||||
def find_matching_code_index(codes, code)
|
||||
found_index = nil
|
||||
|
||||
# Constant-time comparison to prevent timing attacks
|
||||
codes.each_with_index do |stored_code, idx|
|
||||
is_match = ActiveSupport::SecurityUtils.secure_compare(stored_code, code)
|
||||
is_unused = stored_code != 'XXXXXXXX'
|
||||
found_index = idx if is_match && is_unused
|
||||
end
|
||||
|
||||
found_index
|
||||
end
|
||||
|
||||
def mark_code_as_used(codes, index)
|
||||
codes[index] = 'XXXXXXXX'
|
||||
user.otp_backup_codes = codes
|
||||
user.save!
|
||||
true
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def backup_codes_generated?
|
||||
user.otp_backup_codes.present?
|
||||
end
|
||||
|
||||
def mfa_enabled?
|
||||
user.otp_required_for_login?
|
||||
end
|
||||
|
||||
def two_factor_setup_pending?
|
||||
user.otp_secret.present? && !user.otp_required_for_login?
|
||||
end
|
||||
end
|
||||
28
research/chatwoot/app/services/mfa/token_service.rb
Normal file
28
research/chatwoot/app/services/mfa/token_service.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
class Mfa::TokenService < BaseTokenService
|
||||
pattr_initialize [:user, :token]
|
||||
|
||||
MFA_TOKEN_EXPIRY = 5.minutes
|
||||
|
||||
def generate_token
|
||||
@payload = build_payload
|
||||
super
|
||||
end
|
||||
|
||||
def verify_token
|
||||
decoded = decode_token
|
||||
return nil if decoded.blank?
|
||||
|
||||
User.find(decoded[:user_id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_payload
|
||||
{
|
||||
user_id: user.id,
|
||||
exp: MFA_TOKEN_EXPIRY.from_now.to_i
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user