Skip to main content

Source: ocean/docs/fix-otp-magic-link-issue.md | ✏️ Edit on GitHub

Fix: OTP vs Magic Link Issue

Problem

The application expects users to receive a 6-digit OTP code via email, but Supabase is sending magic links instead. When users click the magic link, they get redirected with an error: #error=access_denied&error_code=otp_expired&error_description=Email+link+is+invalid+or+has+expired

Root Cause

  1. Supabase email templates haven't been configured to send OTP codes
  2. The default templates send magic links using {{ .ConfirmationURL }}
  3. The redirect URL in the magic link may not be properly configured

Solution

Follow the instructions in /docs/SETUP_OTP.md to update all email templates in Supabase dashboard to use {{ .Token }} instead of {{ .ConfirmationURL }}.

Option 2: Update Auth Service to Specify emailRedirectTo

If you want to keep using magic links, update the auth service to include the proper redirect URL:

// src/services/auth.ts
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
shouldCreateUser: true,
data: metadata,
emailRedirectTo: `${window.location.origin}/auth/callback`,
},
})

The app already has an auth callback route at /auth/callback that should handle the magic link. The issue might be:

  1. The hash fragment (#error=...) is being handled incorrectly
  2. The token might have expired (default is 5 minutes)

Immediate Workaround

For testing, you can:

  1. Use the Supabase dashboard to manually create users
  2. Use the local Inbucket email service if running Supabase locally
  3. Configure the email templates as described in SETUP_OTP.md

Long-term Fix

The application is designed for OTP flow, so configuring Supabase to send OTP codes is the proper solution.