Class: OtpSecret

Inherits:
Object
  • Object
show all
Defined in:
app/models/otp_secret.rb

Defined Under Namespace

Classes: InvalidUserError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user) ⇒ OtpSecret

Returns a new instance of OtpSecret.



8
9
10
11
# File 'app/models/otp_secret.rb', line 8

def initialize(user)
  @user = user
  @secret = user.otp_secret
end

Instance Attribute Details

#secretObject (readonly)

Returns the value of attribute secret.



6
7
8
# File 'app/models/otp_secret.rb', line 6

def secret
  @secret
end

#userObject (readonly)

Returns the value of attribute user.



6
7
8
# File 'app/models/otp_secret.rb', line 6

def user
  @user
end

Instance Method Details

#account_nameObject



13
14
15
# File 'app/models/otp_secret.rb', line 13

def 
  user.email
end

#disable!Object



17
18
19
20
21
22
# File 'app/models/otp_secret.rb', line 17

def disable!
  user.update(otp_enabled: false,
              otp_secret: nil,
              last_otp_at: nil,
              recovery_codes: [])
end

#enable!(recovery_codes) ⇒ Object



24
25
26
27
28
29
# File 'app/models/otp_secret.rb', line 24

def enable!(recovery_codes)
  user.update(otp_enabled: true,
              otp_secret: secret,
              last_otp_at: Time.zone.now,
              recovery_codes:)
end

#generateObject



31
32
33
# File 'app/models/otp_secret.rb', line 31

def generate
  @secret = ROTP::Base32.random
end

#generate_recovery_codesObject



35
36
37
# File 'app/models/otp_secret.rb', line 35

def generate_recovery_codes
  Array.new(10) { SecureRandom.alphanumeric(16) }
end

#provisioning_uriObject



39
40
41
# File 'app/models/otp_secret.rb', line 39

def provisioning_uri
  totp.provisioning_uri()
end

#regenerate_recovery_codes!Object



43
44
45
46
47
# File 'app/models/otp_secret.rb', line 43

def regenerate_recovery_codes!
  generate_recovery_codes.tap do |recovery_codes|
    user.update(recovery_codes:)
  end
end

#signed_messageObject



49
50
51
52
53
# File 'app/models/otp_secret.rb', line 49

def signed_message
  message_verifier.generate(
    { user_id: user.id, secret: }, expires_in: 1.hour
  )
end

#validate_otp!(code) ⇒ Object



55
56
57
58
59
60
# File 'app/models/otp_secret.rb', line 55

def validate_otp!(code)
  return false unless valid_otp?(code)

  user.update(last_otp_at: Time.zone.now)
  true
end

#validate_otp_or_recovery_code!(code) ⇒ Object



62
63
64
65
66
67
68
# File 'app/models/otp_secret.rb', line 62

def validate_otp_or_recovery_code!(code)
  if /^\d{6}$/.match?(code)
    validate_otp!(code)
  else
    validate_recovery_code!(code)
  end
end

#validate_recovery_code!(code) ⇒ Object



70
71
72
# File 'app/models/otp_secret.rb', line 70

def validate_recovery_code!(code)
  user.use_recovery_code!(code)
end

#verify(params) ⇒ Object



74
75
76
77
# File 'app/models/otp_secret.rb', line 74

def verify(params)
  @secret = verify_secret(params[:signed_message])
  valid_otp?(params[:otp])
end