# StellaOps Console Security Posture > **Audience:** Security Guild, Console & Authority teams, deployment engineers. > **Scope:** OIDC/DPoP flows, scope model, session controls, CSP and transport headers, evidence handling, offline posture, and monitoring expectations for the StellaOps Console (Sprint 23). The console is an Angular SPA fronted by the StellaOps Web gateway. It consumes Authority for identity, Concelier/Excititor for aggregation data, Policy Engine for findings, and Attestor for evidence bundles. This guide captures the security guarantees and required hardening so that the console can ship alongside the Aggregation-Only Contract (AOC) without introducing new attack surface. --- ## 1 · Identity & Authentication ### 1.1 Authorization sequence 1. Browser→Authority uses **OAuth 2.1 Authorization Code + PKCE** (`S256`). 2. Upon code exchange the console requests a **DPoP-bound access token** (`aud=console`, `tenant=`) with **120 s TTL** and optional **rotating refresh token** (`rotate=true`). 3. Authority includes `cnf.jkt` for the ephemeral WebCrypto keypair; console stores the private key in **IndexedDB** (non-exportable) and keeps the public JWK in memory. 4. All API calls attach `Authorization: Bearer ` + `DPoP` proof header. Nonces from the gateway are replay-protected (`dpopt-nonce` header). 5. Tenanted API calls flow through the Web gateway which forwards `X-Stella-Tenant` and enforces tenancy headers. Missing or mismatched tenants trigger `403` with `ERR_TENANT_MISMATCH`. ### 1.2 Fresh-auth gating - Sensitive actions (tenant edits, token revocation, policy promote, signing key rotation) call `Authority /fresh-auth` using `prompt=login` + `max_age=300`. - Successful fresh-auth yields a **300 s** scoped token (`fresh_auth=true`) stored only in memory; the UI disables guarded buttons when the timer expires. - Audit events: `authority.fresh_auth.start`, `authority.fresh_auth.success`, `authority.fresh_auth.expired` (link to correlation IDs for the gated action). ### 1.3 Offline & sealed mode - When `console.offlineMode=true` the console presents an offline banner and suppresses fresh-auth prompts, replacing them with CLI guidance (`stella auth fresh-auth --offline`). - Offline mode requires pre-issued tenant-scoped tokens bundled with the Offline Kit; tokens must include `offline=true` claim and 15 m TTL. - Authority availability health is polled via `/api/console/status`. HTTP failures raise the offline banner and switch to read-only behaviour. --- ## 2 · Session & Device Binding - Access and refresh tokens live in memory; metadata (subject, tenant, expiry) persists in `sessionStorage` for reload continuity. **Never** store raw JWTs in `localStorage`. - Inactivity timeout defaults to **15 minutes**. Idle sessions trigger silent refresh; on failure the UI shows a modal requiring re-auth. - Tokens are device-bound through DPoP; if a new device logs in, Authority revokes the previous DPoP key and emits `authority.token.binding_changed`. - CSRF mitigations: bearer tokens plus DPoP remove cookie reliance. If cookies are required (e.g., same-origin analytics) they must be `HttpOnly`, `SameSite=Lax`, `Secure`. - Browser hardening: enforce `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `Permissions-Policy: camera=(), microphone=(), geolocation=()`. --- ## 3 · Authorization & Scope Model The console client is registered in Authority as `console-ui` with scopes: | Feature area | Required scopes | Notes | |--------------|----------------|-------| | Base navigation (Dashboard, Findings, SBOM, Runs) | `ui.read`, `findings:read`, `advisory:read`, `vex:read`, `aoc:verify` | `findings:read` enables Policy Engine overlays; `advisory:read`/`vex:read` load ingestion panes; `aoc:verify` allows on-demand guard runs. | | Admin workspace | `ui.admin`, `authority:tenants.read`, `authority:tenants.write`, `authority:roles.read`, `authority:roles.write`, `authority:tokens.read`, `authority:tokens.revoke`, `authority:clients.read`, `authority:clients.write`, `authority:audit.read` | Scope combinations are tenant constrained. Role changes require fresh-auth. | | Policy approvals | `policy:read`, `policy:review`, `policy:approve`, `policy:operate`, `policy:simulate` | `policy:operate` (promote/activate/run) requires fresh-auth. | | Observability panes (status ticker, telemetry) | `ui.telemetry`, `scheduler:runs.read`, `advisory:read`, `vex:read` | `ui.telemetry` drives OTLP export toggles. | | Orchestrator dashboard (queues, workers, rate limits) | `orch:read` | Provision via `Orch.Viewer` role; read-only access to job state and telemetry. | | Orchestrator control actions (pause/resume, retry, sync-now, backfill) | `orch:operate` (plus `orch:read`) | CLI/Console must request tokens with `operator_reason` and `operator_ticket`; Authority denies issuance when either value is missing. | | Downloads parity (SBOM, attestation) | `downloads:read`, `attestation:verify`, `sbom:export` | Console surfaces digests only; download links require CLI parity for write operations. | Guidance: - **Role mapping**: Provision Authority role `role/ui-console-admin` encapsulating the admin scopes above. - **Orchestrator viewers**: Assign Authority role `role/orch-viewer` (Authority role string `Orch.Viewer`) to consoles that require read-only access to Orchestrator telemetry. - **Orchestrator operators**: Assign Authority role `role/orch-operator` (Authority role string `Orch.Operator`) to identities allowed to pause/resume or backfill. Tokens must include `operator_reason` (≤256 chars) and `operator_ticket` (≤128 chars); Authority records the values in audit logs. - **Tenant enforcement**: Gateway injects `X-Stella-Tenant` from token claims. Requests missing the header must be rejected by downstream services (Concelier, Excititor, Policy Engine) and logged. - **Separation of duties**: Never grant `ui.admin` and `policy:approve`/`policy:operate` to the same human role without SOC sign-off; automation accounts should use least-privilege dedicated clients. --- ### 3.1 Console Authority endpoints Console uses dedicated Authority endpoints scoped under `/console/*`. All requests must include the tenant header injected by the gateway (`X-Stella-Tenant`); calls without the header fail with `tenant_header_missing` and emit a structured audit event. Keep reverse proxies configured to pass the header end-to-end. | Endpoint | Required scopes | Purpose | Notes | |----------|-----------------|---------|-------| | `GET /console/tenants` | `authority:tenants.read` | Returns the tenant catalogue for the authenticated principal. | Validates `X-Stella-Tenant`; rejects tenants not configured in Authority. | | `GET /console/profile` | `ui.read` | Surfaces subject metadata (roles, scopes, session id, fresh-auth state). | Response includes `freshAuth` (bool) based on a 300 s window since `auth_time`. | | `POST /console/token/introspect` | `ui.read` | Introspects the access token currently in use and reports expiry + tenant. | Console polls this endpoint to drive session inactivity prompts; intended for SPA usage via fetch POST. | **Fresh-auth & session inactivity:** Authority stamps `auth_time` on issued tokens and considers privileged actions “fresh” for five minutes. When `/console/profile` returns `freshAuth: false`, the UI must require an interactive re-authentication before allowing admin operations (`ui.admin`, `authority:*` mutations, `policy:activate`, `exceptions:approve`). Access tokens remain short-lived (`00:02:00` by default); pair this with Console session timeouts so idle dashboards prompt the user before two minutes of inactivity. **DPoP + tenant binding:** All `/console/*` endpoints require DPoP-bound access tokens. Audit events include `tenant.resolved`, `scope`, `correlationId`, and (when applicable) `token.expires_at`. Staple the same headers into downstream services so cross-component troubleshooting uses the same correlation identifiers. --- ## 4 · Transport, CSP & Browser Hardening ### 4.1 Gateway requirements - TLS 1.2+ with modern cipher suites; enable HTTP/2 for SSE streams. - Terminate TLS at the reverse proxy (Traefik, NGINX) and forward `X-Forwarded-*` headers (`ASPNETCORE_FORWARDEDHEADERS_ENABLED=true`). - Rate-limit `/authorize` and `/token` according to [Authority rate-limit guidance](rate-limits.md). ### 4.2 Content Security Policy Default CSP served by the console container: ``` default-src 'self'; connect-src 'self' https://*.stella-ops.local; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-ancestors 'none'; ``` Recommendations: - Extend `connect-src` only for known internal APIs (e.g., telemetry collector). Use `console.config.cspOverrides` instead of editing NGINX directly. - Enable **COOP/COEP** (`Cross-Origin-Opener-Policy: same-origin`, `Cross-Origin-Embedder-Policy: require-corp`) to support WASM policy previews. - Use **Subresource Integrity (SRI)** hashes when adding third-party fonts or scripts. - For embedded screenshots/GIFs sourced from Offline Kit, use `img-src 'self' data: blob:` and verify assets during build. - Enforce `X-Frame-Options: DENY`, `X-XSS-Protection: 0`, and `Cache-Control: no-store` on JSON API responses (HTML assets remain cacheable). ### 4.3 SSE & WebSocket hygiene - SSE endpoints (`/console/status/stream`, `/console/runs/{id}/events`) must set `Cache-Control: no-store` and disable proxy buffering. - Gate SSE behind the same DPoP tokens; reject without `Authorization`. - Proxy timeouts ≥ 60 s to avoid disconnect storms; clients use exponential backoff with jitter. --- ## 5 · Evidence & Data Handling - **Evidence bundles**: Download links trigger `attestor.verify` or `downloads.manifest` APIs. The UI never caches bundle contents; it only surfaces SHA-256 digests and cosign signatures. Operators must use CLI to fetch the signed artefact. - **Secrets**: UI redacts tokens, emails, and attachment paths in logs. Structured logs include only `subject`, `tenant`, `action`, `correlationId`. - **Aggregation data**: Console honours Aggregation-Only contract—no client-side rewriting of Concelier/Excititor precedence. Provenance badges display source IDs and merge-event hashes. - **PII minimisation**: User lists show minimal identity (display name, email hash). Full email addresses require `ui.admin` + fresh-auth. - **Downloads parity**: Every downloadable artefact includes a CLI parity link (e.g., `stella downloads fetch --artifact `). If CLI parity fails, the console displays a warning banner and links to troubleshooting docs. --- ## 6 · Logging, Monitoring & Alerts - Structured logs: `ui.action`, `tenantId`, `subject`, `scope`, `correlationId`, `dpop.jkt`. Log level `Information` for key actions; `Warning` for security anomalies (failed DPoP, tenant mismatch). - Metrics (Prometheus): `ui_request_duration_seconds`, `ui_dpop_failure_total`, `ui_fresh_auth_prompt_total`, `ui_tenant_switch_total`, `ui_offline_banner_seconds`. - Alerts: 1. **Fresh-auth failures** > 5 per minute per tenant → security review. 2. **DPoP mismatches** sustained > 1 % of requests → potential replay attempt. 3. **Tenant mismatches** > 0 triggers an audit incident (could indicate scope misconfiguration). - Correlate with Authority audit events (`authority.scope.granted`, `authority.token.revoked`) and Concelier/Excititor ingestion logs to trace user impact. --- ## 7 · Offline & Air-Gapped Posture - Offline deployments require mirrored container images and Offline Kit manifest verification (see `/docs/deploy/console.md` §7). - Console reads `offlineManifest.json` at boot to validate asset digests; mismatches block startup until the manifest is refreshed. - Tenant and role edits queue change manifests for export; UI instructs operators to run `stella auth apply --bundle ` on the offline Authority host. – Evidence viewing remains read-only; download buttons provide scripts to export from local Attestor snapshots. - Fresh-auth prompts display instructions for hardware-token usage on bastion hosts; system logs mark actions executed under offline fallback. --- ## 8 · Threat Model Alignment | Threat (Authority TM §5) | Console control | |--------------------------|-----------------| | Spoofed revocation bundle | Console verifies manifest signatures before showing revocation status; links to `stella auth revoke verify`. | | Parameter tampering on `/token` | PKCE + DPoP enforced; console propagates correlation IDs so Authority logs can link anomalies. | | Bootstrap invite replay | Admin UI surfaces invite status with expiry; fresh-auth required before issuing new invites. | | Token replay by stolen agent | DPoP binding prevents reuse; console surfaces revocation latency warnings sourced from Zastava metrics. | | Offline bundle tampering | Console refuses unsigned Offline Kit assets; prompts operators to re-import verified bundles. | | Privilege escalation via plug-in overrides | Plug-in manifest viewer warns when a plug-in downgrades password policy; UI restricts plug-in activation to fresh-auth + `ui.admin` scoped users. | Document gaps and remediation hooks in `SEC5.*` backlog as they are addressed. --- ## 9 · Compliance checklist - [x] Authority client `console-ui` registered with PKCE, DPoP, tenant claim requirement, and scopes from §3. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#authority-client-validation)) - [x] CSP enforced per §4 with overrides documented in deployment manifests. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#csp-enforcement)) - [x] Fresh-auth timer (300 s) validated for admin and policy actions; audit events captured. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#fresh-auth-timer)) - [x] DPoP binding tested (replay attempt blocked; logs show `ui_dpop_failure_total` increment). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#dpop-binding-test)) - [x] Offline mode exercises performed (banner, CLI guidance, manifest verification). (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#offline-mode-exercise)) - [x] Evidence download parity verified with CLI scripts; console never caches sensitive artefacts. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#evidence-parity)) - [x] Monitoring dashboards show metrics and alerts outlined in §6; alert runbooks reviewed with Security Guild. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#monitoring--alerts)) - [x] Security review sign-off recorded in sprint log with links to Authority threat model references. (see [console security sign-off](../updates/2025-10-27-console-security-signoff.md#sign-off)) - [x] `/console` Authority endpoints validated for tenant header enforcement, fresh-auth prompts, and introspection flows (Audit IDs `authority.console.tenants.read`, `authority.console.profile.read`, `authority.console.token.introspect`). (see [console security sign-off](../updates/2025-10-31-console-security-refresh.md)) --- *Last updated: 2025-10-31 (Sprint 23).*