Module: PasskeyAuth::Concerns::Passwordless

Extended by:
ActiveSupport::Concern
Defined in:
lib/passkey_auth/concerns/passwordless.rb

Defined Under Namespace

Classes: AuthenticationError

Instance Method Summary collapse

Instance Method Details

#can_request_magic_link?Boolean

Magic link authentication methods



29
30
31
32
33
34
# File 'lib/passkey_auth/concerns/passwordless.rb', line 29

def can_request_magic_link?
  return true if magic_link_sent_at.nil?

  # Rate limit based on configuration
  magic_link_sent_at < PasskeyAuth.magic_link_rate_limit.ago
end

#create_magic_link!(purpose: "login") ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/passkey_auth/concerns/passwordless.rb', line 36

def create_magic_link!(purpose: "login")
  return nil unless can_request_magic_link?

  transaction do
    # Expire any existing active magic links
    magic_links.active.update_all(used_at: Time.current)

    # Create new magic link
    magic_link = magic_links.create!(purpose: purpose)

    # Update rate limiting timestamp
    update!(magic_link_sent_at: Time.current)

    magic_link
  end
end

#passwordless?Boolean



97
98
99
# File 'lib/passkey_auth/concerns/passwordless.rb', line 97

def passwordless?
  webauthn_credentials.exists? || magic_links.active.exists?
end

#send_magic_link!Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/passkey_auth/concerns/passwordless.rb', line 53

def send_magic_link!
  unless can_request_magic_link?
    return { success: false, error: "rate_limited" }
  end

  magic_link = create_magic_link!

  if magic_link
    PasskeyAuth::MagicLinkMailer.(magic_link).deliver_later

    # Call hook if configured
    PasskeyAuth.on_magic_link_requested&.call(self, magic_link)

    { success: true, magic_link: magic_link }
  else
    { success: false, error: "creation_failed" }
  end
rescue StandardError => e
  Rails.logger.error "MagicLink error: #{e.message}"
  { success: false, error: "unexpected_error" }
end