mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-26 14:30:13 +00:00
7396ec42bb
* feat(oauth): delegated install grants through the issuance barrier: code, PKCE, access and refresh for a live human member, the consented access bound and capped by the manifest (TASK-3399, U5b-1) * feat(oauth): delegated app sign-in: a fixed-workspace consent page, code+PKCE for install clients, the person's access choice in the grant, and an ownership check before fosite on code and refresh (TASK-3399, U5b-1) * feat(oauth): app grants are not MCP connections: the readers skip install chains, the console lists and revokes them through the binding, the sweep clears orphan bindings, and revoking a person reaches their delegated codes (TASK-3399, U5b-1) * test(server): the console lists an app grant on its own, refuses every connection mutation on it, and revokes it through the binding (TASK-3399, U5b-1) * test(server): the console revoke ends an app grant through its binding (TASK-3399, U5b-1) * feat(web): Connected apps lists installed apps acting as you, read-only plus revoke (TASK-3399, U5b-1) * test(oauth): pin the binding's person and a resource-less sign-in; name the fosite layers behind the consent gate's PKCE and audience checks (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r1: a delegated binding remembers the person's credential epoch and membership and is tombstoned on revoke; issuance share-locks the person on Postgres; app sign-in and grant management work with MCP off (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r2: only a delegated grant's code writes its binding; member removal, disable and claim end a person's delegated grants through their installs; users before installs before tokens in every path (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r3: delegated revocation takes no install lock (the person's row orders it against issuance) and writes child rows in teardown's order, closing two deadlocks with owner-account deletion (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r4: install teardown and a person's delegated revocation lock their bindings in request_id order before any child row (TASK-3399, U5b-1) * feat(store): bounded retry on a Postgres deadlock for delegated-grant revocation, member removal, account disable and claim, and the OAuth sweep, with a per-site metric (TASK-3399, U5b-1, lead ruling (a)) * fix(oauth): U5b-1 codex r6: the console does not list an app grant the install's epoch has moved past (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r7: a consent carries the person's credential epoch and the barrier refuses it after a disable/re-enable; the console lists a grant not yet exchanged (TASK-3399, U5b-1) * feat(oauth): delegated app sign-in stays closed (access_denied, not yet available) behind one named guard until U5b-2 opens it with the app API (TASK-3399, U5b-1, lead ruling) * fix(oauth): U5b-1 codex r8: a consent carries the install epoch and membership its gate read, so a disable/re-enable or removal/re-add after the gate refuses the code (TASK-3399, U5b-1) * fix(oauth): U5b-1 codex r9: a delegated grant needs a live workspace, at the consent gate and the barrier (TASK-3399, U5b-1) * docs(oauth): name the accepted one-second residual of the consent's membership check (TASK-3399, U5b-1) * test: census the delegated consent's form readers, the app-grant revoke's user agent, and four binding columns that carry no caller text (TASK-3399, U5b-1)
37 lines
2.3 KiB
SQL
37 lines
2.3 KiB
SQL
-- Migration 121: the consented access of a delegated app grant (SPEC-6 U5b,
|
|
-- TASK-3399).
|
|
--
|
|
-- A delegated token's access is the lesser of the manifest's
|
|
-- delegated.access and what the person chose on the consent page (DOC-3371
|
|
-- §4 step 4); the consent page offers read-only even when the manifest asks
|
|
-- for write, defaulting to it (lead ruling Q2). The choice is recorded on the
|
|
-- binding the issuance barrier writes, under the install-row lock, so every
|
|
-- token of the grant carries the same value. NULL on a service binding,
|
|
-- whose access is the install's service_access.
|
|
--
|
|
-- delegated_user_id is the person a delegated grant acts for, written by the
|
|
-- barrier at the grant's FIRST persistence, which is its authorization code:
|
|
-- the code table has no subject column, so without it a person's unexchanged
|
|
-- delegated code could be found by nothing but its session data. Disabling
|
|
-- or claiming the account deactivates those codes through it, and erasing
|
|
-- the account cascades the binding away (introspection then refuses every
|
|
-- token of the grant as unbound). NULL on a service binding.
|
|
ALTER TABLE app_token_bindings ADD COLUMN delegated_access TEXT;
|
|
ALTER TABLE app_token_bindings ADD COLUMN delegated_user_id TEXT REFERENCES users(id) ON DELETE CASCADE;
|
|
--
|
|
-- The barrier also remembers, for a delegated grant, what it was issued
|
|
-- against, so a change between two of its persistences is never forgotten
|
|
-- (codex U5b-1 r1):
|
|
-- - delegated_credential_epoch: the person's users.credential_epoch, which a
|
|
-- disable or an account claim bumps, so a disable and re-enable between
|
|
-- two persistences refuses the next one;
|
|
-- - delegated_member_since: the person's workspace_members.created_at, so a
|
|
-- removal and re-add between two persistences refuses the next one;
|
|
-- - revoked_at: the person's console revoke, a tombstone the barrier refuses
|
|
-- instead of a deleted binding it would take for a first issuance and
|
|
-- recreate. The sweep removes it with the grant's last row.
|
|
ALTER TABLE app_token_bindings ADD COLUMN delegated_credential_epoch INTEGER;
|
|
ALTER TABLE app_token_bindings ADD COLUMN delegated_member_since TEXT;
|
|
ALTER TABLE app_token_bindings ADD COLUMN revoked_at TEXT;
|
|
CREATE INDEX IF NOT EXISTS idx_app_token_bindings_delegated_user ON app_token_bindings (delegated_user_id);
|