Search code examples
cookiesnestjscross-domainsamesitehttponly

Render.com: HttpOnly Cookie not being set in browser storage when doing res.cookie between Web Services


I have a NestJs app that uses HttpOnly cookies for authentication. In development everything works perfectly. My NextJs client (http://localhost:4200) uses Graphql to send a login request to my NestJs server (http://localhost:3333), which sets httpOnly cookies in the response, and then the client successfully adds the cookies to the browser storage.

However, when I deploy both apps to Render.com, everything works apart from the final step. The cookies are successfully received in the response, but are not added to the browser storage. Interestingly, if I use the GraphQL playground of the deployed production server to make the login request, everything works. So, clearly, the issue seems to be that the request is coming from a subdomain.

My production configuration:

Client: https://swingapple-web-staging.onrender.com
Server: https://swingapple-api-staging.onrender.com

Cookies settings (Server):

sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
secure: process.env.NODE_ENV === 'production',
domain: process.env.NODE_ENV === 'production' ? '.onrender.com' : undefined

Nestjs's GraphQLModule settings (Server):

playground: true,
introspection: true,
autoSchemaFile: true,
sortSchema: true,
context: ({ req, res }) => ({ req, res }),
cors: {
    credentials: true,
    origin: [process.env.CLIENT_URL, 'http://localhost:4200']
}

Relevant Nest main.ts config (server):

app.set('trust proxy', 1);

graphql-request settings (client):

credentials: 'include'

I've spent days trying to figure this out so any help would be really appreciated!


Solution

  • After speaking with the Render.com support, they informed me that onrender.com (the domain for their subdomains) is on the public suffixes list, which means cross site cookies won't work. The solution was to add a custom domain for both Web Services (which obviously has to be the same e.g. app.my-custom-domain.com and api.my-custom-domain.com). I did this, and now everything is working as intended!