fix(portal-shell): send withCredentials on /me so the session cookie crosses SPA→BFF in dev #114

Merged
julien merged 1 commits from fix/portal-shell/auth-me-with-credentials into main 2026-05-12 22:35:12 +02:00
2 changed files with 24 additions and 1 deletions
@@ -62,6 +62,18 @@ describe('AuthService', () => {
expect(service.isLoading()).toBe(false);
});
it('issues /me with withCredentials so the session cookie crosses the SPA→BFF origin gap', () => {
const { http } = setup();
const req = http.expectOne(ME_URL);
// Without this, `fetch`'s default `credentials: 'same-origin'`
// would suppress the session cookie on the cross-origin
// (localhost:4200 → localhost:3000) request and /me would
// always answer 401 in dev. Verified manually against the BFF
// log on 2026-05-12.
expect(req.request.withCredentials).toBe(true);
req.flush({ error: 'unauthenticated' }, { status: 401, statusText: 'Unauthorized' });
});
it('transitions to anonymous on 401', async () => {
const { service, http } = setup();
http
+12 -1
View File
@@ -58,7 +58,18 @@ export class AuthService {
*/
async refresh(): Promise<void> {
try {
const user = await firstValueFrom(this.http.get<CurrentUser>(this.meUrl));
// `withCredentials: true` is mandatory: the SPA at
// http://localhost:4200 calls the BFF at http://localhost:3000 —
// different origins, so `fetch`'s default `credentials:
// 'same-origin'` would drop the `__Host-portal_session` cookie
// and /me would always answer 401. Production (single origin
// behind the same edge) doesn't need it but it's harmless there.
// CORS on the BFF side already allows credentials
// (`apps/portal-bff/src/main.ts` → `enableCors({ credentials:
// true })`).
const user = await firstValueFrom(
this.http.get<CurrentUser>(this.meUrl, { withCredentials: true }),
);
this._state.set({ kind: 'authenticated', user });
} catch (err) {
this._state.set(toErrorState(err));