# frozen_string_literal: true require "active_record" require "attr_keyring" require "defaults" require "url_signature" require "rails/engine" require_relative "magic_link/engine" require_relative "magic_link/model" require_relative "magic_link/version" require_relative "magic_link/verifier" require_relative "magic_link/code_verifier" module SimpleAuth module MagicLink class << self attr_accessor :code, :ttl, :purpose, :url delegate :attr_keyring, :keyring, to: :model end def self.default_code -> { Array.new(6) { SecureRandom.random_number(0..9) }.join } end def self.encryption_key MagicLink.keyring.current_key.encryption_key end def self.model Model end def self.create!( email:, code: MagicLink.code.call, purpose: MagicLink.purpose, expires_at: Time.now.utc + MagicLink.ttl ) model.where( email_digest: model.keyring.digest(email), purpose: ).delete_all model.create!( email:, code:, purpose:, expires_at: ) end # Verify whether a url is valid or not. # The provided `id` must match whatever is set on the url. # Ideally, this `id` is saved on the user's session and compared whenever # the user visits the link. This is the only way you can ensure that the # url hasn't be generated on a different device. def self.verify( id:, url:, time: Time.current, purpose: MagicLink.purpose, verifier: Verifier ) verifier.call(id:, url:, purpose:, time:) end def self.verify_code( id:, code:, time: Time.current, purpose: MagicLink.purpose, verifier: CodeVerifier ) verifier.call(id:, code:, purpose:, time:) end def self.restore_defaults! self.code = default_code self.ttl = 3.minutes self.purpose = "default" self.url = proc { raise "please set the url" } end def self.clean! model.where("expires_at < ?", Time.current).delete_all end restore_defaults! end end