A gateway that keeps the session
The problem it solves
A single-page app that holds an access token keeps it somewhere JavaScript can read —
memory, localStorage — and so can any script that runs in the page, including an
injected one. Refresh tokens make it worse: a stolen one outlives the session.
With the Backend-for-Frontend pattern, the browser holds only an HTTP-only,
SameSite=Strict cookie. The gateway keeps the tokens server-side, attaches the
access token to API calls and refreshes it. Script injection can still act within the
page, but it cannot carry a token away.
It also gives the app one origin: the SPA, the API and the event stream are served from the same address, so there is no CORS to configure for the browser.
What it costs
- One more service to run, deploy and monitor —
Blouppy.Gateway. - A hop on every API call.
- CSRF to handle, since a cookie is sent automatically: every API call carries
X-CSRF: 1, which the Angular app adds and a cross-site form cannot. - Session storage: the cookie's encryption keys live in PostgreSQL, so that replicas share them.
What you can relax
- Mobile apps and scripts do not need the gateway. They call the API with a bearer token directly — the Starter's personal access tokens do exactly that, on the API's own domain.
- The SPA could hold tokens itself (authorization code + PKCE in the browser). You would remove the gateway, add CORS to the API and accept tokens in JavaScript. The API would not change: it already validates JWTs from the identity host.
The gateway is built from ASP.NET Core's own pieces — YARP, the cookie handler and
Microsoft's OpenID Connect handler — with no BFF library, so changing its behaviour
means changing ordinary middleware in Blouppy.Gateway/Session/.