Sign In

We're the bridge between marketing ambition and financial wisdom. Born from the frustration of seeing e-commerce companies chase growth at any cost, we built a platform that turns complex customer data into simple profit signals.

Ready to turn your marketing budget into pure rocket fuel? 🚀

Drop us a line at hello@headw.ai and let's explore how we can boost your ROI. (We promise the demo will be worth your time!)

© Copyright 2026 Headwai. All Rights Reserved.

    Legal
    • Privacy Policy

    Magic Links

    Passwordless authentication with email magic links.

    Note: This is mock/placeholder content for demonstration purposes.

    Magic links provide passwordless authentication by sending a one-time link to the user's email.

    How It Works

    1. User enters their email address
    2. System sends an email with a unique link
    3. User clicks the link in their email
    4. User is automatically signed in

    Benefits

    • No password to remember - Better UX
    • More secure - No password to steal
    • Lower friction - Faster sign-up process
    • Email verification - Confirms email ownership

    Implementation

    Magic Link Form

    'use client';
    
    import { useForm } from 'react-hook-form';
    import { sendMagicLinkAction } from '../_lib/actions';
    
    export function MagicLinkForm() {
      const { register, handleSubmit, formState: { isSubmitting } } = useForm();
      const [sent, setSent] = useState(false);
    
      const onSubmit = async (data) => {
        const result = await sendMagicLinkAction(data);
    
        if (result.success) {
          setSent(true);
        }
      };
    
      if (sent) {
        return (
          <div className="text-center">
            <h2>Check your email</h2>
            <p>We've sent you a magic link to sign in.</p>
          </div>
        );
      }
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <div>
            <label>Email address</label>
            <input
              type="email"
              {...register('email', { required: true })}
              placeholder="you@example.com"
            />
          </div>
    
          <button type="submit" disabled={isSubmitting}>
            {isSubmitting ? 'Sending...' : 'Send magic link'}
          </button>
        </form>
      );
    }
    

    Server Action

    'use server';
    
    import { enhanceAction } from '@kit/next/actions';
    import { getSupabaseServerClient } from '@kit/supabase/server-client';
    import { z } from 'zod';
    
    export const sendMagicLinkAction = enhanceAction(
      async (data) => {
        const client = getSupabaseServerClient();
        const origin = process.env.NEXT_PUBLIC_SITE_URL!;
    
        const { error } = await client.auth.signInWithOtp({
          email: data.email,
          options: {
            emailRedirectTo: `${origin}/auth/callback`,
            shouldCreateUser: true,
          },
        });
    
        if (error) throw error;
    
        return {
          success: true,
          message: 'Check your email for the magic link',
        };
      },
      {
        schema: z.object({
          email: z.string().email(),
        }),
      }
    );
    

    Configuration

    Enable in Supabase

    1. Go to Authentication → Providers → Email
    2. Enable "Enable Email Provider"
    3. Enable "Enable Email Confirmations"

    Configure Email Template

    Customize the magic link email in Supabase Dashboard:

    1. Go to Authentication → Email Templates
    2. Select "Magic Link"
    3. Customize the template:
    <h2>Sign in to {{ .SiteURL }}</h2>
    <p>Click the link below to sign in:</p>
    <p><a href="{{ .ConfirmationURL }}">Sign in</a></p>
    <p>This link expires in {{ .TokenExpiryHours }} hours.</p>
    

    Callback Handler

    Handle the magic link callback:

    // app/auth/callback/route.ts
    import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
    import { cookies } from 'next/headers';
    import { NextResponse } from 'next/server';
    
    export async function GET(request: Request) {
      const requestUrl = new URL(request.url);
      const token_hash = requestUrl.searchParams.get('token_hash');
      const type = requestUrl.searchParams.get('type');
    
      if (token_hash && type === 'magiclink') {
        const cookieStore = cookies();
        const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
    
        const { error } = await supabase.auth.verifyOtp({
          token_hash,
          type: 'magiclink',
        });
    
        if (!error) {
          return NextResponse.redirect(new URL('/home', request.url));
        }
      }
    
      // Return error if verification failed
      return NextResponse.redirect(
        new URL('/auth/sign-in?error=invalid_link', request.url)
      );
    }
    

    Advanced Features

    Custom Redirect

    Specify where users go after clicking the link:

    await client.auth.signInWithOtp({
      email: data.email,
      options: {
        emailRedirectTo: `${origin}/onboarding`,
      },
    });
    

    Disable Auto Sign-Up

    Require users to sign up first:

    await client.auth.signInWithOtp({
      email: data.email,
      options: {
        shouldCreateUser: false, // Don't create new users
      },
    });
    

    Token Expiry

    Configure link expiration (default: 1 hour):

    -- In Supabase SQL Editor
    ALTER TABLE auth.users
    SET default_token_lifetime = '15 minutes';
    

    Rate Limiting

    Prevent abuse by rate limiting magic link requests:

    import { ratelimit } from '~/lib/rate-limit';
    
    export const sendMagicLinkAction = enhanceAction(
      async (data, user, request) => {
        // Rate limit by IP
        const ip = request.headers.get('x-forwarded-for') || 'unknown';
        const { success } = await ratelimit.limit(ip);
    
        if (!success) {
          throw new Error('Too many requests. Please try again later.');
        }
    
        const client = getSupabaseServerClient();
    
        await client.auth.signInWithOtp({
          email: data.email,
        });
    
        return { success: true };
      },
      { schema: EmailSchema }
    );
    

    Security Considerations

    Link Expiration

    Magic links should expire quickly:

    • Default: 1 hour
    • Recommended: 15-30 minutes for production
    • Shorter for sensitive actions

    One-Time Use

    Links should be invalidated after use:

    // Supabase handles this automatically
    // Each link can only be used once
    

    Email Verification

    Ensure emails are verified:

    const { data: { user } } = await client.auth.getUser();
    
    if (!user.email_confirmed_at) {
      redirect('/verify-email');
    }
    

    User Experience

    Loading State

    Show feedback while sending:

    export function MagicLinkForm() {
      const [status, setStatus] = useState<'idle' | 'sending' | 'sent'>('idle');
    
      const onSubmit = async (data) => {
        setStatus('sending');
        await sendMagicLinkAction(data);
        setStatus('sent');
      };
    
      return (
        <>
          {status === 'idle' && <EmailForm onSubmit={onSubmit} />}
          {status === 'sending' && <SendingMessage />}
          {status === 'sent' && <CheckEmailMessage />}
        </>
      );
    }
    

    Resend Link

    Allow users to request a new link:

    export function ResendMagicLink({ email }: { email: string }) {
      const [canResend, setCanResend] = useState(false);
      const [countdown, setCountdown] = useState(60);
    
      useEffect(() => {
        if (countdown > 0) {
          const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
          return () => clearTimeout(timer);
        } else {
          setCanResend(true);
        }
      }, [countdown]);
    
      const handleResend = async () => {
        await sendMagicLinkAction({ email });
        setCountdown(60);
        setCanResend(false);
      };
    
      return (
        <button onClick={handleResend} disabled={!canResend}>
          {canResend ? 'Resend link' : `Resend in ${countdown}s`}
        </button>
      );
    }
    

    Email Deliverability

    SPF, DKIM, DMARC

    Configure email authentication:

    1. Add SPF record to DNS
    2. Enable DKIM signing
    3. Set up DMARC policy

    Custom Email Domain

    Use your own domain for better deliverability:

    1. Go to Project Settings → Auth
    2. Configure custom SMTP
    3. Verify domain ownership

    Monitor Bounces

    Track email delivery issues:

    // Handle email bounces
    export async function handleEmailBounce(email: string) {
      await client.from('email_bounces').insert({
        email,
        bounced_at: new Date(),
      });
    
      // Notify user via other channel
    }
    

    Testing

    Local Development

    In development, emails go to InBucket:

    http://localhost:54324
    

    Check this URL to see magic link emails during testing.

    Test Mode

    Create a test link without sending email:

    if (process.env.NODE_ENV === 'development') {
      console.log('Magic link URL:', confirmationUrl);
    }
    

    Best Practices

    1. Clear communication - Tell users to check spam
    2. Short expiry - 15-30 minutes for security
    3. Rate limiting - Prevent abuse
    4. Fallback option - Offer password auth as backup
    5. Custom domain - Better deliverability
    6. Monitor delivery - Track bounces and failures
    7. Resend option - Let users request new link
    8. Mobile-friendly - Ensure links work on mobile