Module: Devise::Models::TwoFactorBackupable
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/devise_two_factor/models/two_factor_backupable.rb
Overview
TwoFactorBackupable allows a user to generate backup codes which provide one-time access to their account in the event that they have lost access to their two-factor device
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
Instance Method Summary collapse
-
#generate_otp_backup_codes! ⇒ Object
1) Invalidates all existing backup codes 2) Generates otp_number_of_backup_codes backup codes 3) Stores the hashed backup codes in the database 4) Returns a plaintext array of the generated backup codes.
-
#invalidate_otp_backup_code!(code) ⇒ Object
Returns true and invalidates the given code iff that code is a valid backup code.
Class Method Details
.required_fields(klass) ⇒ Object
11 12 13 |
# File 'lib/devise_two_factor/models/two_factor_backupable.rb', line 11 def self.required_fields(klass) [:otp_backup_codes] end |
Instance Method Details
#generate_otp_backup_codes! ⇒ Object
1) Invalidates all existing backup codes 2) Generates otp_number_of_backup_codes backup codes 3) Stores the hashed backup codes in the database 4) Returns a plaintext array of the generated backup codes
19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/devise_two_factor/models/two_factor_backupable.rb', line 19 def generate_otp_backup_codes! codes = [] number_of_codes = self.class.otp_number_of_backup_codes code_length = self.class.otp_backup_code_length number_of_codes.times do codes << SecureRandom.hex(code_length / 2) # Hexstring has length 2*n end hashed_codes = codes.map { |code| Devise.bcrypt self.class, code } self.otp_backup_codes = hashed_codes codes end |
#invalidate_otp_backup_code!(code) ⇒ Object
Returns true and invalidates the given code iff that code is a valid backup code.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/devise_two_factor/models/two_factor_backupable.rb', line 36 def invalidate_otp_backup_code!(code) codes = self.otp_backup_codes || [] codes.each do |backup_code| # We hashed the code with Devise.bcrypt, so if Devise changes that # method, we'll have to adjust our comparison here to match it # TODO Fork Devise and encapsulate this logic in a helper bcrypt = ::BCrypt::Password.new(backup_code) hashed_code = ::BCrypt::Engine.hash_secret("#{code}#{self.class.pepper}", bcrypt.salt) next unless Devise.secure_compare(hashed_code, backup_code) codes.delete(backup_code) self.otp_backup_codes = codes return true end false end |