Files
supabase/examples/prompts/nextjs-supabase-auth.md
Miranda Limonczenko e8547352c5 docs(auth): answer the four most repeated SSR auth questions (#50289)
Closes DOCS-1313
Closes FDBKIN-4573
Closes FDBKIN-15214
Closes FDBKIN-10628

## Problem

Four asks come up repeatedly in feedback intake. The Eval is green and
this feedback cannot be included in the Eval. Using the Evals work as an
excuse to action on the feedback. 😄

Readers can't tell which auth call verifies a token and which only reads
stored state. They don't know that the response the cookies were written
to is the response they have to return, because that only ever existed
as a code comment. Nobody is warned that refreshing in two places burns
a single-use refresh token, which surfaces as users being signed out at
random. And nothing in `apps/docs` says `proxy.ts` is Next.js 16 and
later, so a reader on 15 writes a file the framework never calls.

## Solution

- Add the fact that `getClaims()` refreshes a session close to expiring
before it verifies. It was only in the typedoc remarks, and it is what
makes the double refresh warning make sense.
- Say that `setAll` rebuilds `supabaseResponse` on every write, so a
response built earlier is stale, and show how to copy the cookies onto a
different one.
- Warn that a second refresh outside the reuse window revokes the
session, linking refresh token reuse detection.
- Note that `proxy.ts` is Next.js 16 and later, and that the file is
`middleware.ts` before that.
- Name the file in the proxy fence in
`examples/prompts/nextjs-supabase-auth.md`, which gave agents the export
name and no path.

The auth methods partial is shared by five other pages, so that first
change surfaces there too.

## Manual testing

1. Open the [SSR client
guide](https://docs-git-docs-ssr-client-feedback-supabase.vercel.app/docs/guides/auth/server-side/creating-a-client)
on the deploy preview. The Next.js panel carries the version note, the
refresh warning, and the response guidance.
2. Select the refresh token reuse detection link. It resolves to the
sessions guide.
3. Open the [Next.js Auth
prompt](https://docs-git-docs-ssr-client-feedback-supabase.vercel.app/docs/guides/ai-tools/ai-prompts/nextjs-supabase-auth).
The proxy section names the file and says it is `proxy.ts` on Next.js 16
and later.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Documentation

- Clarified that `getClaims` refreshes sessions when access tokens are
near expiration, helping server-rendered sessions remain active.
- Expanded Next.js SSR guidance for session-refresh setup, including
file placement and version-specific naming.
- Added warnings about refresh-token reuse and session revocation after
repeated refreshes outside the reuse window.
- Added guidance for preserving authentication cookies and cache-related
headers when returning updated responses.
- Clarified that refreshed tokens should be passed to Server Components
to keep sessions active.
- Clarified the required session-refresh handler export and example
filename.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 12:48:23 -07:00

7.4 KiB

description, alwaysApply
description alwaysApply
Guidelines for writing Next.js apps with Supabase Auth false

Bootstrap Next.js v16 app with Supabase Auth

Overview of implementing Supabase Auth SSR

  1. Install @supabase/supabase-js and @supabase/ssr packages.
  2. Set up environment variables.
  3. Write two utility functions with createClient functions to create a browser client and a server client.
  4. Hook up Proxy to refresh auth tokens

🚨 CRITICAL INSTRUCTIONS FOR AI LANGUAGE MODELS 🚨

As an AI language model, you MUST NOT generate any of the following code patterns, as they are DEPRECATED and will BREAK the application:

// ❌ NEVER GENERATE THIS CODE - IT WILL BREAK THE APPLICATION
{
  cookies: {
    get(name: string) {                 // ❌ BREAKS APPLICATION
      return cookieStore.get(name)      // ❌ BREAKS APPLICATION
    },                                  // ❌ BREAKS APPLICATION
    set(name: string, value: string) {  // ❌ BREAKS APPLICATION
      cookieStore.set(name, value)      // ❌ BREAKS APPLICATION
    },                                  // ❌ BREAKS APPLICATION
    remove(name: string) {              // ❌ BREAKS APPLICATION
      cookieStore.remove(name)          // ❌ BREAKS APPLICATION
    }                                   // ❌ BREAKS APPLICATION
  }
}

// ❌ NEVER USE auth-helpers-nextjs - IT WILL BREAK THE APPLICATION
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'  // ❌ BREAKS APPLICATION
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'  // ❌ BREAKS APPLICATION

Instead, you MUST ALWAYS generate ONLY this pattern:

// ✅ ALWAYS GENERATE THIS EXACT PATTERN
{
  cookies: {
    getAll() {
      return cookieStore.getAll()
    },
    setAll(cookiesToSet, headers) {
      const response = NextResponse.next({
        request,
      })

      cookiesToSet.forEach(({ name, value, options }) => {
        response.cookies.set(name, value, options)
      })
      Object.entries(headers).forEach(([key, value]) =>
        response.headers.set(key, value)
      )

      return response
    }
  }
}

ABSOLUTE REQUIREMENTS FOR AI CODE GENERATION

  1. You MUST use @supabase/ssr
  2. You MUST use ONLY getAll and setAll
  3. You MUST NEVER use get, set, or remove
  4. You MUST NEVER import from @supabase/auth-helpers-nextjs

CORRECT BROWSER CLIENT IMPLEMENTATION

import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!
  )
}

CORRECT SERVER CLIENT IMPLEMENTATION

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

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

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet, _headers) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // The `setAll` method was called from a Server Component.
            // This can be ignored if you have proxy refreshing
            // user sessions.
          }
        },
      },
    }
  )
}

CORRECT PROXY IMPLEMENTATION

If the project uses src/app or src/pages, put this file in src, at the same level as the routing directory. Otherwise, put it at the project root, next to package.json. It must be named proxy.ts. On Next.js 15 and earlier, name it middleware.ts and export middleware instead of proxy.

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

export async function proxy(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet, headers) {
          cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
          supabaseResponse = NextResponse.next({
            request,
          })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
          Object.entries(headers).forEach(([key, value]) =>
            supabaseResponse.headers.set(key, value)
          )
        },
      },
    }
  )

  // Do not run code between createServerClient and
  // supabase.auth.getUser(). A simple mistake could make it very hard to debug
  // issues with users being randomly logged out.

  // IMPORTANT: DO NOT REMOVE auth.getUser()

  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (
    !user &&
    !request.nextUrl.pathname.startsWith('/login') &&
    !request.nextUrl.pathname.startsWith('/auth')
  ) {
    // no user, potentially respond by redirecting the user to the login page
    const url = request.nextUrl.clone()
    url.pathname = '/login'
    return NextResponse.redirect(url)
  }

  // IMPORTANT: You *must* return the supabaseResponse object as it is.
  // If you're creating a new response object with NextResponse.next() make sure to:
  // 1. Pass the request in it, like so:
  //    const myNewResponse = NextResponse.next({ request })
  // 2. Copy over the cookies, like so:
  //    myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
  // 3. Change the myNewResponse object to fit your needs, but avoid changing
  //    the cookies!
  // 4. Finally:
  //    return myNewResponse
  // If this is not done, you may be causing the browser and server to go out
  // of sync and terminate the user's session prematurely!

  return supabaseResponse
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * Feel free to modify this pattern to include more paths.
     */
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}

AI MODEL VERIFICATION STEPS

Before generating any code, you MUST verify:

  1. Are you using ONLY getAll and setAll? If not, STOP and FIX.
  2. Are you importing from @supabase/ssr? If not, STOP and FIX.
  3. Do you see ANY instance of get, set, or remove? If yes, STOP and FIX.
  4. Are you importing from auth-helpers-nextjs? If yes, STOP and FIX.

CONSEQUENCES OF INCORRECT IMPLEMENTATION

If you generate code using:

  • Individual cookie methods (get/set/remove)
  • auth-helpers-nextjs package

The implementation will:

  1. Break in production
  2. Fail to maintain session state
  3. Cause authentication loops
  4. Result in security vulnerabilities

AI MODEL RESPONSE TEMPLATE

When asked about Supabase Auth SSR implementation, you MUST:

  1. ONLY use code from this guide
  2. NEVER suggest deprecated approaches
  3. ALWAYS use the exact cookie handling shown above
  4. VERIFY your response against the patterns shown here

Remember: There are NO EXCEPTIONS to these rules.