Skip to content
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

Custom Headers not present in RLS #233

Closed
2 tasks done
riderx opened this issue Mar 20, 2023 · 11 comments
Closed
2 tasks done

Custom Headers not present in RLS #233

riderx opened this issue Mar 20, 2023 · 11 comments
Labels
bug Something isn't working

Comments

@riderx
Copy link

riderx commented Mar 20, 2023

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

Custom header in Supabse SDK are not present to use in RLS as it is for query.

To Reproduce

Create a table test_rls with real-time and RLS

CREATE TABLE "public"."test_realtime_rls" (
    "id" int8 NOT NULL,
    "created_at" timestamptz DEFAULT now(),
    PRIMARY KEY ("id")
);

Add this RLS on ALL

(((current_setting('request.headers'::text, true))::json ->> 'capgkey'::text) = '8fd6f83fd1842b0d79cc212a133c4f10'::text)

Then set the SDK like that:

  const options: SupabaseClientOptions<'public'> = {
    global: {
      headers: {
        capgkey: '8fd6f83fd1842b0d79cc212a133c4f10',
      },
    },
  }
  const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, options)

Now register a real-time listener:

    supabase
      .channel('table-db-changes')
      .on(
        'postgres_changes',
        {
          event: 'INSERT',
          schema: 'public',
          table: 'test_realtime_rls',
        },
        (payload) => {
          console.log('payload', payload)
        })
      .subscribe()

And do insert with default value

supabase
      .from('test_realtime_rls')
      .insert({created_at: new Date()})

Expected behavior

Insert should work, and a log payload should appear.

Where, in fact, only the insert works.

If I disable RLS and do insert again, the payload is logged.

System information

  • OS: macOS
  • Browser chrome
  • Version of supabase-js: 2.12.0
  • Version of Node.js: v19.8.1

Additional context

I did try many solutions unsuccessfully:
supabase/realtime#470
supabase/supabase-js#553 (comment)
https://discord.com/channels/839993398554656828/1087461400647049377/1087461400647049377

This is coming from a use case of my product Capgo.
Where I created apikey ( access key) system for my users to use in the CLI of Capgo who is used in their CI/CD.
Using the login/password, seems not reliable to me in that case.
I know that made me an edge case of the SDK.

Hope I found an easy way to explain how to reproduce it.

@riderx riderx added the bug Something isn't working label Mar 20, 2023
@riderx
Copy link
Author

riderx commented Mar 23, 2023

Current solution used:

    let loop = true
    let now = new Date().toISOString()
    while (loop) {
        const { data, error } = await supabase
            .from('test_realtime_rls')
            .select()
            .single()
        if (data && !error) {
          console.log('payload', data)
        }
        now = new Date().toISOString()
        await wait(1000)
    }

It kind of self DDOS, but this is only used during onboarding of my users, so that will be like that until something better appear

@w3b6x9
Copy link
Member

w3b6x9 commented Mar 23, 2023

This is a followup to supabase/realtime#470 (comment).

@riderx Yes, something like supabase/realtime#470 (comment).

supabase/realtime#470 (comment)

did you sign your own token and pass that in?

@riderx
Copy link
Author

riderx commented Mar 23, 2023

@w3b6x9 that will require to my side to refactor the whole system who work already for normal query with header, the latest solution is not optimal but works.
Do allowing the header to pass is planned in the near future?
otherwise , I will try to plan a change to forge JWT, but for the CLI context it looks huge work compared to the actual super simple solution.

@w3b6x9
Copy link
Member

w3b6x9 commented Mar 28, 2023

Do allowing the header to pass is planned in the near future?

@riderx we do not plan to support this in the near term.

I think your looping query above is a nice workaround.

Let me know if you decide to sign your own JWT or continue with the looping query you shared above.

@riderx
Copy link
Author

riderx commented Mar 28, 2023

Ok thanks !

@riderx riderx closed this as completed Mar 28, 2023
@NoRKin
Copy link

NoRKin commented Feb 16, 2024

@w3b6x9 do you still not have any plans to support this? Would you be open to a PR for that?

A warning in the supabase documentation to never use the request headers in RLS would be useful to avoid people designing the wrong solution only to find out later that it will never work with realtime.

@filipecabaco
Copy link
Contributor

@NoRKin we're changing some things around regarding how we use RLS for authorization and this feature is planned to be added (this is a draft PR, the real implementation is yet to be implemented but it will be similar)

https://github.com/supabase/realtime/pull/757/files#diff-9542ac1036d63c2de0cf927b9cebf2c4d045ce6388432f56a0ba26ca83920c8bR47

@NoRKin
Copy link

NoRKin commented Apr 17, 2024

@lauri865
Copy link
Contributor

Also running into this issue.

Is there a way to detect in postgres when a function is being called within the realtime context? Could at least add a temporary workaround for RLS rules. But I tried e.g. current_setting('realtime.channel_name', TRUE) and it returns NULL.

It's impossible to have a multi-account setup without headers. Only option seems to be issuing a new JWT per account, but that doesn't work well across tabs either.

@GaryAustin1
Copy link

@lauri865
You might need to turn on the feature by adding the channel to have RLS...

From the temp docs at github discussions...
image
https://github.com/orgs/supabase/discussions/22484

But just a guess.

@lauri865
Copy link
Contributor

lauri865 commented Apr 18, 2024

Just to reiterate, this is our setup:
We have a multi-account setup whereby a JWT is issued for an array of accounts. Which account is active depends on the URL slug or subdomain. This can be achieved with PostgREST without an issue with headers (either reading referrer or setting a custom header with selected account_id).

Now, the problem is with realtime, where it's not possible to set request headers. Which makes computing the RLS impossible, as we don't know which account is selected from the JWT.

And before anyone suggests that we forge a new token, it's a strict requirement to be able to use different accounts across tabs. That would make the token refreshing logic quite complex, and another thing we would have to handle on our own without supabase.

I think it would be great if realtime.subscription table also stored either supabase.realtime.params or supabase.realtime.headers values (which would be loaded into request.headers or request.params), which could then be used to compute RLS policies against. That would close the gap between REST + realtime.


On a side-note, the only way I could find to check if postgres function is called within the realtime context is the following – quite contrived, but essentially adding a custom claim to the subscription table, which then becomes accessible through request.jwt.claims.realtime. It's not a solution to the above, but enabled us to add a temporary workaround that doesn't make realtime completely unusable (we can only use it for the primary account for the time being):

CREATE OR REPLACE FUNCTION app.set_realtime_token()
    RETURNS TRIGGER
    LANGUAGE plpgsql
    AS $function$
BEGIN
    NEW.claims := jsonb_set(NEW.claims, '{realtime}', to_jsonb(true), true);
    RETURN NEW;
END
$function$;

CREATE OR REPLACE TRIGGER set_realtime_token
BEFORE INSERT OR UPDATE ON realtime.subscription
FOR EACH ROW
EXECUTE FUNCTION app.set_realtime_token();

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

6 participants