2311efd03e
Previously if someone started signing in on their phone, but finished it on their laptop, they'd end up on the menu screen with no account. Bu tracking the purpose of a Magic Link we can always direct the user to sign up if they requested a magic link through sign up. This also makes the logic for changing the copy in the email more robust.
44 lines
930 B
Ruby
44 lines
930 B
Ruby
class MagicLink < ApplicationRecord
|
|
CODE_LENGTH = 6
|
|
EXPIRATION_TIME = 15.minutes
|
|
|
|
belongs_to :identity
|
|
|
|
enum :purpose, %w[ sign_in sign_up ], prefix: :for, default: :sign_in
|
|
|
|
scope :active, -> { where(expires_at: Time.current...) }
|
|
scope :stale, -> { where(expires_at: ..Time.current) }
|
|
|
|
before_validation :generate_code, on: :create
|
|
before_validation :set_expiration, on: :create
|
|
|
|
validates :code, uniqueness: true, presence: true
|
|
|
|
class << self
|
|
def consume(code)
|
|
active.find_by(code: Code.sanitize(code))&.consume
|
|
end
|
|
|
|
def cleanup
|
|
stale.delete_all
|
|
end
|
|
end
|
|
|
|
def consume
|
|
destroy
|
|
self
|
|
end
|
|
|
|
private
|
|
def generate_code
|
|
self.code ||= loop do
|
|
candidate = Code.generate(CODE_LENGTH)
|
|
break candidate unless self.class.exists?(code: candidate)
|
|
end
|
|
end
|
|
|
|
def set_expiration
|
|
self.expires_at ||= EXPIRATION_TIME.from_now
|
|
end
|
|
end
|