require 'test_helper' require 'model_tests_helper' class OtpAuthenticatableTest < ActiveSupport::TestCase def setup new_user end test 'new users have a non-nil secret set' do assert_not_nil User.first.otp_auth_secret end test 'new users have OTP disabled by default' do assert !User.first.otp_enabled end test 'users should have an instance of TOTP/ROTP objects' do u = User.first assert u.time_based_otp.is_a? ROTP::TOTP assert u.recovery_otp.is_a? ROTP::HOTP end test 'users should have their otp_auth_secret/persistence_seed set on creation' do assert User.first.otp_auth_secret assert User.first.otp_persistence_seed end test 'reset_otp_credentials should generate new secrets and disable OTP' do u = User.first u.update_attribute(:otp_enabled, true) assert u.otp_enabled otp_auth_secret = u.otp_auth_secret otp_persistence_seed = u.otp_persistence_seed u.reset_otp_credentials! assert !(otp_auth_secret == u.otp_auth_secret) assert !(otp_persistence_seed == u.otp_persistence_seed) assert !u.otp_enabled end test 'reset_otp_persistence should generate new persistence_seed but NOT change the otp_auth_secret' do u = User.first u.update_attribute(:otp_enabled, true) assert u.otp_enabled otp_auth_secret = u.otp_auth_secret otp_persistence_seed = u.otp_persistence_seed u.reset_otp_persistence! assert (otp_auth_secret == u.otp_auth_secret) assert !(otp_persistence_seed == u.otp_persistence_seed) assert u.otp_enabled end test 'generating a challenge, should retrieve the user later' do u = User.first u.update_attribute(:otp_enabled, true) challenge = u.generate_otp_challenge! w = User.find_valid_otp_challenge(challenge) assert w.is_a? User assert_equal w,u end test 'expiring the challenge, should retrieve nothing' do u = User.first u.update_attribute(:otp_enabled, true) challenge = u.generate_otp_challenge!(1.second) sleep(2) w = User.find_valid_otp_challenge(challenge) assert_nil w end test 'expired challenges should not be valid' do u = User.first u.update_attribute(:otp_enabled, true) challenge = u.generate_otp_challenge!(1.second) sleep(2) assert_equal false, u.otp_challenge_valid? end test 'null otp challenge' do u = User.first u.update_attribute(:otp_enabled, true) assert_equal false, u.validate_otp_token('') assert_equal false, u.validate_otp_token(nil) end test 'generated otp token should be valid for the user' do u = User.first u.update_attribute(:otp_enabled, true) secret = u.otp_auth_secret token = ROTP::TOTP.new(secret).now assert_equal true, u.validate_otp_token(token) end test 'generated otp token, out of drift window, should be NOT valid for the user' do u = User.first u.update_attribute(:otp_enabled, true) secret = u.otp_auth_secret [3.minutes.from_now, 3.minutes.ago].each do |time| token = ROTP::TOTP.new(secret).at(time) assert_equal false, u.valid_otp_token?(token) end end test 'recovery secrets should be valid, and valid only once' do u = User.first u.update_attribute(:otp_enabled, true) recovery = u.next_otp_recovery_tokens assert u.valid_otp_recovery_token? recovery.fetch(0) assert_nil u.valid_otp_recovery_token?(recovery.fetch(0)) assert u.valid_otp_recovery_token? recovery.fetch(2) end end