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.

Install Supabase Auth Helpers#

The Auth Helpers will assist you in implementing user authentication within your server-side rendering (SSR) framework.


_10
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js

Set environment variables#

Create an .env.local file in your project root directory. You can get your SITE_URL and ANON_KEY from inside of the dashboard.

.env.local

_10
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
_10
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

Setting up the Auth Helpers#

When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.

Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.

Next.js Middleware runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.

Create a new middleware.js file in the root of your project and populate with the following:

middleware.js

_10
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
_10
import { NextResponse } from 'next/server'
_10
_10
export async function middleware(req) {
_10
const res = NextResponse.next()
_10
const supabase = createMiddlewareClient({ req, res })
_10
await supabase.auth.getSession()
_10
return res
_10
}

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.js and populate with the following:

app/auth/confirm/route.js

_22
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
_22
import { cookies } from 'next/headers'
_22
import { NextResponse } from 'next/server'
_22
_22
export async function GET(req) {
_22
const { searchParams } = new URL(req.url)
_22
const token_hash = searchParams.get('token_hash')
_22
const type = searchParams.get('type')
_22
const next = searchParams.get('next') ?? '/'
_22
_22
if (token_hash && type) {
_22
const cookieStore = cookies()
_22
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
_22
const { error } = await supabase.auth.verifyOtp({ type, token_hash })
_22
if (!error) {
_22
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
_22
}
_22
}
_22
_22
// return the user to an error page with some instructions
_22
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
_22
}

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><a href="{{ .ConfirmationURL }}">Change Email</a></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>