Auth

Email Auth with PKCE flow for SSR

Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.

Setting up SSR client

Check out our guide for creating a client to learn how to install the necessary packages, declare environment variables, and create a Supabase client configured for SSR in your framework.

Create API endpoint for handling token_hash

In order to use the updated email links we will need to setup a endpoint for verifying the token_hash along with the type to exchange token_hash for the user's session, which is set as a cookie for future requests made to Supabase.

Create a new file at app/auth/confirm/route.ts and populate with the following:

app/auth/confirm/route.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { type EmailOtpType } from '@supabase/supabase-js'
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const token_hash = searchParams.get('token_hash')
const type = searchParams.get('type') as EmailOtpType | null
const next = searchParams.get('next') ?? '/'
const redirectTo = request.nextUrl.clone()
redirectTo.pathname = next

if (token_hash && type) {
const cookieStore = cookies()
const supabase = 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) {
cookieStore.set({ name, value, ...options })
},
remove(name: string, options: CookieOptions) {
cookieStore.delete({ name, ...options })
},
},
}
)

const { error } = await supabase.auth.verifyOtp({
type,
token_hash,
})
if (!error) {
return NextResponse.redirect(redirectTo)
}
}

// return the user to an error page with some instructions
redirectTo.pathname = '/auth/auth-code-error'
return NextResponse.redirect(redirectTo)
}

Update email templates with URL for API endpoint

Let's update the URL in our email templates to point to our new confirmation endpoint for the user to get confirmed.

Confirm signup template

<h2>Confirm your signup</h2>

<p>Follow this link to confirm your user:</p>
<p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
>Confirm your email</a
>
</p>

Invite user template

<h2>You have been invited</h2>

<p>
You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:
</p>

<p>
<a
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=invite&next=/path-to-your-update-password-page"
>Accept the invite</a
>
</p>

Magic Link template

<h2>Magic Link</h2>

<p>Follow this link to login:</p>
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink">Log In</a></p>

Change Email Address template

<h2>Confirm Change of Email</h2>

<p>Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}:</p>
<p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email_change">
Change Email
</a>
</p>

Reset Password template

<h2>Reset Password</h2>

<p>Follow this link to reset the password for your user:</p>
<p>
<a
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=recovery&next=/path-to-your-update-password-page"
>Reset Password</a
>
</p>