Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

GraphQL access control check failing via local API

default discord avatar
waterlord935 months ago
1

Hello. I am on payload 3.0 beta 55



Worth noting that in this case

payload

object is generated with sanitized config (code for that at the end of the post)



i have following access control in place on collection:


export const adminsAndUsersInSession: Access = async ({ req: { user }, }): Promise<AccessResult> => { if (!user) { return false; } if (checkRole([ERole.admin], user)) { return true; } if (checkRole([ERole.player], user)) { console.log('adminsAndUsersInSession role is player, userId:', user.id); return { players: { contains: user.id, }, }; } return false; };

I get that last console.log with correct userId before getting 403 response




On collection its set up like this


access: { create: anyUser, update: admins, delete: admins, read: adminsAndUsersInSession, // read: () => true, },

but when i do this:


const session = await payload.findByID({ collection: 'game-session', id: sessionId, overrideAccess: false, user: userId, depth: 0, showHiddenFields: false, });

i get 403 fobidden.



However when i target api (from browser window of same user i am passing to findById) for that session id like this: localhost:3000/api/game-session/{sessionId}?depth=0, i get response, which should mean that access control for read works, but for some reason its failing when i am "simulating" user via local-api.



code to get sanitized payload config:


export const payloadWithServerOnlyConfig = async () => { // initiate payload local API const configPath = path.join( process.cwd(), process.env.PAYLOAD_CONFIG_PATH || './src/payload.config.ts', ); const fullConfig = await importWithoutClientFiles<{ default: Promise<SanitizedConfig>; }>(configPath); const config = await fullConfig.default; return await getPayload({ config: config }); };

and then:


const payload = await payloadWithServerOnlyConfig();

GraphQL access control check failing via local API



It has 3 fields, id, players which is relationship hasMany: true of userIds and boardId which is relationship to boards, both of these have basically no access control (just that user must be non-null in req).


I understand what you are saying and it makes sense, but wouldnt access control on child objects also impact reading when i visit api route for that specific game-session?



Sure, give me 5 min do switch to personal pc



handleChosenSession calls payloadWithServerOnlyConfig and getPlayersInSessionForSocket, the rest of the files are collections and custom access rules i have.



btw i am using mongoDb cluster with replicaset but only one instance in local, whole project is local only at the moment



Yes, i get 403 error, if i change that findById to overrideAccess, it works



Only thing i have not specifically checked is if somehow userId has space or something like that, but in access control method user object with roles exist so i suppose it would have failed that if that was the problem.



I am pretty sure thats not the case, as user object is fine in read access hook, user.roles and user.id both exist, automatically populated... and cookie should not matter, this is local api that i am using...


this action is triggered when socket.io event happens, that is the reason i am using using config without client dependicies, as i have to register socket events during server boot, and client dependencies were causing me issues - asking for custom loaders for css and scss files etc...



Yup, when user picks session on the fe, socket.io event is emitted that triggers code in "handleChosenSession.ts"



Do you think that could be the factor somehow? I cant wrap by head about issue being anything else than graphql return of "adminsAndUsersInSession" not working properly



I am doing that, i am getting userId from that cookie and passing it as userId in payload.findById



I might be mislabeling the last return of "adminsAndUsersInSession" as "GraphQL" since it looks like graphQL to me ( players: { contains: userId } )



That happens before any of this code. I didnt consider it relevant since userId i am passing to payload.findById is defined and has a value, and access hook is able to read whole user object



I read from cookie when connection event happens, and store user id in socket.data.userId, then consume it from there is following socket events



Ok, will do, still dont see how it would be relevant since payload.findById takes in userId as input and i can guarantee it has correct value at that moment



Ok, i will give that a try in about 5h from now when i am done with work :)



i gave it a shot and it absolutely did not change anything :)


i even tried making PayloadRequest to pass to payload.findById, by using socket.request and socket.request.headers but they are totally incompatible so couldnt do it.



on the positive note, i just realized that req.user.id in access control hook is actually undefined...



well i am a fool, i am so sorry for wasting your time, and thank you very much for your help. You did point me in right direction with credentials as it forced me to recheck and add more logs to figure out why user isnt getting there...


What I didnt realize was that I had another request still being fired from frontend for this session and that was console.log with userId in access control that i was seeing, while one that came from using payload.findById was failing before console.log.



fix was to fetch whole User object by id, without access control, then pass it to my call in user prop in order to have access control working, just passing id was not enough.

  • default discord avatar
    markatomniux6 months ago

    What does your game session collection look like?



    Access control is applied to all collections that are referenced as relationships in the target collection. So whatever your access control settings are in game-session is what will be blocking you



    also, use 3 backticks to create a codeblock, and you can add automatic code annotations by using the language file extension netx to the first set of backticks;



    like this '''ts  '''


    can you please code dump for me?



    and it's failing specifically on your findById line?



    are you sure your attaching your user correctly when this gets called? Is it triggered by an endpoint or a hook?



    if you are checking for a user, but there is no user being parsed by the Payload middleware because it can't find the cookie, that would be a likely cause of failure



    oh socket io



    so socket io is initiating the payload request?



    I'm don't see any GraphQL anywhere so I don't think that's the issue, and regarding access controls, GraphQL is just querying, access control happens before that.



    I would imagine there's an issue with socket.io. I don't even know how it could work in a 'use server' function, i wouldn't have thought the NextJS app router lifecycle would have allowed for that. Not to mention how expensive that would get running on an edge function.



    I think your socket.io is running in isolation without context of your NextJS app, which is why you're passing the Payload object. That would also indicate to me that your user object will not be getting pulled through either as User is populated in payload middleware (or it WAS populated in middleware, i think it's done in a server action now)



    your user lives in a cookie called payload-token. You might be able to pass that to socket.io. The cookie contains your JWT which payload uses to seed your User object in all contextualised requests and hooks



    that's just Payload's query language



    I don't see any cookie code



    before your socket.io connection is triggered?



    https://socket.io/docs/v3/handling-cors/

    Try adding credentials include



    if you do it with includes, your user ID should get passed to the Payload "middleware"

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.