supabase / auth-helpers

A collection of framework specific Auth utilities for working with Supabase.
https://supabase.github.io/auth-helpers/
MIT License
901 stars 237 forks source link

auth-helpers-shared: "Session from session_id claim in JWT does not exist" error #804

Open rsimon opened 1 month ago

rsimon commented 1 month ago

Bug report

Describe the bug

We've been getting the above error message ("AuthApiError: Session from session_id claim in JWT does not exist") when accessing supabase.auth.getUser() on the server (SSR) right after successful log-in, after switching from @supabase/auth-helpers-shared@0.3.4 to @0.7.0.

Weirdly enough, the problem doesn't always occur, almost as if a timing issue were involved somewhere. But certainly in the majority of attempts.

To Reproduce

We use the following code on the server to create the supabase client. This code is run every time a user visits a protected route.

import { createSupabaseClient, type SupabaseClientOptionsWithoutAuth } from '@supabase/auth-helpers-shared';

export async function initClient(
  cookies: AstroCookies,
  {
    supabaseUrl,
    supabaseKey,
    options
  }: {
    supabaseUrl: string;
    supabaseKey: string;
    options?: SupabaseClientOptionsWithoutAuth;
  }
) {
  const client = createSupabaseClient(
    supabaseUrl,
    supabaseKey,
    {
      ...options,
      global: {
        ...options?.global,
        headers: {
          ...options?.global?.headers
        }
      },
      auth: {
        storage: {
          getItem: (name: string) => cookies.get(name)?.value,

          setItem: (name: string, value: string) => cookies.set(name, value),

          removeItem: (name: string) => cookies.delete(name)
        }
      }
    }
  );

  await refreshSession(client, cookies);
  return client;
}

const refreshSession = async (supabase: SupabaseClient, cookies: AstroCookies) => {  
  const { data: { session }} = await supabase.auth.getSession();
  if (session) return true;

  const refreshToken = cookies.get('sb-refresh-token');
  const accessToken = cookies.get('sb-access-token');

  if (refreshToken?.value && accessToken?.value) {
    return await supabase.auth.setSession({ 
      refresh_token: refreshToken.value, 
      access_token: accessToken.value 
    }).then(({ error }) => {
      return !error
    })
}

Aditionally, here's our code for retrieving the user object. Likewise, this gets called each time the user visits a protected route, immediately after creating our client.

export const getUser = (supabase: SupabaseClient): Promise<User> =>
  supabase.auth.getUser().then(({ error, data }) => {
    if (error) {
      throw error;
    } else if (data.user === null) {
      throw 'Unauthorized';
    } else if (data.user.role !== 'authenticated') {
      throw 'Unauthorized';
    } else {
      return data.user;
    }
  });

Expected behavior

We can confirm that every time the user hits a protected route...

System information

Additional context

andrew-app commented 1 month ago

I am also experiencing this issue in Next.js 14 with @supabase/ssr@0.4.0. I am calling supabase.auth.getUser() in middleware as mentioned in docs. This bug occurs in the following:

The docs suggest using server actions to do login I will try that see if i get the same error.

https://github.com/user-attachments/assets/56cf451a-eb87-40dd-9e96-4d63e6c8146c

My middleware:

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

  const supabase = supabaseServerClient(
    {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
          supabaseResponse = NextResponse.next({
            request,
          })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
        },
    });

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

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

  if (user && url.pathname.startsWith('/register') || user && url.pathname === '/') {
    url.pathname = '/bookings';
    return NextResponse.redirect(url);
  }

  if (error) {
    console.error('Supabase error:', error);
  }

  return supabaseResponse;
}

My supabase server client:

import { CookieMethodsServer, createServerClient } from '@supabase/ssr'

export default (cookieConfig: CookieMethodsServer) => {

  return (createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: cookieConfig
    }
  ));
};
gary-lo commented 1 month ago

Very similar where it's intermittent.

image

What we also experimented is - we've taken a copy of the network request as a CURL. We replay it some time after with the same JWT and it'll work.

ygill-impel commented 1 day ago

Hi We are also having the issue with this when we send jwt from Remix app to the backend FastAPI server to validate request function.