File: two_factor_backupable.rb

package info (click to toggle)
ruby-devise-two-factor 4.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 264 kB
  • sloc: ruby: 528; makefile: 3
file content (57 lines) | stat: -rw-r--r-- 1,755 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
module Devise
  module Models
    # 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
    module TwoFactorBackupable
      extend ActiveSupport::Concern

      def self.required_fields(klass)
        [:otp_backup_codes]
      end

      # 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
      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::Encryptor.digest(self.class, code) }
        self.otp_backup_codes = hashed_codes

        codes
      end

      # Returns true and invalidates the given code
      # iff that code is a valid backup code.
      def invalidate_otp_backup_code!(code)
        codes = self.otp_backup_codes || []

        codes.each do |backup_code|
          next unless Devise::Encryptor.compare(self.class, backup_code, code)

          codes.delete(backup_code)
          self.otp_backup_codes = codes
          return true
        end

        false
      end

    protected

      module ClassMethods
        Devise::Models.config(self, :otp_backup_code_length,
                                    :otp_number_of_backup_codes,
                                    :pepper)
      end
    end
  end
end