supabase icon indicating copy to clipboard operation
supabase copied to clipboard

"supabase.auth.getUser()" is returning a null user value in nextjs middleware.ts using @supabase/supabase-js @supabase/ssr only

Open kritik-sah opened this issue 1 year ago • 10 comments

Bug report

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

Describe the bug

 const { data, error } = await supabase.auth.getUser();
  console.log("user : ", error); //getting an error 

error, even if I followed everything in doc. https://supabase.com/docs/guides/auth/server-side/nextjs

[AuthSessionMissingError: Auth session missing!] {
  __isAuthError: true,
  name: 'AuthSessionMissingError',
  status: 400,
  code: undefined
}

my observation:

const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          console.log("name ==>", name);
          console.log("nameValue ==>", request.cookies.get(name)?.value);
          return request.cookies.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value,
            ...options,
          });
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          });
          response.cookies.set({
            name,
            value,
            ...options,
          });
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value: "",
            ...options,
          });
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          });
          response.cookies.set({
            name,
            value: "",
            ...options,
          });
        },
      },
    }
  );

logs:

name ==> sb-yelbdeiefeqnignhtxkc-auth-token
nameValue ==> undefined
name ==> sb-yelbdeiefeqnignhtxkc-auth-token.0
nameValue ==> undefined

real name of cookie is something different Screenshot 2024-05-05 132915

I am using Google and github as provider. and this is what my code look like full repo an opensource portfolio & resume builder

src/middleware.ts

import { updateSession } from "@/utils/supabase/middleware";
import { type NextRequest } from "next/server";

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

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|p|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
};

src\utils\supabase\client.ts

import { createBrowserClient } from "@supabase/ssr";

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

src\utils\supabase\server.ts

"use server";
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";

export default async function createClient() {
  const cookieStore = cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options });
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: "", ...options });
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  );
}

src\utils\supabase\middleware.ts

import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";

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

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          console.log("name ==>", name);
          console.log("nameValue ==>", request.cookies.get(name)?.value);
          return request.cookies.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value,
            ...options,
          });
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          });
          response.cookies.set({
            name,
            value,
            ...options,
          });
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({
            name,
            value: "",
            ...options,
          });
          response = NextResponse.next({
            request: {
              headers: request.headers,
            },
          });
          response.cookies.set({
            name,
            value: "",
            ...options,
          });
        },
      },
    }
  );

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

  // refreshing the auth token
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (
    !user &&
    request.nextUrl.pathname !== "/signin" &&
    request.nextUrl.pathname !== "/"
  ) {
    return NextResponse.redirect(new URL("/signin", request.url));
  }
  return NextResponse.next({
    request: {
      headers: request.headers,
    },
  });
}

src\app\auth\callback\route.ts

import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  // if "next" is in param, use it as the redirect URL
  const next = searchParams.get("next") ?? "/";

  if (code) {
    const cookieStore = cookies();
    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          get(name: string) {
            return cookieStore.get(name)?.value;
          },
          set(name: string, value: string, options: CookieOptions) {
            cookieStore.set({ name, value, ...options });
          },
          remove(name: string, options: CookieOptions) {
            cookieStore.delete({ name, ...options });
          },
        },
      }
    );
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) {
      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  // return the user to an error page with instructions
  return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}

onClick function signinWithSocial("google" | "github")

async function signinWithSocial(provider: Provider) {
    const supabase = await createClient();
    const { data, error } = await supabase.auth.signInWithOAuth({
      provider: provider,
      options: {
        redirectTo: `${location.origin}/auth/callback?next=${location.origin}/app`,
      },
    });
    if (error) {
      console.error(error);
    }
    console.log(data);
  }

To Reproduce

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

  1. Go to https://supabase.com/docs/guides/auth/server-side/nextjs follow the guide
  2. use google social login
  3. check the user value in middleware its null
  4. See error

Expected behavior

in the middleware i should get the user value, by which i can check and redirect someone if they are not loged in

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: [ Windows]
  • Browser (if applies) [brave]
  • Version of supabase-js:
  • "@supabase/ssr": "^0.3.0", "@supabase/supabase-js": "^2.43.1",
  • Version of Node.js: [v20.11.0]

Additional context

I have setuped oauth provider in supabase dashboard successfully, so that wont be any problem

kritik-sah avatar May 05 '24 08:05 kritik-sah

Hi! Is there a specific reason why calling auth.getUser() in your protected route to see if they are logged in and then kicking them out is not an option? It seems like the guide you linked does exactly that in the last step. afaik there won't be a user before a code exchange is done, which looks like that is the case in the attached picture because I only see the code challenge.

simonha9 avatar May 06 '24 15:05 simonha9

We're also experiencing a similar problem. On our server, we're having the following check:

    const authenticatedUser = await this.supabaseAdminClient.auth.getUser(jwtToken)
    if (!authenticatedUser.data.user) {
      this.logger.debug('No user is associated with the access token!')
      return false
    }

However, sometimes, even though we are using a valid jwt, await this.supabaseAdminClient.auth.getUser(jwtToken) is returning null.

"@supabase/supabase-js": "^2.39.8"

d-e-h-i-o avatar May 13 '24 16:05 d-e-h-i-o

Think this is the same issue I reported here: https://github.com/supabase/ssr/issues/36

Logging in twice gets it to set - somoeone suggested the set method is only setting one cookie at a time...

aaa3334 avatar May 20 '24 00:05 aaa3334

Following this, @supabase-automated-user pls look into this

abhimanyu891998 avatar Jun 23 '24 14:06 abhimanyu891998

somoeone suggested the set method is only setting one cookie at a time...

This could very well be the case. We're about to release 0.4.0 of @supabase/ssr in the next 24 hours. Please check this discussion: https://github.com/orgs/supabase/discussions/27037 and upgrade as soon as able to use getAll() and setAll(). Docs are here: https://github.com/supabase/supabase/pull/27242

hf avatar Jun 23 '24 15:06 hf

still have an error event update to latest version of @supabase/ssr.

I did a quick fix that solved the error. In the process of sign-in, I save a cookie reference for refresh and access tokens (sb-access-token, sb-refresh-token)

const cookieStore = cookies();

const { data, error } = await repository.auth.signInWithPassword({
    email,
    password,
  });
  
  cookieStore.set('sb-access-token', data?.session?.access_token);
  cookieStore.set('sb-refresh-token', data?.session?.refresh_token);

When I try to capture user information in my API routes I've an auxiliary function:

retrieveAuthOrSessionUser = async () => {
    let userFinal = null;
    const cookieStore = cookies();
    const accessToken = cookieStore.get('sb-access-token');
    const refreshToken = cookieStore.get('sb-refresh-token');

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

    if (error) {
      const { data: dataSession } = await this.supabase.auth.setSession({
        access_token: accessToken?.value ?? '',
        refresh_token: refreshToken?.value ?? '',
      });
      userFinal = dataSession?.user;
    } else {
      userFinal = user;
    }

    const { id } = userFinal ?? {};

    return {
      id: id ?? '',
    };
  };

Remember to erase the cookies on the process of sign-out

  const supabaseCookies = cookies().getAll();

  supabaseCookies.map((cookie) => {
    if (cookie.name.includes('sb-')) {
      cookies().delete(cookie.name);
    }
  });

isn't the best scenario or code, but does the job. Hope this Helps.

fernando-plank avatar Jul 02 '24 14:07 fernando-plank

Same issue, any solutions? Next.js 14.2.5

eposha avatar Jul 19 '24 03:07 eposha

In my case

removing export const dynamic = 'force-static' from layout resolve issue

eposha avatar Jul 19 '24 07:07 eposha

Hi all - just checking in here, version 0.5.0 @supabase/ssr was released last week. Are you still experiencing the same issue?

Hallidayo avatar Aug 25 '24 19:08 Hallidayo

@Hallidayo Yes I'm still experiencing the issue, nextjs version 14.2.6

AdzeB avatar Sep 16 '24 18:09 AdzeB

Hi, just want to report here I am experiencing the same issue.

Currently on ssr 0.5.1 and nextjs version 14.2.14

themmyloluwaa avatar Oct 06 '24 11:10 themmyloluwaa

Experiencing this issue as well - ssr 0.5.1, next 14.2.1

agomesd avatar Oct 08 '24 02:10 agomesd

Same issue here, ssr 0.5.1, next 14.2.14

nip10 avatar Oct 26 '24 15:10 nip10