supabase / auth-helpers

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

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

Open rsimon opened 4 months ago

rsimon commented 4 months 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 4 months 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 4 months 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 2 months 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.

SkycladObserver commented 2 months ago

Hello, we are experiencing this issue as well. This only started occurring when we refresh the session. From experience, it seems having multiple sessions from different devices increases the frequency, but it's still not consistent.

gurkiranksingh commented 1 month ago

I'm also experiencing this issue on logging out the user - also intermittent.

JUDE-EQ commented 3 weeks ago

having the same issue with our app. supabase.auth.signOut() triggers the same error. any updates?

finallyblueskies commented 3 weeks ago

Experiencing the same issue on supabase.auth.signOut

JeffK-IM commented 3 weeks ago

Same issue here!

seanc commented 2 days ago

I'm encountering this as well with Deno backend using the https://esm.sh/@supabase/supabase-js@2.38.4?pin=v135 package. I have middleware that verifies whether the token is valid and it passes but when trying to use the token with client using the following:

  const authHeader = ctx.request.headers.get("Authorization")!;
  const userSupabaseClient = getSupabaseClient(authHeader);

  const { data: user } = await userSupabaseClient.auth.getUser();

I get this error:

  [api-prod] [2024-11-25 03:05:24] Error occurred: AuthApiError: Session from session_id claim in JWT does not exist
[api-prod] [2024-11-25 03:05:24]     at le (https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:5284)
[api-prod] [2024-11-25 03:05:24]     at eventLoopTick (ext:core/01_core.js:175:7)
[api-prod] [2024-11-25 03:05:24]     at async Ie (https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:6071)
[api-prod] [2024-11-25 03:05:24]     at async h (https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:5808)
[api-prod] [2024-11-25 03:05:24]     at async https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:26221
[api-prod] [2024-11-25 03:05:24]     at async g._useSession (https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:25010)
[api-prod] [2024-11-25 03:05:24]     at async g._getUser (https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:26138)
[api-prod] [2024-11-25 03:05:24]     at async https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:26001
[api-prod] [2024-11-25 03:05:24]     at async https://esm.sh/v135/@supabase/gotrue-js@2.57.0/denonext/gotrue-js.mjs:2:24296 {
[api-prod] [2024-11-25 03:05:24]   __isAuthError: true,
[api-prod] [2024-11-25 03:05:24]   name: "AuthApiError",
[api-prod] [2024-11-25 03:05:24]   status: 403
[api-prod] [2024-11-25 03:05:24] }