auth-helpers: @supabase/ssr auth cookie is not set in OAuth flow in Next.js

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

I have created a brand-new project using npx create-next-app -e with-supabase starter. I verified that the built-in email auth flow works and changed it to use OAuth (in my case for Spotify, but I don’t think that’s important here) by following the docs pretty much to the letter.

The whole OAuth flow goes through as expected and a user is created in the database. However, unlike the email flow the sb-localhost-auth-token cookie is not set in the browser.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Create a new Next.js with Supabase project using npx create-next-app -e with-supabase
  2. Configure Supabase auth for an OAuth provider
  3. Replace the login page with just a sign-in button
  4. Replace sign-in route (/app/auth/sign-in/route.ts) with an OAuth flow:
import { createClient } from '@/utils/supabase/server'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

export async function POST(request: Request) {
  const requestUrl = new URL(request.url)
  const cookieStore = cookies()
  const supabase = createClient(cookieStore)

  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'spotify',
    options: {
      scopes: 'user-read-email',
      redirectTo: `${requestUrl.origin}/auth/callback`,
    },
  })

  return NextResponse.redirect(data.url ?? requestUrl.origin, {
    // a 301 status is required to redirect from a POST to a GET route
    status: 301,
  })
}
  1. Replace callback route handler (app/auth/callback/route.ts) with an OAuth version:
import { createClient } from "@/utils/supabase/server";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/empty";

  if (code) {
    const cookieStore = cookies();

    const supabase = createClient(cookieStore);
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(new URL(`/${next.slice(1)}`, request.url));
    }
  }

  // return the user to an error page with instructions
  return NextResponse.redirect(
    new URL("/auth/auth-code-error", request.url)
  );
}
  1. See that sb-localhost-auth-token cookie is not set.

Expected behavior

Authentication cookie should be set.

System information

  • OS: Windows 11
  • Browser (if applies) Chrome
  • Version of supabase-js: 2.38.4
  • Version of supabase/ssr: 0.0.4
  • Version of Next.js: 14.0.0 (also tried with the latest 13.x with same result)
  • Version of Node.js: 18.18.2

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 5
  • Comments: 27 (5 by maintainers)

Most upvoted comments

I’m still having this issue on supabase/ssr 0.1.0.

I’ve followed the instructions to setup server-side auth for Next.js detailed here: https://supabase.com/docs/guides/auth/server-side/nextjs

In the auth/confirm route I can get the user (or the session) no problem:

import { type NextRequest, NextResponse } from 'next/server';

import { createClient } from '@/utils/supabase/server';
import { type EmailOtpType } from '@supabase/supabase-js';

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;
  redirectTo.searchParams.delete('token_hash');
  redirectTo.searchParams.delete('type');

  if (token_hash && type) {
    const supabase = createClient();

    const { error } = await supabase.auth.verifyOtp({
      type,
      token_hash,
    });

    if (!error) {
      const {
        data: { user },
      } = await supabase.auth.getUser();

      console.log('USER: ', user);

      redirectTo.searchParams.delete('next');
      return NextResponse.redirect(redirectTo);
    }
  }

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

But I never see the cookie for the session show up in the inspector and then unsurprisingly, if I try to get the user or session in the middleware, or on a SSR page, doing things exactly as is shown in the docs I linked above, I always get null for the user or session and this error:

AuthApiError: invalid claim: missing sub claim

I assume this is because the cookie is never actually set.

Strangely, I do see the sb-localhost-auth-token cookie get set if I do a supabase db reset, supabase stop, and supabase start and then request a Magic Link. That first time this results in my having to confirm my email. When I follow the link to confirm my email, I can see the correct cookie in the inspector. As soon as I then make a second request for a Magic Link, resulting in the normal Magic Link email and link, when I follow that, the sb-localhost-auth-token gets removed.

This is likely due to the cookie size being larger than what you are allowed to save in the browser. We just released version 0.0.5 of the ssr package which has some cookie chunking methods in it but it will take a few days for us to get some guides out on how to use these. And this works in the auth-helpers because we shipped a version a few days ago that handles cookie chunking for you in it’s code.

@silentworks works like charm for me in with 0.0.7. Thanks!

This is likely due to the cookie size being larger than what you are allowed to save in the browser. We just released version 0.0.5 of the ssr package which has some cookie chunking methods in it but it will take a few days for us to get some guides out on how to use these. And this works in the auth-helpers because we shipped a version a few days ago that handles cookie chunking for you in it’s code.

Is there any code we can look at to fix this in the meantime?