Skip to content

Failing RLS policy on bucket returns 400 (StorageUnknownError) instead of 403 (Access Denied) #640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
2 tasks done
tomtitherington opened this issue Mar 6, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@tomtitherington
Copy link

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

When trying to check the existence of a file within a private storage bucket, if the RLS policy fails, a storage error of type StorageUknownError with HTTP status 400 is returned from the JavaScript client.

To Reproduce

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

  1. Create a private bucket with a simple failing RLS policy.
-- Create the bucket for assets
insert into storage.buckets(id, name, public)
    values ('assets', 'assets', false);

-- Create a policy that will always fail
-- NOTE: Changing false to true here results in a successful response
create policy "Users can never access" on storage.objects
    for select to public using (bucket_id = 'assets' and false);
  1. Apply the migration
  2. Seed it with an asset (optional)
  3. Try to access the bucket by checking the existence of such an asset. For example...
import { createClient } from "@supabase/supabase-js";

const main = async () => {
    // Check for URL, anon key and service role key
    if (!process.env.SUPABASE_URL) {
        console.error("No SUPABASE_URL environment variable found. Exiting...");
        process.exit(1);
    }
    if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
        console.error(
            "No SUPABASE_SERVICE_ROLE_KEY environment variable found. Exiting...",
        );
        process.exit(1);
    }
    if (!process.env.SUPABASE_ANON_KEY) {
        console.error(
            "No SUPABASE_ANON_KEY environment variable found. Exiting...",
        );
        process.exit(1);
    }

    const serviceClient = createClient(
        process.env.SUPABASE_URL,
        process.env.SUPABASE_SERVICE_ROLE_KEY,
    );

    // Create a user
    const DEFAULT_PASSWORD = "Developer123!";
    const adminUserResponse = await serviceClient.auth.admin.createUser({
        email: "hello@example.com",
        password: DEFAULT_PASSWORD,
        email_confirm: true,
        user_metadata: {
            first_name: "Bill",
            last_name: "Keys",
        },
    });

    if (!adminUserResponse?.data.user) {
        console.error(
            "No user returned from Supabase signup. Error:",
            adminUserResponse.error,
        );
        console.log("Exiting...");
        process.exit(1);
    }

    const client = createClient(
        process.env.SUPABASE_URL,
        process.env.SUPABASE_ANON_KEY,
    );

    const signIn = async () => {
        const { data, error } = await client.auth.signInWithPassword({
            email: "hello@example.com",
            password: "Developer123!",
        });
    };

    await signIn();
    
    const { data: exists, error: assetsError } = await client.storage.from(
        "assets",
    )
        .exists(
            "an-asset.webp",
        );

    console.log("(JWT client) Storage response error: ", assetsError);

};

main();

This should output something like this...

(JWT client) Storage response error:  StorageUnknownError: {}
    at <anonymous> (/Users/tomtitherington/Development/supa-asset-api/supabase/node_modules/.pnpm/@supabase+storage-js@2.7.1/node_modules/@supabase/storage-js/src/lib/fetch.ts:36:12)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/tomtitherington/Development/supa-asset-api/supabase/node_modules/.pnpm/@supabase+storage-js@2.7.1/node_modules/@supabase/storage-js/dist/main/lib/fetch.js:5:58)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
  __isStorageError: true,
  originalError: Response {
    status: 400,
    statusText: 'Bad Request',
    headers: Headers {
      'content-type': 'application/json; charset=utf-8',
      'content-length': '69',
      connection: 'close',
      date: 'Thu, 06 Mar 2025 11:57:09 GMT',
      'access-control-allow-origin': '*',
      'x-kong-upstream-latency': '9',
      'x-kong-proxy-latency': '0',
      via: 'kong/2.8.1'
    },
    body: null,
    bodyUsed: false,
    ok: false,
    redirected: false,
    type: 'basic',
    url: 'http://127.0.0.1:54321/storage/v1/object/assets/5a80860e-d075-4181-96f6-b0c075fcbc15.webp'
  }
}

Expected behavior

I would expect an error of type "AccessDenied" or a HTTP status 403 to be returned as documented here.

System information

  • OS: macOS
  • Version of supabase-js: v2.49.1
  • Version of Node.js: v23.7.0
@tomtitherington tomtitherington added the bug Something isn't working label Mar 6, 2025
@fenos
Copy link
Contributor

fenos commented Mar 12, 2025

Hey @tomtitherington
thanks for reaching out.

Yes, the 400 status code was a bit of a legacy decision to have a single status code for errors, however, we are going to fix it in the next major version of Storage

@tomtitherington
Copy link
Author

Thanks for getting back @fenos. Is there a roadmap for releases or rough timeline for when this might be coming out?

This is effecting an app we have in production so would be great if you could share some details so we can plan around this.

Thanks!

@fenos
Copy link
Contributor

fenos commented Mar 13, 2025

Hey @tomtitherington no worries at all; it is my pleasure 👍

I don't have an exact timeline for this, but it is something we are actively changing for the next major release of Storage.
In the meantime, i'd be happy to help handle this error

Could you explain further what the problem you are encountering when trying to handle this error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants