Class: RailsBase::Mfa::Totp::ValidateCode

Inherits:
ServiceBase
  • Object
show all
Includes:
Helper
Defined in:
app/services/rails_base/mfa/totp/validate_code.rb

Instance Method Summary collapse

Methods included from Helper

#current_code, #lgp, #otp, #secret

Methods inherited from ServiceBase

inherited, #internal_validate, #service_base_logging

Methods included from ServiceLogging

#aletered_message, #class_name, #log, #log_prefix, #logger, #service_id

Instance Method Details

#callObject



10
11
12
13
14
# File 'app/services/rails_base/mfa/totp/validate_code.rb', line 10

def call
  return if validate_and_consume_otp

  context.fail!(message: "Invalid TOTP code")
end

#consume_otp!Object

An OTP cannot be used more than once in a given timestep Storing timestep of last valid OTP is sufficient to satisfy this requirement



34
35
36
37
38
39
40
41
42
43
44
# File 'app/services/rails_base/mfa/totp/validate_code.rb', line 34

def consume_otp!
  timestep = Time.now.utc.to_i / otp.interval
  if user.consumed_timestep != timestep
    user.consumed_timestep = timestep
    log(level: :debug, msg: "#{lgp} Consuming timestep based on code input")
    return user.save(validate: false)
  end

  log(level: :debug, msg: "#{lgp} Timestep for code was already consumed. Invalid code")
  false
end

#validate!Object



46
47
48
49
50
# File 'app/services/rails_base/mfa/totp/validate_code.rb', line 46

def validate!
  raise "Expected user to be a User. " unless User === user
  raise "Expected otp_code to be present" if otp_code.nil?
  raise "Expected `otp_secret` passed in or `otp_secret` present on User" if secret.nil?
end

#validate_and_consume_otpObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'app/services/rails_base/mfa/totp/validate_code.rb', line 16

def validate_and_consume_otp
  if user.consumed_timestep
    # reconstruct the timestamp of the last consumed timestep
    after_timestamp = user.consumed_timestep * otp.interval
  end

  if otp.verify(otp_code.gsub(/\s+/, ""), drift_behind: User.totp_drift_behind, drift_ahead: User.totp_drift_ahead, after: after_timestamp)
    log(level: :debug, msg: "#{lgp} Correct code provided")
    return consume_otp!
  else
    log(level: :debug, msg: "#{lgp} InValid code provided")
  end

  false
end