NestJS BFF must forward the Sanctum cookie
Laravel owns /api/login and the httpOnly auth_token. Nest maps DTOs. A second JWT in memory is a second identity.
After logout a Nest guard still served the previous user. Some products put NestJS in front of Laravel. The browser still talks JSON under /api. Auth stays cookie-only: /api/login, /api/register, /api/logout, /api/user. The token lives in httpOnly auth_token. Nest is a BFF, not a second IdP.
The failure was minting a Nest JWT after Laravel already set the cookie. The SPA then had two clocks. A logout that cleared the cookie left the JWT in memory. Guards in Nest that trusted the JWT and ignored the cookie served the wrong user after a refresh. Users saw someone else's name. Laravel had already returned 401.
The host needed one identity. Nest had invented a second.
Forward Cookie, map the DTO
Nest receives the browser request, copies the Cookie header to Laravel, and maps the JSON. It does not parse the token. CSRF follows the same cookie jar. CORS stays tight: same site, credentials on, no token in the body.
Queue work stays in Horizon or Messenger on PHP. Nest should not grow a second Redis prefix for jobs that already have a fence in MySQL. A TypeScript worker that retries HTTP without that fence is the same double charge with a prettier stack trace.
I logged Set-Cookie on the Nest response. If Nest rewrote auth_token or added a Bearer body, the BFF had become an IdP. The Axios instance on Nest must forward Cookie and the CSRF header, and must not attach Authorization from memory. A 401 from Laravel must clear Nest request-scoped user state. It must not keep a decoded JWT until process restart.
When Nest is the only API
If Laravel is gone, Nest still should not put the session in localStorage. An httpOnly cookie and a BFF that never echoes the secret is the same rule. This site uses Laravel Sanctum. The Nest path exists on other hosts. The cookie rule does not change with the language.
I also stopped Nest from caching /api/user in memory per process. That cache survived logout for the next request on the same instance. User identity is the cookie Laravel set, checked on every call, or a 401.
What I took from this
I measure logout then /api/user through Nest, and I grep the BFF for JWT sign and localStorage. A 401 from Laravel with a 200 from Nest is two identities.
I do not trust a Nest JWT next to Sanctum, or a process cache of the current user. Forward Cookie. Map the DTO. Do not parse the token.
The rule I keep: Laravel owns login. Nest does not mint a second session. Jobs stay on PHP with the MySQL fence. No token in the JSON body, on any host.
