mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-05 13:02:04 +00:00
Fold in Gemini (round-3, third model family) auth findings
- Gemini-BLOCK-1: AllowedOAuthScopes strictly filtering a V2 pre-token Lambda's output is unverified -> fatal Phase-0a check (§6 #8a); if it fails, pre-token fail-closed becomes the primary boundary. - Gemini-BLOCK-2: Cognito access tokens carry client_id/scopes, not native aud -> align §1h + facade/server checks to client_id allow-list / scope-prefix audience proxy. - Gemini-Q: 0a spike must run on production-equivalent Enterprise Grid + real licenses. - Gemini-NIT (path-corrected): project memory is a private store at ~/.claude/.../memory/, not the repo and not Gemini's own ~/.gemini path. Three model families now converge: structural plan sound; only open risk is the auth token-mint mechanism, fully spike-gated in Phase 0a.
This commit is contained in:
parent
70987522b2
commit
9cf124a24b
1 changed files with 45 additions and 14 deletions
|
|
@ -144,8 +144,13 @@ credential that requests **only its tier's scopes**. Mechanism (specified, testa
|
||||||
Finance client → `finance:read`; Lauren Exec client → `ops:read ops:tasks gmail:self calendar:self` (**never
|
Finance client → `finance:read`; Lauren Exec client → `ops:read ops:tasks gmail:self calendar:self` (**never
|
||||||
finance**). Cognito's token endpoint **cannot issue a scope outside a client's `AllowedOAuthScopes`** — so even
|
finance**). Cognito's token endpoint **cannot issue a scope outside a client's `AllowedOAuthScopes`** — so even
|
||||||
though Lauren-the-person holds `finance:read` (via `-finance@`), the Exec client can never obtain a token
|
though Lauren-the-person holds `finance:read` (via `-finance@`), the Exec client can never obtain a token
|
||||||
carrying it. This is the boundary — it does NOT depend on the pre-token Lambda, and it is independent of
|
carrying it. This is the intended boundary — independent of design.md §2.3's group→scope union.
|
||||||
design.md §2.3's group→scope union.
|
**CAVEAT (Gemini round-3 BLOCK — UNVERIFIED, fatal Phase-0a check):** this rests on Cognito **strictly
|
||||||
|
filtering the issued token's scopes to the client's `AllowedOAuthScopes` even when a pre-token V2 Lambda
|
||||||
|
runs**. In some Cognito configs a V2 pre-token Lambda can *return scopes exceeding* the client's allow-list — if
|
||||||
|
so, the app-client control is soft and the pre-token Lambda becomes load-bearing after all. Verify "issued
|
||||||
|
scopes ⊆ `AllowedOAuthScopes` regardless of Lambda output" as a **fatal** 0a gate item (§6 #8); if it does NOT
|
||||||
|
hold, the pre-token fail-closed suppression below is promoted from backstop to primary and gets the heavier test.
|
||||||
- **AMENDS design.md §2.3 (BLOCK-1 / F2).** §2.3 describes a plain **union** of a user's group-scopes (and even
|
- **AMENDS design.md §2.3 (BLOCK-1 / F2).** §2.3 describes a plain **union** of a user's group-scopes (and even
|
||||||
shows Lauren's token *with* `finance:read`); the substrate is ambiguous on per-audience subsetting. This plan
|
shows Lauren's token *with* `finance:read`); the substrate is ambiguous on per-audience subsetting. This plan
|
||||||
**amends** §2.3: group→scope mapping still defines what a user *may* hold, but the **issued token is bounded by
|
**amends** §2.3: group→scope mapping still defines what a user *may* hold, but the **issued token is bounded by
|
||||||
|
|
@ -219,6 +224,22 @@ and FIX is addressed below; this revision supersedes the pre-audit text wherever
|
||||||
flip-point clarified as operational re-announce. **Q1** fallback-scope honesty + **Q2** freshness-bound
|
flip-point clarified as operational re-announce. **Q1** fallback-scope honesty + **Q2** freshness-bound
|
||||||
mechanism documented.
|
mechanism documented.
|
||||||
|
|
||||||
|
**Round 3 (Gemini `scanner`, third model family — 2026-06-11).** Triangulated the auth nerve all three families
|
||||||
|
flagged; two sharp BLOCKs folded in:
|
||||||
|
- **Gemini-BLOCK-1:** `AllowedOAuthScopes` strictly filtering a V2 pre-token Lambda's output is **unverified** —
|
||||||
|
added as a fatal Phase-0a check (§6 #8a); if it doesn't hold, pre-token fail-closed becomes the primary boundary.
|
||||||
|
(Sharpens BLOCK-1: neither layer is *independently* guaranteed until this interaction is proven.)
|
||||||
|
- **Gemini-BLOCK-2:** Cognito access tokens carry `client_id`/scopes, **not a native `aud`** — aligned §1h and the
|
||||||
|
facade/server checks to a **`client_id` allow-list / scope-prefix audience proxy** (§0.1, §1h, §6 #8c).
|
||||||
|
- **Gemini-Q:** the 0a spike must run on a **production-equivalent Enterprise Grid org with real licenses**, not a
|
||||||
|
Dev/non-Grid Slack (false-positive risk) — §6 #6.
|
||||||
|
- **Gemini-NIT (path-corrected):** project memory is a private store at `~/.claude/projects/.../memory/`, distinct
|
||||||
|
from repo READMEs — §4. (Gemini cited its own `~/.gemini/...` path; corrected — a cross-family infra-fact
|
||||||
|
assertion to verify, not adopt, per `feedback_fable_review_gates`.)
|
||||||
|
Gemini APPROVED the rest: B1/B5 parallel-run dual-feed + Bolt fallback sound; §1a identity segregation coherently
|
||||||
|
integrated with the token-layer mechanism. **All three families (Claude/Fable, GPT-4.1, Gemini) now converge: the
|
||||||
|
structural plan is sound; the only open risk is the auth token-mint mechanism, fully spike-gated in Phase 0a.**
|
||||||
|
|
||||||
**Cross-family review (GPT-4.1 `cross_reviewer`, 2026-06-11) — auth/trifecta sections.** Run per the mandatory
|
**Cross-family review (GPT-4.1 `cross_reviewer`, 2026-06-11) — auth/trifecta sections.** Run per the mandatory
|
||||||
cross-review gate for auth/IAM design (Fable is Claude-family, not a substitute). Findings folded in:
|
cross-review gate for auth/IAM design (Fable is Claude-family, not a substitute). Findings folded in:
|
||||||
- **CR-1 (BLOCK):** validate `aud` at the edge **AND** server-side (defense in depth) — the per-tier authorizer
|
- **CR-1 (BLOCK):** validate `aud` at the edge **AND** server-side (defense in depth) — the per-tier authorizer
|
||||||
|
|
@ -436,9 +457,11 @@ real home:
|
||||||
deprecating each bot (design.md §6, §7.3 parity gate).
|
deprecating each bot (design.md §6, §7.3 parity gate).
|
||||||
|
|
||||||
**Core security tests (authoritative, transport-agnostic, in `sh-mcp`, design.md §7.3):**
|
**Core security tests (authoritative, transport-agnostic, in `sh-mcp`, design.md §7.3):**
|
||||||
**audience-binding rejection at the per-tier facade AND re-validated server-side** (an `aud=sh-mcp-ops` token
|
**audience-binding rejection at the per-tier facade AND re-validated server-side** — bound on the chosen
|
||||||
rejected by the `sh-mcp-finance` gateway authorizer *and* by the finance server itself — B3/CR-1, defense in
|
audience proxy (**`client_id` allow-list per gateway, or resource-server scope-prefix**, since Cognito access
|
||||||
depth); per-tool **server-side** scope enforcement; **per-agent credential mints only its tier's scopes** (a
|
tokens carry `client_id`/scopes, **not a native `aud` claim** unless injected via pre-token V2 — Gemini round-3):
|
||||||
|
an ops-tier token is rejected by the `sh-mcp-finance` gateway authorizer *and* by the finance server itself
|
||||||
|
(B3/CR-1, defense in depth); per-tool **server-side** scope enforcement; **per-agent credential mints only its tier's scopes** (a
|
||||||
token obtained via the Lauren Exec app client never contains `finance:read`, even though the person holds it —
|
token obtained via the Lauren Exec app client never contains `finance:read`, even though the person holds it —
|
||||||
B4); **pre-token fails closed on a cross-audience scope** (CR-3); **a finance-audience token cannot reach a
|
B4); **pre-token fails closed on a cross-audience scope** (CR-3); **a finance-audience token cannot reach a
|
||||||
Gmail/Calendar tool** (CR-6); **backend rejects a token whose `sub` is not a valid Google Workspace user** (CR-2);
|
Gmail/Calendar tool** (CR-6); **backend rejects a token whose `sub` is not a valid Google Workspace user** (CR-2);
|
||||||
|
|
@ -694,9 +717,12 @@ MCP layer) are unchanged — those stay locked.
|
||||||
**Repo READMEs & memory (F6 — global-instruction obligations, were omitted):**
|
**Repo READMEs & memory (F6 — global-instruction obligations, were omitted):**
|
||||||
- `sh-mcp` README updated to **OpenAPI-first** (servers expose OpenAPI; MCP adapter deferred).
|
- `sh-mcp` README updated to **OpenAPI-first** (servers expose OpenAPI; MCP adapter deferred).
|
||||||
- `sh-agentforce` README created (agent metadata repo, `cd-sfdx`, scratch-org flow).
|
- `sh-agentforce` README created (agent metadata repo, `cd-sfdx`, scratch-org flow).
|
||||||
- **Project memory:** create `project_sh_agentforce` before the repo-creating conversation ends; update
|
- **Project memory (PRIVATE memory store, NOT repo files — Gemini round-3 NIT, path-corrected):** these live in
|
||||||
`project_sh_mcp` for the transport/auth changes; add **cross-project references** `sh-mcp` ↔ `sh-agentforce`
|
`~/.claude/projects/-Users-adammoussa-Documents-repositories/memory/` (the Sea Haven memory location — **not**
|
||||||
↔ `project_seahaven_slack_bot` ↔ `project_exec_aide` (each side links the other).
|
any repo, and not Gemini's own `~/.gemini/...` path, which it incorrectly cited). Create `project_sh_agentforce`
|
||||||
|
before the repo-creating conversation ends; update `project_sh_mcp` for the transport/auth changes; add
|
||||||
|
**cross-project references** `sh-mcp` ↔ `sh-agentforce` ↔ `project_seahaven_slack_bot` ↔ `project_exec_aide`.
|
||||||
|
Keep this distinct from the repo-side READMEs above.
|
||||||
|
|
||||||
**Monitoring (N2 — ALARM-state-only convention, design.md alarm preferences):**
|
**Monitoring (N2 — ALARM-state-only convention, design.md alarm preferences):**
|
||||||
- Each per-tier **API Gateway facade**: 5xx-rate, auth-failure-spike, and **wrong-audience-token** alarms (the
|
- Each per-tier **API Gateway facade**: 5xx-rate, auth-failure-spike, and **wrong-audience-token** alarms (the
|
||||||
|
|
@ -804,15 +830,20 @@ Groups to reconcile the two control planes (G11, recommend SCIM).
|
||||||
must run as scripted per-user sessions instead of batch — decide before Phase 1.
|
must run as scripted per-user sessions instead of batch — decide before Phase 1.
|
||||||
6. **(Slack plan + licensing, Q1/Q2/G15)** Confirm Sea Haven's Slack plan supports **Agentforce Employee Agents**,
|
6. **(Slack plan + licensing, Q1/Q2/G15)** Confirm Sea Haven's Slack plan supports **Agentforce Employee Agents**,
|
||||||
and whether the all-staff Ops agent needs a provisioned Agentforce **user + license per employee** (prices G9,
|
and whether the all-staff Ops agent needs a provisioned Agentforce **user + license per employee** (prices G9,
|
||||||
feeds G11 SCIM scope).
|
feeds G11 SCIM scope). **Run this on a production-equivalent Enterprise Grid org with real Agentforce licenses
|
||||||
|
(Gemini round-3) — a Developer Org / non-Grid Slack can false-positive on Employee Agent support.**
|
||||||
7. **(Agent variables, Q3/G15)** Confirm Agentforce exposes **`$User.GoogleGroups`** and **`$Session.Channel`** to
|
7. **(Agent variables, Q3/G15)** Confirm Agentforce exposes **`$User.GoogleGroups`** and **`$Session.Channel`** to
|
||||||
agent instructions. If not, design an alternate enforcement for Lauren Exec's DM-only and per-scope gating
|
agent instructions. If not, design an alternate enforcement for Lauren Exec's DM-only and per-scope gating
|
||||||
(e.g. a context Apex action that returns channel + group facts) before Phase 1/3.
|
(e.g. a context Apex action that returns channel + group facts) before Phase 1/3.
|
||||||
8. **(Cognito `aud`/authorizer mechanism, BLOCK-1 — on the 0a/0b gate)** Confirm the **pre-token V2** access-token
|
8. **(Cognito token-mint mechanism, BLOCK-1 + Gemini round-3 — FATAL on the 0a gate)** Three things to prove in
|
||||||
customization trigger is available on our Cognito plan, and pick + prove the concrete per-tier rejection
|
the 0a throwaway kit before 0b builds real facades:
|
||||||
mechanism: **scope-prefix check**, **`client_id` allow-list per gateway**, or **custom `aud` via pre-token V2**.
|
(a) **Confirm `AllowedOAuthScopes` strictly filters the issued token** — i.e. issued scopes ⊆ the client's
|
||||||
The B3 edge rejection and B4 fail-closed both depend on this; Cognito access tokens do not carry a per-
|
allow-list **even when a pre-token V2 Lambda runs** (Gemini BLOCK). If a V2 Lambda can widen beyond the
|
||||||
resource-server `aud` by default. Do this in the 0a throwaway kit before 0b builds the real facades.
|
allow-list, the app-client control is soft → promote pre-token fail-closed suppression to primary.
|
||||||
|
(b) **Confirm the `pre-token V2` trigger is available** on our Cognito plan (it may be a paid feature).
|
||||||
|
(c) **Pick + prove the per-tier rejection mechanism** — `client_id` allow-list per gateway, resource-server
|
||||||
|
scope-prefix, or custom `aud` via V2 — since Cognito access tokens carry no native per-resource-server `aud`.
|
||||||
|
The B3 edge rejection, B4 boundary, and §1h audience tests all depend on (a)–(c).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue