Home

OAuth with PKCE flow for SSR

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

Install Supabase Auth Helpers#

The Auth Helpers assist with user authentication within server-side rendering (SSR) frameworks.


_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#

For SSR, the Supabase client requires 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 the code exchange#

In order to use OAuth we will need to setup a endpoint for the code exchange, to exchange an auth code for the user's session, which is set as a cookie for future requests made to Supabase.

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

app/auth/callback/route.js

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

Let's point our .signInWithOAuth method's redirect to the callback route we create above:


_10
await supabase.auth.signInWithOAuth({
_10
provider,
_10
options: {
_10
redirectTo: `http://example.com/auth/callback`,
_10
},
_10
})