Gateway and session

Blouppy.Gateway is the single origin the browser talks to. It serves the SPA, holds the session, and forwards API calls with an access token — the Backend-for-Frontend (BFF) pattern. The consequence that matters: no token ever reaches JavaScript.

Web APIIdentity hostGatewayBrowserWeb APIIdentity hostGatewayBrowserGET /bff/loginOpenID Connect (authorization code + PKCE)tokenssession cookie (HTTP-only, SameSite=Strict)GET /api/v1/workspaces + cookie + X-CSRF: 1read the access token from the session, refresh it if close to expiryGET /api/v1/workspaces + Authorization: Bearer …200200
Web APIIdentity hostGatewayBrowserWeb APIIdentity hostGatewayBrowserGET /bff/loginOpenID Connect (authorization code + PKCE)tokenssession cookie (HTTP-only, SameSite=Strict)GET /api/v1/workspaces + cookie + X-CSRF: 1read the access token from the session, refresh it if close to expiryGET /api/v1/workspaces + Authorization: Bearer …200200

It is built from ASP.NET Core's own pieces — YARP, the cookie handler and Microsoft's OpenID Connect handler — with no BFF library.

What it does

ConcernHow
The sessionA cookie (__Host-blouppy-gateway, SameSite=Strict, 8 hours sliding) holding the tokens. The cookie's encryption keys are in PostgreSQL (schema gateway), so a restart or a second replica keeps everyone signed in.
The access tokenAttached to /api and /identity calls, refreshed a minute before it expires, one refresh at a time per session. A refused refresh ends the session.
CSRFEvery /api and /identity call must carry X-CSRF: 1, which a cross-site form cannot send.
The SPA's endpoints/bff/login, /bff/logout (which revokes the refresh token), /bff/user for the signed-in user and the session's expiry.
The event streamProxied with response buffering off, and exempt from X-CSRF, since a browser EventSource cannot send a header.
Everything elseProxied to the Angular app.

Routes that are not the browser's

Two routes go through the gateway without the session:

  • /mcp — the MCP server. The client's own bearer goes through, the cookie is stripped and a foreign Origin refused.
  • /scim/v2 — an identity provider provisioning people, routed to the identity host.

Why it matters

A token in the browser can be read by any script that runs there — an injected one included. A cookie that JavaScript cannot read, scoped to one site, cannot be. The cost is one more service to run; see the choice for what you can change.