Bind the Session cookie to the App and enforce per-App client uniqueness #58

Open
opened 2026-09-19 01:23:16 +00:00 by ginjiruu · 0 comments
Owner

Context

Security architecture review, 18 Sep 2026 (candidate 4, "Worth exploring"). The Session blob is {id_token, refresh_token} with no appKey (the State blob carries one). Cross-App Session reuse is prevented today only by per-App issuer+audience verification: App A's id_token carries aud = A's clientID, so it fails verification under App B. That defense holds only while ADR 0002's invariant (every App owns its own Client) holds — but the builder enforces hostname uniqueness and secret-ref well-formedness and never enforces clientID uniqueness. Two Apps configured with the same clientID + issuer (a misconfiguration, or a shared IdP tenant) make App A's Session cookie — including its refresh_token — fully valid on App B. The per-App security boundary silently degrades to per-clientID.

What to build

Two small, mutually reinforcing changes:

  1. Carry the appKey in the Session blob and check it in Check against the Host-resolved App, mirroring how the callback resolves the App from the State cookie. Pre-existing cookies without an appKey fail closed to re-login — the same shape as a Cookie-key rotation (one re-login, by design).
  2. Add a structural gate to the pure Build: duplicate clientID across Apps → both Degraded, exactly like the hostname-conflict gate. The binding makes the per-App invariant explicit in the data; the gate makes it impossible to configure away.

Acceptance criteria

  • The Session blob carries appKey; Check rejects a Session whose appKey does not match the Host-resolved App (302 re-login, fail-closed)
  • Pre-existing cookies (no appKey) fail closed to re-login, not to an error status
  • The builder gate: two OauthClients sharing a clientID → both Degraded with a clear reason (same shape as hostname conflicts)
  • Tests: cross-App Session rejection through the Check path; duplicate-clientID build through the pure Build interface
  • Existing OIDC flow tests stay green

Blocked by

  • None — can start immediately
## Context Security architecture review, 18 Sep 2026 (candidate 4, "Worth exploring"). The Session blob is `{id_token, refresh_token}` with no appKey (the State blob carries one). Cross-App Session reuse is prevented today only by per-App issuer+audience verification: App A's id_token carries `aud` = A's clientID, so it fails verification under App B. That defense holds only while ADR 0002's invariant (every App owns its own Client) holds — but the builder enforces hostname uniqueness and secret-ref well-formedness and never enforces clientID uniqueness. Two Apps configured with the same clientID + issuer (a misconfiguration, or a shared IdP tenant) make App A's Session cookie — including its refresh_token — fully valid on App B. The per-App security boundary silently degrades to per-clientID. ## What to build Two small, mutually reinforcing changes: 1. Carry the appKey in the Session blob and check it in Check against the Host-resolved App, mirroring how the callback resolves the App from the State cookie. Pre-existing cookies without an appKey fail closed to re-login — the same shape as a Cookie-key rotation (one re-login, by design). 2. Add a structural gate to the pure Build: duplicate clientID across Apps → both Degraded, exactly like the hostname-conflict gate. The binding makes the per-App invariant explicit in the data; the gate makes it impossible to configure away. ## Acceptance criteria - [ ] The Session blob carries appKey; Check rejects a Session whose appKey does not match the Host-resolved App (302 re-login, fail-closed) - [ ] Pre-existing cookies (no appKey) fail closed to re-login, not to an error status - [ ] The builder gate: two OauthClients sharing a clientID → both Degraded with a clear reason (same shape as hostname conflicts) - [ ] Tests: cross-App Session rejection through the Check path; duplicate-clientID build through the pure Build interface - [ ] Existing OIDC flow tests stay green ## Blocked by - None — can start immediately
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lab/authz-bridge#58
No description provided.