Supabase Magic Link not working in Next.js 14

76 views Asked by At

Magic link works when sent from Supabase dashboard, but not when sent from Next.js. The return url includes code in the searchParams instead of the intended token.

Code

// middleware.ts

import { NextRequest } from 'next/server'
import { updateSession } from './supabase/middleware'

export async function middleware(request: NextRequest) {
  return await updateSession(request)
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}
// supabase/middleware.ts

import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function updateSession(request: NextRequest) {
  let response = NextResponse.next({
    request: {
      headers: request.headers,
    },
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value,
            ...options,
          })
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          })
          response.cookies.set({
            name,
            value,
            ...options,
          })
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value: '',
            ...options,
          })
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          })
          response.cookies.set({
            name,
            value: '',
            ...options,
          })
        },
      },
    },
  )

  await supabase.auth.getUser()

  return response
}
// supabase/server.ts

import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export function createClient() {
  const cookieStore = cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    },
  )
}
// lib/supabase.ts

'use server'

import { createClient } from '@/supabase/server'

export async function signIn(data: FormData) {
  const supabase = createClient()

  const email = data.get('Username') as string

  if (!email) throw new Error('No email provided.')

  try {
    await supabase.auth.signInWithOtp({
      email,
      options: {
        shouldCreateUser: false,
        emailRedirectTo: `${process.env.NEXT_PUBLIC_BASE_URL}/account`,
      },
    })
  } catch (error) {
    throw new Error(`${error}`)
  }
}
// app/log-in/page.tsx

import { signIn } from '@/lib/supabase'

export default function LogIn() {
  return (
    <main>
      <section>
        <form action={signIn}>
          <input type='email' name='Username' />

          <button type='submit'>Log In</button>
        </form>
      </section>
    </main>
  )
}

Magic Link Email Template

<a href="{{ .ConfirmationURL }}">Log In</a>

I've followed every tutorial and documentation on this, but no one ever mentions anything about {url}?code={code}. I don't even get that when I use the "Send Magic Link" button in the Supabase dashboard.

0

There are 0 answers