hcb icon indicating copy to clipboard operation
hcb copied to clipboard

[Login] Create back up codes

Open garyhtou opened this issue 1 year ago • 2 comments

Similar to what github has. hash them when storing in the DB

When a user wants to restore their account.

  1. Successful complete email login code
  2. Enter a valid back up code

garyhtou avatar Jun 13 '24 20:06 garyhtou

just going to create a new branch - was trying to resolve the merge conflicts but realized it's probably easier to just start over and steal some code from my old branch

rluodev avatar Oct 19 '24 00:10 rluodev

  • A user can request backup codes and view them once.
  • When backup codes are requested, we prompt them (almost force them) to note them down somewhere.
    • If a user doesn't confirm they've saved the backup codes anywhere, the process is incomplete, and we basically reset (as if they didn't request them in the first place).
    • We'll provide a copy button to make it easy to paste it into their password manager
    • We will NOT provide a .txt file of the backup codes.
    • We will provide 10 backup codes.
  • Each backup code can only be used once
  • Hash backup codes
  • Prompt users to generate backup codes after turning on 2FA
  • When backup codes are rolled, the old ones are immediately invalidated.
  • Users should be able to disable back up codes.
  • Send a mailer once backup codes have been full setup, and anytime backup codes are rolled.
    • Also send a mailer when a backup code is used.
  • When the user has two or less unused backup codes, we will prompt them to regenerate (basically roll) their backup codes.
  • Ensure backup codes are unique (across all)
    • Add a database unique index
    • Add a rails validation
    • The generate_backup_codes should retry if it accidentally generates a used code.

Implementation

Login::BackupCode

  • belongs_to user
  • aasm_state
    • unsaved # user has generated codes, but hasn't confirmed they saved them.
    • unused
    • used
    • invalidated # when rolled or backup codes disabled
  • hash

User

  • has_many :backup_codes
  • has_many :unused_backup_codes, -> { where(aasm_state: :unused) }, class_name: "Login::BackupCodes"
  • def backup_codes_enabled? = unused_backup_codes.any?
  •  def generate_backup_codes
        backup_codes.each &:mark_invalidated!
    
        @codes = 10.times.map do
          code = SecureRandom.hex(10)
          backup_codes.create(hash: some_hash_function(code), aasm_state: :unsaved)
          code
        end
     end
    

In the controller, we call the User#generate_backup_codes and render the codes. When the user click the "I have saved them" button, we'll make an API call to activate those backup codes (current_user.backup_codes.each(&:mark_unused))

garyhtou avatar May 08 '25 21:05 garyhtou