Skip to content

Magic Login Setup Guide

Magic Link is a passwordless authentication method that allows users to log in by clicking a secure email link instead of entering a password. Mindful Auth’s magic link authentication provides enterprise-grade security through a multi-layered defense system. When 2FA is enabled, users must pass through four distinct security layers before gaining access.

Without 2FA (Two Layers)

  1. Magic Link Token - Unique, time-limited token sent to verified email address
  2. Turnstile Bot Protection - Cloudflare Turnstile validates human interaction on verification page

With 2FA Enabled (Four Layers)

  1. Magic Link Token - Unique, time-limited token sent to verified email address
  2. First Turnstile Validation - Bot protection validates human on initial page visit
  3. 2FA Code - TOTP code from authenticator app (separate device)
  4. Second Turnstile Validation - Fresh bot protection token validates human during 2FA submission
  1. Registration — New user creates account and verifies email via magic link
  2. Login — Existing user requests magic link to authenticate without password

Both flows use one-time tokens that expire after 15 minutes (login) or 24 hours (registration verification).


Endpoint: POST /auth/register-magic-link

Request:

{
"email": "user@example.com",
"name": "John Doe",
"cf-turnstile-response": "<turnstile-token>"
}

Security Checks:

  • ✅ Turnstile bot protection (required)
  • ✅ IP-based registration rate limit (5 registrations/hour per IP)
  • ✅ Email velocity tracking (3 attempts/hour per email, 5+ IPs = harassment detected)

Step 2: Account Created with Pending Verification

Section titled “Step 2: Account Created with Pending Verification”

If all checks pass:

  • New user record created in backend with authentication_status: "Email Verification Pending"
  • authentication_type: ["Magic Link"] set (user can add password later)
  • _2fa_status: "Disabled" (user can enable 2FA after email verification)
  • No password set (passwordless account)

Response:

{
"success": true,
"recordid": "123456789",
"message": "Account created. Please check your email to verify your account."
}
  • Verification token (32-byte, base64-encoded) generated
  • Token stored in Cloudflare KV with 24-hour expiration
  • Email sent via emailVerificationWebhook (tenant-configured automation)
  • Email contains verification link: https://portal.example.com/email-verified/{recordId}/{token}

Webhook Payload:

{
"event_type": "magic_login",
"recordid": 123456789,
"email": "email@example.com",
"name": "John Doe",
"verificationLink": "https://your.domain.com/email-verified/record_id/verificationToken"
}

Endpoint: POST /auth/verify-email

Request:

{
"recordId": "123456789",
"token": "abc123..."
}

Validation:

  • Token must match Cloudflare KV stored token (one-time use)
  • Token must not be expired (24-hour window)

On Success:

  • authentication_status set to "Unlocked"
  • Token deleted from Cloudflare KV (can’t be reused)
  • User can now log in

Endpoint: POST /auth/request-magic-link

Request:

{
"email": "user@example.com",
"cf-turnstile-response": "<turnstile-token>"
}

Security Checks:

  • ✅ Turnstile bot protection (required)
  • ✅ IP-based rate limit (10 requests/hour per IP)
  • ✅ Email velocity tracking (5+ IPs in 1 hour = harassment detected)
  • ✅ Account status validation:
    • Account must not be Locked
    • Email must be verified (authentication_status != "Email Verification Pending")
ConditionResult
Account not foundGeneric success (email enumeration protection)
Account lockedGeneric success (don’t reveal locked status)
Email unverifiedExplicit error: “Please verify your email first”
All checks passMagic link email sent

Why generic responses? Prevents attackers from discovering which emails are registered.

  • Magic link token (32-byte, base64-encoded) generated
  • Token stored in Cloudflare KV with 15-minute expiration (short window for security)
  • Email sent via magicLoginWebhook (tenant-configured automation)
  • Email contains login link: https://portal.example.com/verify-magic-link/{recordId}/{token}

Webhook Payload:

{
"recordid": "123456789",
"email": "user@example.com",
"name": "John Doe",
"magicLoginLink": "https://portal.example.com/verify-magic-link/123456789/xyz789..."
}

Endpoint: POST /auth/verify-magic-link/{recordId}/{token}

Request:

{
"cf-turnstile-response": "<turnstile-token>",
"twoFACode": "123456" // Optional, only if 2FA enabled
}

Validation:

  • ✅ Turnstile bot protection (required)
  • ✅ Magic link token must match Cloudflare KV (one-time use)
  • ✅ Token must not be expired (15-minute window)
  • ✅ Account must not be locked
  • ✅ If 2FA enabled: require and validate 2FA code

If user has 2FA enabled but didn’t provide code:

Response (200 OK):

{
"success": false,
"requires2FA": true,
"message": "2FA code is required."
}

Frontend re-prompts for 2FA code and calls endpoint again with twoFACode.

  • Magic link token deleted from Cloudflare KV (one-time use)
  • Session created with user’s recordId
  • Session cookie set: session_id={sessionId}; HttpOnly; Secure; SameSite=Lax
  • Session TTL determined by sessionTier (configurable: 15-mins to 30-days)

Response (200 OK):

{
"success": true,
"message": "Login successful!",
"redirect": "/dashboard" // Determined by loginSuccessRedirect config
}

Set-Cookie Header:

session_id=sess_abc123...; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600

  • Tokens are deleted immediately after use (Cloudflare KV.delete)
  • Cannot be reused even within the expiration window
  • If user clicks link twice, second click fails with “Invalid or expired magic link”
  • Size: 32 random bytes (256 bits of entropy)
  • Encoding: Base64 URL-safe (+ replaced with -, / replaced with _, = removed)
  • Example: abc123-_XYZ789 (no +/= characters)
  • Cloudflare KV Key Format:
    • Login: magic_link:{hostname}:{recordId}
    • Registration: email_verify:{hostname}:{recordId}
  • TTL:
    • Magic link (login): 15 minutes
    • Verification token (registration): 24 hours
  • No plaintext in logs — only token existence is logged, never the actual token

LimitScopeWindowBlock Action
5 registrationsSingle IP1 hour429 Too Many Requests
3 attemptsSingle email1 hour429 Too Many Requests
5+ IPsSingle email1 hourLog warning (allowed but flagged)
LimitScopeWindowBlock Action
10 requestsSingle IP1 hour429 Too Many Requests
5+ IPsSingle email1 hour429 Too Many Requests
1 per emailPer email3 minutes429 Too Many Requests
  • IP-based: Prevents single attacker from spamming many emails
  • Email-based: Detects distributed attacks (many IPs hammering one email)
  • Cooldown: Prevents immediate retry of same email

During tenant onboarding, provide:

{
"magicLoginWebhook": "https://your-automation.com/send-magic-link",
"emailVerificationWebhook": "https://your-automation.com/verify-email"
}

These are webhook URLs pointing to your email automation service (Tape automation, Make, Zapier, n8n, etc.).

Your automation should:

  • Accept recordid, email, name, magicLoginLink (login flow)
  • Accept recordid, verificationLink (registration flow)
  • Send branded emails with secure links
  • Never log or display tokens in plaintext
  • Add Turnstile widget to registration & magic link request forms
  • Create /email-verified/{recordId}/{token} page to verify email
  • Create /verify-magic-link/{recordId}/{token} page to verify login
  • Handle requires2FA: true response by prompting for 2FA code

Magic Link generates comprehensive audit logs. See Audit Logs Guide for details.


FeatureMagic LinkPassword
Credentials requiredNone (email only)Password + email
Reset flowEmail verificationPassword reset via email
Phishing riskLow (token expires in 15 min)Medium (password reuse)
UsabilityHigh (click link)Medium (remember password)
2FA capableYes (optional)Yes (optional)
Account recoveryPassword reset availablePassword reset only
Session TTLConfigurable (15 min - 30 days)Configurable (15 min - 30 days)

Attack VectorWithout 2FAWith 2FA
Email compromise❌ Vulnerable✅ Blocked by 2FA
Automated bots✅ Blocked by Turnstile✅ Blocked by dual Turnstile
Phishing/social engineering❌ Vulnerable⚠️ Mitigated (requires TOTP)
Token replay✅ Blocked (single-use)✅ Blocked (single-use)
Brute force✅ Blocked (velocity limits)✅ Blocked (velocity + 2FA)

Most magic link implementations provide only one layer (email token). Mindful Auth provides:

  • 2x security layers (minimum) - Magic link + Turnstile
  • 4x security layers (with 2FA) - Magic link + Turnstile + 2FA + Turnstile

This exceeds the security of traditional password + 2FA systems, while providing better user experience (no password to remember/manage).

IssueCauseSolution
User gets “Invalid or expired link”Token expired after 15 min or already usedRequest new magic link (3-minute cooldown)
“You’ve requested too many links”Rate limit triggeredWait 3 minutes or try different email
”Bot protection validation failed”Turnstile widget not passed or expiredRefresh page and try again
Email never arrivesWebhook not configured or automation failedCheck webhook URL in tenant config
User registers but can’t log inEmail not verified yetClick verification link in email first
”Account has been locked”Admin locked accountContact support to unlock