authenticator_app.rb 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. class Auth::TwoFactor::AuthenticationMethod::AuthenticatorApp < Auth::TwoFactor::AuthenticationMethod
  3. ORDER = 2000
  4. def verify(payload, configuration = user_two_factor_preference_configuration)
  5. return verify_result(false) if payload.blank? || configuration.blank?
  6. secret = configuration[:secret]
  7. return verify_result(false) if secret.blank?
  8. last_otp_at = configuration[:last_otp_at]
  9. timestamp = totp(secret).verify(payload, drift_behind: 15, after: last_otp_at)
  10. # The provided code is invalid if we don't get a timestamp value.
  11. return verify_result(false) if timestamp.blank?
  12. # Return new configuration hash with the updated timestamp.
  13. verify_result(true, configuration: configuration, new_configuration: { last_otp_at: timestamp })
  14. end
  15. def initiate_configuration
  16. require 'rotp' # Only load when it is actually used
  17. secret = ROTP::Base32.random_base32
  18. {
  19. secret: secret,
  20. provisioning_uri: totp(secret).provisioning_uri(user.login),
  21. }
  22. end
  23. def without_client_config?
  24. true
  25. end
  26. private
  27. def issuer
  28. Setting.get('organization').presence || Setting.get('product_name').presence || 'Zammad'
  29. end
  30. def totp(secret)
  31. require 'rotp' # Only load when it is actually used
  32. ROTP::TOTP.new(secret, issuer: issuer)
  33. end
  34. end