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.



10
11
12
13
# File 'app/models/otp_secret.rb', line 10

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

Instance Attribute Details

#secretObject (readonly)

Returns the value of attribute secret.



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

def secret
  @secret
end

#userObject (readonly)

Returns the value of attribute user.



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

def user
  @user
end

Instance Method Details

#account_nameObject



15
16
17
# File 'app/models/otp_secret.rb', line 15

def 
  user.email
end

#disable!Object



19
20
21
22
23
24
# File 'app/models/otp_secret.rb', line 19

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

#enable!(recovery_codes) ⇒ Object



26
27
28
29
30
31
# File 'app/models/otp_secret.rb', line 26

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

#generateObject



33
34
35
# File 'app/models/otp_secret.rb', line 33

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

#generate_recovery_codesObject



37
38
39
# File 'app/models/otp_secret.rb', line 37

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

#provisioning_uriObject



41
42
43
# File 'app/models/otp_secret.rb', line 41

def provisioning_uri
  totp.provisioning_uri()
end

#regenerate_recovery_codes!Object



45
46
47
48
49
# File 'app/models/otp_secret.rb', line 45

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

#signed_messageObject



51
52
53
54
55
# File 'app/models/otp_secret.rb', line 51

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

#validate_otp!(code) ⇒ Object



57
58
59
60
61
62
# File 'app/models/otp_secret.rb', line 57

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



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

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



72
73
74
# File 'app/models/otp_secret.rb', line 72

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

#verify(params) ⇒ Object



76
77
78
79
# File 'app/models/otp_secret.rb', line 76

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