Move browser authentication behind a generated-contract BFF
This commit is contained in:
parent
6f0f20f5cc
commit
fa7220ee33
2 changed files with 7259 additions and 2825 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Kubernetes Deployment
|
||||
|
||||
`deploy/charts/remora` runs API, Worker, and WebClient as separate Deployments.
|
||||
`deploy/charts/remora` runs API, Worker, and WebClient with its `WebBFF` as separate Deployments.
|
||||
CloudNativePG, Dragonfly, Vault, ZITADEL, and VersityGW remain externally
|
||||
managed.
|
||||
|
||||
|
|
@ -19,8 +19,12 @@ managed.
|
|||
- Dragonfly uses a deployment-unique namespace.
|
||||
`REMORA_GLOBAL_ATTEMPT_LIMIT` and `REMORA_WORKER_LOCAL_ATTEMPT_LIMIT` remain
|
||||
independent.
|
||||
- ZITADEL supplies the issuer, project audience, client identifier, and exact
|
||||
project-role claim.
|
||||
- The WebBFF stores ephemeral WebSessions through a WebBFF-only Dragonfly
|
||||
credential and namespace. Its credential Secret is not shared with API or
|
||||
Worker coordination access. Loss of that namespace signs browser users out
|
||||
without affecting durable application state.
|
||||
- ZITADEL supplies the issuer, project audience, exact project-role claim, and
|
||||
a confidential WebBFF client whose secret is mounted from a Secret.
|
||||
- VersityGW uses dedicated credentials and a bucket whose `_remora` namespace
|
||||
excludes other writers and lifecycle expiry. Deployments unable to establish
|
||||
both properties are unsupported.
|
||||
|
|
@ -28,13 +32,18 @@ managed.
|
|||
## Security
|
||||
|
||||
NetworkPolicies default-deny each workload, isolate the Worker and management
|
||||
ports, and allow dependencies by selector or CIDR. Public source egress excludes
|
||||
ports, and allow dependencies by selector or CIDR. The WebBFF may reach only
|
||||
the API, identity provider, and its session store. Public source egress excludes
|
||||
non-global and metadata-capable IPv4 ranges. Private sources require matching
|
||||
application and NetworkPolicy CIDR allowances.
|
||||
|
||||
Containers run non-root with read-only roots, dropped capabilities, resource
|
||||
limits, and no service links. Worker temporary storage is bounded.
|
||||
|
||||
TLS ingress redirects HTTP to HTTPS and emits HSTS. The WebBFF session cookie
|
||||
is always `Secure`; unencrypted origins are unsupported outside loopback test
|
||||
environments.
|
||||
|
||||
## Operations
|
||||
|
||||
API and Worker expose liveness, dependency-aware readiness, and Prometheus
|
||||
|
|
|
|||
Loading…
Reference in a new issue