supabase/storage-js

`New row violates row-level security policy` Error with presigned upload URLs using service_role

martinbartolo opened this issue · 2 comments

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 am trying to enable client-side uploads to a storage bucket using presigned upload URLs generated by my server-side supabase client created with my service_role secret key. However, I am running into an issue where using the signed URL for uploads using axios (since I would like to track upload progress and be able to abort uploads) gives me the following error.

{
    "statusCode": "403",
    "error": "Unauthorized",
    "message": "new row violates row-level security policy"
}

There doesn't seem to be any documentation on the storage v1 endpoints (the API docs https://supabase.github.io/storage-api redirect to a 404). so I'm not exactly sure how to use the signedURL to upload with axios apart from a post request to the signedURL. Could this be a problem with signedURLs when using service_role or am I using the signedURL incorrectly?

To Reproduce

I am getting my pre-signed URLs as follows on the server side. I am using a service_role supabase client to bypass RLS and send the signedURL back to my client so that users can upload to the URL without supabase authorization (I am using another authorization service).

const supabase = createClient(env.SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY);

export const supabaseRouter = createTRPCRouter({
  getUploadURL: protectedProcedure
    .input(
      z.object({
        bucket: z.string().optional(),
        filename: z.string().optional(),
        userId: z.string().optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      if (!input.bucket || !input.filename || !input.userId) {
        return null;
      }
      if (ctx.session.user.id !== input.userId) {
        throw new Error("User is not authorized to upload to this bucket");
      }
      const { data, error } = await supabase.storage
        .from(input.bucket)
        .createSignedUploadUrl(`${input.userId}/${input.filename}`);
      if (error) {
        throw error;
      }
      return data;
    }),
});

Now when I try to use the URL and the token from the presigned URL with this axios POST request client-side I get the 403 error.

const uploadToURL = async (file: File, url: string, token: string) => {
      await axios
        .post(url, file, {
          headers: {
            "Content-Type": file.type,
            authorization: `Bearer ${token}`,
          },
          onUploadProgress: (progressEvent) => {
            if (progressEvent.total) {
              const progress =
                (progressEvent.loaded / progressEvent.total) * 100;
              setUploadProgress!(progress);
            }
          },
          signal: abortController.current?.signal,
        })
        .then((response) => console.log(response))
        .catch((error) => console.log(error));
    };

Expected behavior

I would expect my client-side axios request to be able to upload a file to the signedURL even if I have not created a supabase client and there is no supabase auth since this does not require any RLS policies.

My bucket has no policies assigned but my service_role client bypasses any RLS policies and my axios request to the signedURL should not require any RLS policies either so I don't think that is an issue. Since the createSignedUploadUrl function returns a signed URL, a path and a token, I have tried using the signed URL without any authorization header, the signed URL with the token as authorization header and the path with the token as authorization header and I get the same error regardless.

System information

  • OS: Windows
  • Browser: Chrome
  • Version of supabase-js: 2.38.4
  • Version of Node.js: 21.1.0

Hello! You'll need to use PUT instead of POST to upload via signed upload URL

The repo was renamed from storage-api to storage the correct doc link is here:

https://supabase.github.io/storage