supabase / ssr

Supabase clients for use in server-side rendering frameworks.
MIT License
71 stars 7 forks source link

Unable to Create Client with Service Role Key #59

Closed mariojfduarte closed 2 months ago

mariojfduarte commented 2 months ago

Description

After recent changes in the createServerClient.ts file, it seems that creating a client with a service role key is no longer possible. Previously, my code allowed this by setting the admin flag to true so that Service Role Key was used, but now, even with the correct environment variables set, the client does not retain service role permissions as expected. I suspect this might be related to the changes in session persistence.

Code References

Before:

export const createClient = (options?: CreateClientOptions) => {
  const { admin = true, ...rest } = options ?? {};

  const cookieStore = cookies();

  const key = admin
    ? process.env.NEXT_SUPABASE_SERVICE_ROLE_KEY!
    : process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

  const auth = admin
    ? {
        persistSession: false,
        autoRefreshToken: false,
        detectSessionInUrl: false,
      }
    : {};

  return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, key, {
    ...rest,
    cookies: {
      get(name: string) {
        return cookieStore.get(name)?.value;
      },
      set(name: string, value: string, options: CookieOptions) {
        try {
          cookieStore.set({ name, value, ...options });
        } catch (error) {}
      },
      remove(name: string, options: CookieOptions) {
        try {
          cookieStore.set({ name, value: "", ...options });
        } catch (error) {}
      },
    },
    auth,
    global: {
      headers: {
        "user-agent": headers().get("user-agent") as string,
      },
    },
  });
};

After:

return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, key, {
  ...rest,
  cookies: {
    getAll() {
      return cookieStore.getAll();
    },
    setAll(cookiesToSet) {
      try {
        for (const { name, value, options } of cookiesToSet) {
          cookieStore.set(name, value, options);
        }
      } catch (error) {}
    },
  },
  auth,
  global: {
    headers: {
      "user-agent": headers().get("user-agent") as string,
    },
  },
});

Problem

The recent changes seem to have removed the ability to create a client with a service role key. Even when the admin flag is set to true, the client does not behave as expected with service role permissions. This could be related to the session persistence or other changes in the client creation process.

Suspected Cause

The issue may stem from the configuration of persistSession, autoRefreshToken, or other related authentication settings. These may be preventing the client from properly maintaining service role permissions.

Suggested Solution

Investigate the interaction between the auth options and the ability to use service role keys. It may be necessary to adjust how session persistence or other authentication features are handled when using service role permissions.

Additional Context

This issue was observed in the createServerClient.ts file after the following commit:

549fe62813fb9c7277f7eda9e46ee0420f8e0b5f.

j4w8n commented 2 months ago

You should be using createClient() from @supabase/supabase-js to create a service role client, not ssr versions.

mariojfduarte commented 2 months ago

You should be using createClient() from @supabase/supabase-js to create a service role client, not ssr versions.

image

why? I tried it but no luck, doesn't seem to work with cookies

mariojfduarte commented 2 months ago

image

if I comment it out, my query result always returns null

j4w8n commented 2 months ago

A service role client shouldn't need to access cookies in order to work. What are you trying to accomplish?

mariojfduarte commented 2 months ago

A bit of context:

I need the service role to bypass row-level security (RLS) policies (not using any to make sure all requests come from server side).

I want to pre-init a zustand instance with the authenticated user. To do so, I use the session to check which user is authenticated then query my own public.users table by email using a service role key (all while in server side). The thing is, when I updated the package, it stopped working.

I had to fork, make the change, and rebuild, after what it started working. Not sure why the service role needs the perserveSession set to false, but it looks like it does.

mariojfduarte commented 2 months ago

I think this relates: https://github.com/orgs/supabase/discussions/27377#discussioncomment-10428125

j4w8n commented 2 months ago

Just to be clear, is your issue resolved?

mariojfduarte commented 2 months ago

No, I need to merge the pull request (currenlty using a fork).

I can't bypass the auth options.

image

Why is it implemented this way?

auth: {
        ...(options?.cookieOptions?.name
          ? { storageKey: options.cookieOptions.name }
          : null),
        ...options?.auth,
        flowType: "pkce",
        autoRefreshToken: false,
        detectSessionInUrl: false,
        persistSession: true,
        storage,
      },

This change solves my problem:

auth: {
        ...(options?.cookieOptions?.name
          ? { storageKey: options.cookieOptions.name }
          : null),

        flowType: "pkce",
        autoRefreshToken: false,
        detectSessionInUrl: false,
        persistSession: true,
...options?.auth,
        storage,
      },
j4w8n commented 2 months ago

I guess I still don't understand why you can't use createClient from @supabase/supabase-js for this.

What does your query look like?

To be a bit more clear: you should be using both clients. The ssr server client to get the user data (presumably their id), then the native service role client to do the query thst bypasses RLS.

mariojfduarte commented 2 months ago

Correct, I'm using two clients.

Still, when using superbase-js native service role client is doesn't work.

The only way to make it work is to pass the persistSession flag to false.

j4w8n commented 2 months ago

If you call getSession with the service role client, is it returning a session? If so, I suspect the issue is that you're mistakenly setting persistSession to false for your ssr client. This causes it to store the user's session in memory. And since the service role client also has that set to false, it's picking up the user session and passing along the user's JWT with your query - resulting in not bypassing RLS.

mariojfduarte commented 2 months ago

it worked, thanks