Move browser authentication behind a generated-contract BFF

This commit is contained in:
ms2k 2026-07-19 18:21:17 +09:00
commit fa7220ee33

View file

@ -20,6 +20,8 @@ Terms in this glossary are stable identifiers for use across Remora documentatio
| `ResultManifest` | The immutable exact inventory and root identity for one attempt's result aggregate. |
| `CommittedResult` | The concrete durable descriptor corresponding to the attempt committed by `JobProtocol`. |
| `WebClient` | The browser-based user interface for submitting and managing jobs. |
| `WebBFF` | The same-origin confidential OAuth client and token-mediating backend for `WebClient`. |
| `WebSession` | Ephemeral server-side OAuth session state addressed by an opaque browser cookie. |
| `API` | The authenticated service interface for job submission, status, cancellation, history, and deletion. |
| `Worker` | A stateless process that executes attempts and reports progress and outcomes. |
| `IdentityProvider` | The external authority for user identity, administrative roles, and storage authorization scopes. |
@ -51,9 +53,9 @@ Identity, coordination, and durable state are external services. Multiple API an
## Architecture
`WebClient` communicates with `API`, which authorizes requests through `IdentityProvider`, records jobs in `DurableStore`, and submits work through `CoordinationStore`. `Worker` replicas consume jobs and stream data from `Source` through `StorageAdapter` into `TargetStore`.
`WebClient` communicates only with its same-origin `WebBFF`. `WebBFF` is the confidential OAuth and OpenID Connect client, mediates browser API and live-event access, and sends bearer credentials only to `API`. `API` validates the bearer identity and roles supplied by `IdentityProvider`, records jobs in `DurableStore`, and submits work through `CoordinationStore`. `Worker` replicas consume jobs and stream data from `Source` through `StorageAdapter` into `TargetStore`.
`API` and `Worker` replicas are stateless. `DurableStore` realizes `durableVars` and retains architecture data outside the model. `CoordinationStore` realizes `coordinationVars` and may be reconstructed from durable state as permitted by `JobProtocol`.
`API`, `Worker`, and `WebBFF` replicas are stateless. `WebSession` records are ephemeral external state: their loss signs users out and does not alter durable application state. `DurableStore` realizes `durableVars` and retains architecture data outside the model. `CoordinationStore` realizes `coordinationVars` and may be reconstructed from durable state as permitted by `JobProtocol`.
`TargetStore` contains immutable attempt-scoped objects. The model's staging identity abstracts each concrete aggregate, and its committed attempt maps to the manifest descriptor exposed as the `CommittedResult`. `JobProtocol` is the sole specification of attempt authority and publication behavior.
@ -70,6 +72,7 @@ flowchart TD
end
subgraph Service
WebBFF[WebBFF]
API[API]
Worker[Worker]
end
@ -88,8 +91,9 @@ flowchart TD
TargetStore[TargetStore]
end
WebClient -- OIDC --> IdentityProvider
WebClient -- API + live updates --> API
WebClient -- Session + CSRF --> WebBFF
WebBFF -- OAuth + OIDC --> IdentityProvider
WebBFF -- Bearer API + live updates --> API
IdentityProvider -- Identity and roles --> API
API -- Submit and coordinate --> CoordinationStore
API -- Record and query --> DurableStore
@ -100,6 +104,19 @@ flowchart TD
StorageAdapter --> TargetStore
```
## Browser Session Boundary
- `WebBFF` uses the OAuth authorization-code flow as a confidential client with Proof Key for Code Exchange and nonce validation. OAuth and OpenID Connect remain the only user-authentication path.
- Access tokens, refresh tokens, and ID tokens remain inside `WebBFF` and its protected `WebSession` store. Browser JavaScript receives only an authorization snapshot and a CSRF value.
- The browser carries a random session identifier in a host-only `__Host-` cookie with `Secure`, `HttpOnly`, `SameSite=Strict`, and `Path=/`. Authentication creates both the session identifier and CSRF value. Token renewal rotates the identifier while retaining the CSRF value for in-flight same-session requests; an identity change invalidates the session.
- `WebSession` state is stored under a deployment-specific, WebBFF-only namespace in `CoordinationStore`. Its lifetime is bounded by the provider session and token lifetimes. Loss, expiry, unreadable state, or failed renewal destroys the local session and requires authentication again.
- Unsafe same-origin requests require the current session CSRF value in a request header. `WebBFF` also rejects unsafe requests with an absent or foreign `Origin` or a cross-site Fetch Metadata classification. SameSite cookies are defense in depth rather than the sole CSRF control.
- Browser API requests use a same-origin BFF route. `WebBFF` removes browser credentials and forwarding headers, injects the current access token, preserves idempotency keys and response status, and never retries an unsafe application request.
- Browser live-event connections use a same-origin BFF WebSocket route. The browser obtains the one-use event ticket through the API proxy; `WebBFF` validates the browser session and proxies the ticket-bearing connection without exposing a bearer token.
- Local logout invalidates `WebSession` before redirecting to the provider logout endpoint. Provider logout failure cannot restore the local session.
- The public `API` bearer-token interface remains available to non-browser OAuth clients. `WebBFF` does not weaken API authentication or authorization and does not reinterpret roles.
- `WebBFF` is presentation and identity mediation outside the state abstracted by `JobProtocol`. It preserves the authorization-snapshot, event-ticket, replay, and idempotency semantics defined at the API boundary.
## Worker Execution Envelope
- Every Worker process creates a fresh, non-reused `WorkerIncarnation`. A restarted process cannot reuse authority issued to an earlier lifetime, including when it occupies the same replica slot.
@ -244,7 +261,7 @@ flowchart TD
| Observability | Expose job status, progress, and errors through the UI and API |
| Scalability | Support multiple stateless API and worker replicas using shared state services |
| Performance | Stream multi-gigabyte objects without buffering complete objects in worker memory |
| Security | Authenticate and authorize all operations; do not expose long-lived source or target credentials to the UI |
| Security | Authenticate and authorize all operations; keep OAuth tokens and long-lived source or target credentials outside browser JavaScript |
| Deployment | Deploy as a standard Kubernetes service using externally managed identity, state, and object storage |
## Responsibility Boundaries