fix(08): revise oauth plans after checker feedback

This commit is contained in:
Jakub Zych
2026-09-23 17:46:38 +02:00
parent 3c6a505c5f
commit e23cbac240
13 changed files with 444 additions and 197 deletions

View File

@@ -104,9 +104,9 @@ Existing serializer:
- Connected-app list is newest-first, owner-only, live OAuth tokens only, with manual count separate and no secret/client-id fields.
- Revoke of an owned OAuth token kills its refresh lineage; foreign, missing, and manual token IDs share the exact 404.
</behavior>
<action>D-04: and D-18: extend the RED suite with deterministic in-memory tests and synchronized real-Postgres contention tests for T-08-REFRESH-REPLAY, T-08-CROSS-USER, T-08-SCOPE-CEILING, T-08-REQUEST-LEAK, and T-08-SURFACE. D-16: cover the lifecycle sequence later recorded by parity. D-17: prove exact sweep retention. Include an assembled lifecycle that starts with the grant from 08-05, refreshes, replays the spent predecessor, verifies the new branch and access token are dead, creates another grant, lists it, revokes it, and proves refresh afterward fails. Use compiling stubs and separate `PHASE8_RED:lifecycle-framework` and `PHASE8_RED:lifecycle-app` assertions; reject syntax/build/setup/missing-test failures through the shared RED verifier. Assert exact UI response allow-lists and 404 bytes.</action>
<action>D-04 and D-18: extend the RED suite with deterministic in-memory tests and synchronized real-Postgres contention tests for T-08-REFRESH-REPLAY, T-08-CROSS-USER, T-08-SCOPE-CEILING, T-08-REQUEST-LEAK, and T-08-SURFACE. D-16: cover the lifecycle sequence later recorded by parity. D-17: prove exact sweep retention. Include an assembled lifecycle that starts with the grant from 08-05, refreshes, replays the spent predecessor, verifies the new branch and access token are dead, creates another grant, lists it, revokes it, and proves refresh afterward fails. Use compiling stubs and exact `TestPhase8RedLifecycleFramework`/`PHASE8_RED:lifecycle-framework` and `TestPhase8RedLifecycleApp`/`PHASE8_RED:lifecycle-app` triples under `go test -json`; reject every unexpected failing action/package/test, compile/setup/panic/no-test case, and missing/duplicate sentinel. Assert exact UI response allow-lists and 404 bytes.</action>
<verify>
<automated>scripts/check-phase8-red.sh lifecycle-framework go test ./wristband -run 'Test(Refresh|Replay|Sweep)' -count=1 &amp;&amp; scripts/check-phase8-red.sh lifecycle-app bash -lc "cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka/... -run 'TestOAuth(Refresh|Replay|Connected|Revoke|Sweep)' -count=1"</automated>
<automated>scripts/check-phase8-red.sh go PHASE8_RED:lifecycle-framework git.golem15.com/golem15/summercms/wristband TestPhase8RedLifecycleFramework -- go test -json ./wristband -run '^TestPhase8RedLifecycleFramework$' -count=1 &amp;&amp; scripts/check-phase8-red.sh go PHASE8_RED:lifecycle-app git.golem15.com/golem15/fonoteka/plugins/golem15/fonoteka TestPhase8RedLifecycleApp -- bash -lc "cd ../fonoteka.go &amp;&amp; go test -json ./plugins/golem15/fonoteka -run '^TestPhase8RedLifecycleApp$' -count=1"</automated>
</verify>
<acceptance_criteria>
- Tests include sequential replay, a barrier-synchronized double refresh, committed lineage kill, expiry retention, owner isolation, manual-token exclusion, list ordering, and post-revoke refresh failure.
@@ -140,7 +140,7 @@ Existing serializer:
<acceptance_criteria>
- Normal refresh and sequential/concurrent replay tests pass under real Postgres.
- A spent-token replay leaves every lineage refresh row and its live access token revoked after the response transaction commits.
- `go test -race ./wristband` passes and the app contention test produces a single usable branch.
- The focused app contention test produces a single usable branch; full race execution is reserved for 08-10's final checkpoint.
- Sweep tests prove expired rows are removed and unexpired rotated/revoked rows remain.
</acceptance_criteria>
<done>Refresh rotation is atomic, preserves replay evidence, and commits whole-lineage revocation before emitting the protocol error.</done>
@@ -164,7 +164,7 @@ Existing serializer:
</behavior>
<action>Per D-08 and the UI-SPEC, add GET and DELETE connected-app controllers in the JWT group. Reuse `serializeToken`; append only the sanitized/truncated client name, initialize collection/scope arrays as arrays, count live manual tokens separately, and order OAuth tokens newest first. Scope every query by `bouncer.User`. For DELETE, require an owned OAuth token, invoke the wristband lineage-revoke operation in the same committed transaction, and collapse missing/foreign/manual IDs to exact `{"error":"Token not found"}` 404. Mount only under `/_fonoteka/api/v1/oauth`; do not expose these routes on the personal-token or raw groups.</action>
<verify>
<automated>cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka/... -run 'TestOAuth(ConnectedApps|Revoke|Lifecycle|Surface)' -count=1</automated>
<automated>(cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka/controllers/api ./plugins/golem15/fonoteka -run '^TestOAuth(ConnectedApps|Revoke|Lifecycle|Surface)$' -count=1)</automated>
</verify>
<acceptance_criteria>
- Empty, populated, manual-count, newest-first, foreign/manual 404, and successful atomic revoke tests pass with exact bytes.
@@ -200,8 +200,8 @@ Existing serializer:
<verification>
- `go test ./wristband -run 'Test(Refresh|Replay|Sweep)' -count=1`
- `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/... -run 'TestOAuth(Refresh|Replay|ConnectedApps|Revoke|Lifecycle|Surface)' -count=1`
- `cd ../fonoteka.go && go test -race ./plugins/golem15/fonoteka/classes/auth ./plugins/golem15/fonoteka`
- `cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/classes/auth ./plugins/golem15/fonoteka/controllers/api ./plugins/golem15/fonoteka -run '^TestOAuth(Refresh|Replay|ConnectedApps|Revoke|Lifecycle|Surface)$' -count=1`
- Complete race execution is reserved exclusively for Plan 08-10's final blocking checkpoint.
</verification>
<success_criteria>