Home

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

_46
import { createServerClient, type CookieOptions } from '@supabase/ssr'
_46
import { type EmailOtpType } from '@supabase/supabase-js'
_46
import { cookies } from 'next/headers'
_46
import { NextRequest, NextResponse } from 'next/server'
_46
_46
export async function GET(request: NextRequest) {
_46
const { searchParams } = new URL(request.url)
_46
const token_hash = searchParams.get('token_hash')
_46
const type = searchParams.get('type') as EmailOtpType | null
_46
const next = searchParams.get('next') ?? '/'
_46
const redirectTo = request.nextUrl.clone()
_46
redirectTo.pathname = next
_46
_46
if (token_hash && type) {
_46
const cookieStore = cookies()
_46
const supabase = createServerClient(
_46
process.env.NEXT_PUBLIC_SUPABASE_URL!,
_46
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
_46
{
_46
cookies: {
_46
get(name: string) {
_46
return cookieStore.get(name)?.value
_46
},
_46
set(name: string, value: string, options: CookieOptions) {
_46
cookieStore.set({ name, value, ...options })
_46
},
_46
remove(name: string, options: CookieOptions) {
_46
cookieStore.delete({ name, ...options })
_46
},
_46
},
_46
}
_46
)
_46
_46
const { error } = await supabase.auth.verifyOtp({
_46
type,
_46
token_hash,
_46
})
_46
if (!error) {
_46
return NextResponse.redirect(redirectTo)
_46
}
_46
}
_46
_46
// return the user to an error page with some instructions
_46
redirectTo.pathname = '/auth/auth-code-error'
_46
return NextResponse.redirect(redirectTo)
_46
}

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


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

Invite user template


_12
<h2>You have been invited</h2>
_12
_12
<p>
_12
You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:
_12
</p>
_12
_12
<p>
_12
<a
_12
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=invite&next=/path-to-your-update-password-page"
_12
>Accept the invite</a
_12
>
_12
</p>

Magic Link template


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

Change Email Address template


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

Reset Password template


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