N
Neon6mo ago
national-gold

RLS with anonymous and authenticated

Hi there. I'm wondering if it's possible to use one single connection string to connect to either an anonymous or authenticated role, depending on the JWT provided by the connection string - i.e. if i can use only the anonymous role (with a password) and provide the token to optionally move the user to authenticated - is something like this possible?
20 Replies
national-gold
national-goldOP6mo ago
the idea of what i want to achieve here is to have one connection string and based on the params provided it will allow me to connect to different roles - if token is given then it will auto parse it and switch to authenticated the thing is that i use drizzle on the client so that i can fetch data there (just like with supabase) so i'm not 100% certain how to set it up right i've tried a few things so far: 1. disable rls and just use the authenticated role with a password (works) 2. have RLS disabled with the authenticated role and pass the token (throws a 500, no response body) 3. have RLS enabled with the authenticated role, with no JWT (works, returns no data) 4. have RLS enabled with the authenticated role AND provide the JWT (throws 500 again with no body) when i take the JWT (from the Authorization header) and verify it on jwt.io with the public key available to Neon, it is a valid JWT and the signature is valid as well - i'm not really sure what to do here
extended-salmon
extended-salmon6mo ago
neon rls assumes that you're using our serverless driver. under the hood serverless driver runs sql-over-http by connecting to our multi tentat proxy. this multi tenant proxy actually requires a passwordless connection string in the request header
extended-salmon
extended-salmon6mo ago
now if you don't want to use neon serveless driver then recently we've introduced one improvement into our postgres extention (pg_session_jwt) -- you might be interested in responses under https://github.com/neondatabase/pg_session_jwt/issues/33 but it would require your backend to connect to the postgres via TCP (standard connection method with database drivers) and run some SET LOCAL ops
GitHub
auth functions (session, user_id) return null after setting `...
Steps to reproduce SET request.jwt.claims = '{"sub": "test-user"}' SELECT auth.user_id() -- returns null Expected result test-user Actual result null Environment Neon Co...
extended-salmon
extended-salmon6mo ago
also responsibility to validate the JWT and SET LOCAL decoded JWT body would be on your backend refs: - https://github.com/neondatabase/pg_session_jwt/issues/33#issuecomment-2778427970 - https://github.com/neondatabase/pg_session_jwt/issues/33#issuecomment-2778441191
national-gold
national-goldOP6mo ago
i am using the serverless driver - @neondatabase/serverless with drizzle as a proxy as well like i do one of these (they are equivalent)
const db = drizzle(neon(NEON_URI), {schema}).$withAuth(JWT_TOKEN)

const db = drizzle(neon(NEON_URI, {authToken: JWT_TOKEN}), {schema})
const db = drizzle(neon(NEON_URI), {schema}).$withAuth(JWT_TOKEN)

const db = drizzle(neon(NEON_URI, {authToken: JWT_TOKEN}), {schema})
the issue with the passwordless string is that i can't have a JWT for anonymous users - they just don't have one, so i need to fetch something differently there. the fact that neon also returns a 500 when fetching is also an issue the way i know neon is throwing a 500 is because i'm running the query from my browser by just frontend fetching - pretty much the same as supabase which i migrated from
extended-salmon
extended-salmon6mo ago
having passwordless connection string that cant be used for TCP connection is actually a security feature - esp. when you include it in your frontend. Anyone could see that and connect to your database otherwise
under the hood serverless driver runs sql-over-http by connecting to our multi tentat proxy. this multi tenant proxy actually requires a passwordless connection string in the request header
if you need to use both authenticated and anonymous I guess you should create two db objects for each role and your frontend logic would need to read the JWT to know when to use which
the issue with the passwordless string is that i can't have a JWT for anonymous users - they just don't have one
I can see your point now. This is indeed problematic if you're using neon serverless driver directly from your frontend and want to use both authenticated and anonymous
optimistic-gold
optimistic-gold6mo ago
@oof2win2 it's not a good idea to connect directly to the database from the frontend
national-gold
national-goldOP6mo ago
yeah. i guess i can switch the URI based on the presence of the JWT which is fine, it was just the 500s throwing me off - i'm not sure what to do to actually get the query to execute with RLS why not? the issues i see are rate limiting but isn't that soon to be solved via allowed queries and rate limits on the serverless driver itself
extended-salmon
extended-salmon6mo ago
yeah. i guess i can switch the URI based on the presence of the JWT which is fine
I think such request will be rejected because the absence of JWT -- that's why I said that it's problematic
national-gold
national-goldOP6mo ago
ah i see. so basically anonymous auth does not work, even on the anon role? if that's the case, does it mean i need to move all of my data fetching from the client to the server?
extended-salmon
extended-salmon6mo ago
yeah I think that's a possible way of achieving what you described: - using authenticated + jwt + rls policies that rely on JWT - using anonymous with the TCP connection in backend -- you will have to finegrain the anonymous access rights so that it can only SELECT only from specific tables I guess
national-gold
national-goldOP6mo ago
i have all of the policies setup right - my anonymous role can only select from stuff that it has rights to. might try to figure out some other shenanigans with the anon role since most data fetching is in the frontend (i'm coming from supabase which is why) @mrl5 is there a reason why i shouldn't just make the anonymous role have a password and fetch data through that? it would still be a nologin role so you can't directly connect to it and it would solve the issue of not having a password - is there something wrong with this approach? it's granted only select + rls policies prevent it from fetching private data
extended-salmon
extended-salmon6mo ago
make the anonymous role have a password and fetch data through that?
it would still be a nologin role so you can't directly connect to it
isn't it mutually exclusive? and actually anonymous created by Neon RLS has a LOGIN postgres property, it's just have an empty password which prevents from logging in
national-gold
national-goldOP6mo ago
hmm @mrl5 you were right on that one. how else can i get data as an anonymous user, if i don't want to fetch everything server side and keep it on the client? and is there a difference between having NOLOGIN on it or not? like if a user just creates an account and then is authenticated vs just querying on anonymous (security wise, assuming RLS is correct)
extended-salmon
extended-salmon5mo ago
and is there a difference between having NOLOGIN on it or not?
in our specific context - practically as long as you have empty password then you can't log in as this user anyway. theoretically you can check https://www.postgresql.org/docs/current/sql-createrole.html
These clauses determine whether a role is allowed to log in; that is, whether the role can be given as the initial session authorization name during client connection.
These clauses determine whether a role is allowed to log in; that is, whether the role can be given as the initial session authorization name during client connection.
national-gold
national-goldOP5mo ago
yeah i had to make it a standard LOGIN role, else it wouldn't work. i think it should be fine but i'll see i guess
extended-salmon
extended-salmon5mo ago
our backend bootstraps authenticated and anonymous with empty password and LOGIN prop if you'd like to connect as those roles with any postgres client (e.g. psql) it will be rejected so in practice it's the same behavior as with NOLOGIN
national-gold
national-goldOP5mo ago
yeah but i had to add a password to anon, else i wouldn't be able to use it - anonymous users don't have a JWT so there is no way for me to authenticate
extended-salmon
extended-salmon5mo ago
please remember about one of the previous posts from David:
David Gomes — 09/04/2025, 10:43 @oof2win2 it's not a good idea to connect directly to the database from the frontend
national-gold
national-goldOP5mo ago
yeah i know it's not due to no rate limiting being present as of now, but besides me rewriting all of my fetching logic i have no other way to do this

Did you find this page helpful?