docs(08-06): complete lifecycle and sweeps plan
This commit is contained in:
@@ -345,7 +345,7 @@ Plans:
|
||||
|
||||
**Wave 6** *(blocked on 08-05)*
|
||||
|
||||
- [ ] 08-06-PLAN.md — Rotate refresh grants, kill replayed lineages, and manage connected apps
|
||||
- [x] 08-06-PLAN.md — Rotate refresh grants, kill replayed lineages, and manage connected apps
|
||||
|
||||
**Wave 7** *(parallel; blocked on 08-06)*
|
||||
|
||||
@@ -496,7 +496,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
||||
| 5. Data layer full fidelity | 6/6 | Complete | 2026-09-18 |
|
||||
| 6. HTTP routing, auth groups and rate limiting | 14/14 | Complete | 2026-09-21 |
|
||||
| 7. User plugin and authentication | 8/8 | Complete | 2026-09-23 |
|
||||
| 8. OAuth2.1 authorization server | 5/10 | In Progress| |
|
||||
| 8. OAuth2.1 authorization server | 6/10 | In Progress| |
|
||||
| 9. Backend admin authentication and schema pipeline | 0/TBD | Not started | - |
|
||||
| 10. Admin Vue SPA | 0/TBD | Not started | - |
|
||||
| 11. Jobs, realtime and search infrastructure | 0/TBD | Not started | - |
|
||||
|
||||
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: executing
|
||||
stopped_at: Completed 08-05-PLAN.md
|
||||
last_updated: "2026-09-23T19:11:27.527Z"
|
||||
stopped_at: Completed 08-06-PLAN.md
|
||||
last_updated: "2026-09-23T19:42:20.033Z"
|
||||
last_activity: 2026-09-23
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 7
|
||||
total_plans: 55
|
||||
completed_plans: 50
|
||||
completed_plans: 51
|
||||
percent: 47
|
||||
---
|
||||
|
||||
@@ -26,11 +26,11 @@ See: .planning/PROJECT.md (updated 2026-09-16)
|
||||
## Current Position
|
||||
|
||||
Phase: 08 (oauth2-1-authorization-server) — EXECUTING
|
||||
Plan: 6 of 10
|
||||
Plan: 7 of 10
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-09-23
|
||||
|
||||
Progress: [█████████░] 91%
|
||||
Progress: [█████████░] 93%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -96,6 +96,7 @@ Progress: [█████████░] 91%
|
||||
| Phase 08 P03 | 20min | 2 tasks | 6 files |
|
||||
| Phase 08 P04 | 15min | 2 tasks | 5 files |
|
||||
| Phase 08 P05 | 55min | 3 tasks | 12 files |
|
||||
| Phase 08 P06 | 50min | 3 tasks | 10 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -233,6 +234,10 @@ Recent decisions affecting current work:
|
||||
- [Phase 08]: [Phase 08 P05] Consent scope ordering follows auth.MintableScopes's declared read/write/ai order, not PHP array_intersect's incidental first-array order — 08-UI-SPEC.md explicitly documents canonical read -> write -> ai order as the fixed contract
|
||||
- [Phase 08]: [Phase 08 P05] POST /oauth/mcp/token mounted in this plan, closing 08-04's deliberate D-09 deferral — Only once consent produces a real issued code does an end-to-end /token call through the real route have anything to exchange
|
||||
- [Phase 08]: [Phase 08 P05] AUTH-05/06/07 remain Pending in REQUIREMENTS.md — Refresh rotation (08-06) and connected-apps list/revoke are still outstanding pieces of those requirements; this plan ships consent plus the token mount only
|
||||
- [Phase 08 P06]: rotateRefreshToken commits lineage-kill revocation inside WithinTx and returns nil (success) on replay, mapping a captured replayed flag to invalid_grant outside the transaction -- mirrors PHP rotateRefresh's own commit-then-throw shape
|
||||
- [Phase 08 P06]: RevokeLineage walks forward only through RotatedToID; sufficient for both replay-kill and connected-app-revoke because every normal rotation already revokes its own predecessor's access token and connected-app revoke always starts from the terminal row
|
||||
- [Phase 08 P06]: Fixed RevokeLineage (GORM adapter and in-memory test double) to also revoke each visited row's linked access token -- the 08-02-era method only stamped the refresh row itself, leaving a replayed lineage's live access token usable
|
||||
- [Phase 08 P06]: AUTH-05/06/07 remain Pending in REQUIREMENTS.md: refresh-rotation and connected-apps pieces are done, but AUTH-05/07's unchanged-fonoteka-mcp clause needs 08-08/08-09, and AUTH-06's CSRF/rate-limit/cache-header claims are reconciled by 08-10's security review
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -254,6 +259,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-09-23T19:11:27.509Z
|
||||
Stopped at: Completed 08-05-PLAN.md
|
||||
Last session: 2026-09-23T19:42:20.015Z
|
||||
Stopped at: Completed 08-06-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
phase: 08-oauth2-1-authorization-server
|
||||
plan: 06
|
||||
subsystem: auth
|
||||
tags: [oauth2, rfc6749, refresh-rotation, replay-detection, wristband, gorm, transactions, postgres, row-lock, connected-apps]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 08-oauth2-1-authorization-server
|
||||
plan: 05
|
||||
provides: "wristband.Server.PendingRequest/IssueCode/DenyPending, the fonoteka JWT-group consent API, POST /oauth/mcp/token mounted on the raw group, and rotateRefreshToken's invalid_grant placeholder this plan replaces"
|
||||
provides:
|
||||
- "wristband.Server.rotateRefreshToken: real refresh-grant rotation (revoke old access token, mint successor, link rotated_to_id) and replay-kill (revoke the entire lineage and commit that kill before mapping the outcome to invalid_grant outside the transaction)"
|
||||
- "wristband.Server.Revoke: the cascade-revoke seam (access token + linked refresh lineage, one committed transaction) a connected-app controller calls instead of touching refresh rows directly"
|
||||
- "D-17 expiry sweep on /token (DeleteExpiredCodes/DeleteExpiredRefreshTokens), in addition to /register's existing SweepUnconsented"
|
||||
- "fonoteka OAuthStore adapter: ByAPITokenIDForUpdate, MarkRotated, DeleteExpiredCodes, DeleteExpiredRefreshTokens, and a fixed RevokeLineage that now also revokes each visited row's linked access token"
|
||||
- "fonoteka JWT-group connected-apps API: GET/DELETE /_fonoteka/api/v1/oauth/connected-apps(/{id}), reusing serializeToken plus a sanitized client_name, owner/live/OAuth-only filtering, and the identical 404 for foreign/missing/manual token ids"
|
||||
affects: [08-07-oauth-client-command, 08-08-mcp-me-prerequisite, 08-09-parity-and-real-mcp-gate, 08-10-unit-tests-and-security-review]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Commit-then-map-to-error: replay detection returns nil (success) from the WithinTx callback so the lineage-kill revocation commits, then the caller maps a captured 'replayed' flag to errInvalidGrant outside the transaction -- mirrors PHP rotateRefresh's own commit-then-throw shape and is now the second instance of this pattern in wristband after 08-05's IssueCode boundary"
|
||||
- "RevokeLineage walks forward only through RotatedToID starting at the presented/terminal row; because every normal rotation already revokes its own predecessor's access token, and a connected-app revoke always starts from the terminal (never-yet-rotated) row, a forward-only walk is sufficient for both replay-kill and connected-app-revoke without needing a backward predecessor search (unlike PHP's own bidirectional lineageIds)"
|
||||
- "RED/GREEN staged via a saved-then-reverted rotateRefreshToken body (not a full route/handler revert) for the framework RED commit, since the store-interface extensions and the Token handler's sweep/Revoke additions are compile prerequisites shared by both RED and GREEN test states"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/connected_app_controller.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/connected_app_controller_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/oauth_lifecycle_test.go
|
||||
modified:
|
||||
- wristband/token.go
|
||||
- wristband/token_test.go
|
||||
- wristband/stores.go
|
||||
- wristband/registration_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/routes.go
|
||||
|
||||
key-decisions:
|
||||
- "AUTH-05/AUTH-06/AUTH-07 remain Pending in REQUIREMENTS.md even though this plan ships the refresh-rotation and connected-apps pieces their text names: AUTH-05/AUTH-07 both also require an 'unchanged fonoteka-mcp install/auth flow' verification that only 08-08 (mcp-me prerequisite) and 08-09 (parity-and-real-mcp-gate) can prove; AUTH-06's CSRF/rate-limit/cache-header claims were largely already true from earlier plans, not newly established here. Marking any of the three Complete now would misrepresent phase progress against their full requirement text."
|
||||
- "RevokeLineage's fix (also revoking each visited row's linked access token) is Rule 1 (bug), not Rule 2: the 08-02-era method already existed and was already wired into the replay path by 08-04's placeholder-era interface, but only ever stamped the refresh row itself -- a replayed lineage's live access token would have stayed usable had this plan not caught it while implementing the real rotation caller."
|
||||
- "ByAPITokenIDForUpdate/MarkRotated/DeleteExpiredCodes/DeleteExpiredRefreshTokens were added to RefreshTokenStore/AuthCodeStore as new interface methods (not folded into existing ones) so the same Tx bundle continues to serve every later Phase 8 plan without another interface change, following 08-02's precedent for the store seam."
|
||||
- "The D-17 sweep on /token runs as its own WithinTx call before authenticateClient/grant processing, not folded into the exchange/rotation transaction, so a sweep failure surfaces as a plain opaque 500 independent of which grant type was requested."
|
||||
|
||||
patterns-established:
|
||||
- "grantThenExchange (classes/auth package) and oauthLifecycleGrant (app package) are the shared 'drive a real grant through the real endpoints' fixtures later Phase 8 plans' refresh/revoke tests should reuse rather than re-deriving PKCE/consent/exchange boilerplate."
|
||||
|
||||
requirements-completed: []
|
||||
|
||||
# Metrics
|
||||
duration: ~50min
|
||||
completed: 2026-09-23
|
||||
---
|
||||
|
||||
# Phase 08 Plan 06: Lifecycle and Sweeps (Refresh Rotation + Connected Apps) Summary
|
||||
|
||||
**Refresh-token rotation with commit-then-kill replay detection, a D-17 expiry sweep on `/token`, and owner-scoped connected-app list/revoke that cascades through a new `wristband.Server.Revoke` seam -- all proven against real Postgres including synchronized concurrent-replay and concurrent-rotation row-lock tests.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~50 min
|
||||
- **Started:** ~2026-09-23T19:00:00Z (approx.)
|
||||
- **Completed:** ~2026-09-23T19:40:00Z (approx.)
|
||||
- **Tasks:** 3 completed (5 commits: RED/GREEN pair in summercms.go for the wristband lifecycle framework, RED test + two GREEN commits in fonoteka.go for the store adapter and the connected-apps controller)
|
||||
- **Files modified:** 10 (4 modified in summercms.go's wristband package; 3 created + 3 modified in fonoteka.go)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `wristband.Server.rotateRefreshToken` replaces 08-04's `invalid_grant` placeholder with a real port of `OAuthCodeManager::rotateRefresh`: a fresh refresh token row-locks, revokes its own old access token, mints a same-scope/same-collection successor pair, and links `rotated_to_id`; a replayed (already-rotated) token instead revokes the entire lineage -- every successor row and every linked access token -- and commits that kill before the handler reports `invalid_grant`, so a stolen predecessor can never resurrect a live branch (T-08-REFRESH-REPLAY)
|
||||
- `RevokeLineage` (both the fonoteka GORM adapter and wristband's in-memory test double) was fixed to also revoke each visited row's linked access token -- the 08-02-era method only ever stamped the refresh row's own `revoked_at`, leaving a replayed lineage's currently-live access token usable; this was caught while wiring the real rotation caller, not left as a known gap
|
||||
- `wristband.Server.Revoke` is the new cascade-revoke seam: it revokes an access token and, if a refresh row is still linked to it, kills that row's whole lineage too, in one committed transaction -- the exact operation the connected-app controller needs and never has to reimplement with its own refresh-table SQL
|
||||
- D-17's expiry sweep now also runs on `/token` (`AuthCodeStore.DeleteExpiredCodes`/`RefreshTokenStore.DeleteExpiredRefreshTokens`), deleting only rows already past `expires_at` while unexpired rotated/revoked rows survive as replay evidence -- proven both against the in-memory backend and real Postgres
|
||||
- `fonoteka`'s `ConnectedAppsIndex`/`ConnectedAppsDestroy` expose the existing Settings -> Integrations UI's backing API on the JWT group only: live/owner/OAuth-only tokens newest-first via `serializeToken` plus a sanitized `client_name`, and a foreign/missing/manual token id all collapse to the identical `{"error":"Token not found"}` 404 (T-08-CROSS-USER) -- a revoke removes the app from the next list read and kills both the access token and the refresh token in the same request
|
||||
- Both `scripts/check-phase8-red.sh` sentinels (`PHASE8_RED:lifecycle-framework` in `wristband`, `PHASE8_RED:lifecycle-app` in the assembled `fonoteka` app) were verified fail-closed against the genuine pre-implementation state (rotation placeholder; unmounted connected-apps routes) before their GREEN commits; the full GREEN suite (`go vet`/`go test`/`go test -race`) is green across `summercms.go` and every `fonoteka.go` workspace module (`fonoteka`/`parity`, `plugins/golem15/fonoteka` and its subpackages, `plugins/golem15/user`)
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task's RED test was committed and verified fail-closed via `scripts/check-phase8-red.sh` before its GREEN implementation:
|
||||
|
||||
1. **Task 1: lifecycle RED anchors** -- `b2c2cc0` (test, summercms.go): `TestPhase8RedLifecycleFramework` fails against the `rotateRefreshToken` placeholder (`PHASE8_RED:lifecycle-framework`); extends `RefreshTokenStore`/`AuthCodeStore` with the store seams Task 2 needs and updates the in-memory test double to compile against them. `b3c235d` (test, fonoteka.go): `TestPhase8RedLifecycleApp` fails against the unmounted connected-apps routes (`PHASE8_RED:lifecycle-app`), plus the list/revoke contract test matrix Task 3 turns green.
|
||||
2. **Task 2: implement refresh rotation, committed replay kill, and exact sweeps** -- `dab2b8f` (feat, summercms.go): the real `rotateRefreshToken`, the `/token` expiry sweep, and `Server.Revoke`. `f62a952` (feat, fonoteka.go): `ByAPITokenIDForUpdate`/`MarkRotated`/`DeleteExpiredCodes`/`DeleteExpiredRefreshTokens` on the GORM adapter, the `RevokeLineage` access-token-revocation fix, and real-Postgres proofs (rotation linking, full lineage-kill on replay, concurrent-rotation row-lock winner, sweep retention, connected-app-revoke cascade).
|
||||
3. **Task 3: connected-app list/revoke on the JWT group** -- `3b5fda5` (feat, fonoteka.go): `ConnectedAppsIndex`/`ConnectedAppsDestroy`, their route mount, package-local handler unit tests, and the app-level lifecycle/surface-isolation test suite.
|
||||
|
||||
**Plan metadata:** committed as part of this summary/state-update commit.
|
||||
|
||||
_Note: Tasks 1/2 carry `tdd="true"`; RED/GREEN pairs land as separate commits, split per repo. Task 3's RED anchor (`TestPhase8RedLifecycleApp`) was written and verified alongside Task 1's framework RED commit (both are the plan's single Task 1), then turned green by Task 3's own commit once the controller/routes existed._
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `wristband/token.go` -- real `rotateRefreshToken` (rotation + replay-kill), the `/token` D-17 sweep call, `Server.Revoke`
|
||||
- `wristband/token_test.go` -- `TestPhase8RedLifecycleFramework` plus `TestRefreshRotationIssuesNewPairAndKeepsPredecessorAsEvidence`, `TestRefreshWrongClientIsInvalidGrant`, `TestRefreshExpiredIsInvalidGrant`, `TestRefreshUnknownTokenIsInvalidGrant`, `TestRefreshMissingTokenIsInvalidGrant`, `TestRefreshConcurrentReplayHasExactlyOneWinner`, `TestTokenSweepDeletesExpiredRowsButKeepsUnexpiredEvidence`, `TestServerRevokeKillsAccessAndLineage`
|
||||
- `wristband/stores.go` -- `RefreshTokenStore` gains `ByAPITokenIDForUpdate`/`MarkRotated`/`DeleteExpiredRefreshTokens`; `AuthCodeStore` gains `DeleteExpiredCodes`
|
||||
- `wristband/registration_test.go` -- `memoryTx`/`memoryBackend` updated for the extended interfaces, a `revoked` tracking map, a `RevokeLineage` fix mirroring the GORM adapter, and a `Mint` secret-uniqueness fix (the old `"mem_" + name` secret collided across two mints for the same client name, which a rotation test would have otherwise silently mismatched)
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go` -- `ByAPITokenIDForUpdate`, `MarkRotated`, `DeleteExpiredCodes`, `DeleteExpiredRefreshTokens`, and `RevokeLineage`'s access-token-revocation fix
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store_test.go` -- `TestOAuthRefreshRotationLinksPredecessorToSuccessor`, `TestOAuthRefreshReplayRevokesLineageAndBothAccessTokens`, `TestOAuthRefreshConcurrentReplayHasExactlyOneWinner`, `TestOAuthSweepDeletesExpiredCodesAndRefreshTokensKeepsUnexpiredEvidence`, `TestOAuthConnectedAppRevokeStoreKillsAccessAndLineage`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/controllers/api/connected_app_controller.go` -- `ConnectedAppsIndex`, `ConnectedAppsDestroy`, `serializeConnectedApp`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/controllers/api/connected_app_controller_test.go` -- package-local handler unit tests (list allow-list/forbidden-fields, foreign/missing/owned destroy)
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/routes.go` -- mounts `GET`/`DELETE /_fonoteka/api/v1/oauth/connected-apps(/{id})` on the existing JWT group
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/oauth_lifecycle_test.go` -- `TestPhase8RedLifecycleApp`, `TestConnectedAppsListEmptyThenPopulatedWithManualCount`, `TestConnectedAppsRevokeForeignAndMissingAndManualShareExact404`, `TestConnectedAppsRevokeKillsAccessAndRefreshThenDisappearsFromList`, `TestOAuthConnectedAppsSurfaceIsolation`
|
||||
|
||||
## Decisions Made
|
||||
|
||||
See frontmatter `key-decisions`. The most load-bearing: `RevokeLineage`'s access-token gap was a genuine Rule 1 bug in code that predates this plan (08-02's `Tx` interface shipped `RevokeLineage`'s signature, and 08-04's placeholder already wired the replay branch to call it) -- without this plan's fix, a replayed refresh token would have correctly killed the refresh-row lineage but left the currently-live access token minted by the last legitimate rotation fully usable, defeating T-08-REFRESH-REPLAY's entire point.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] `RevokeLineage` did not revoke linked access tokens**
|
||||
- **Found during:** Task 2, while implementing `rotateRefreshToken`'s replay branch and designing its verification
|
||||
- **Issue:** `RevokeLineage` (both the GORM adapter from 08-02 and wristband's in-memory test double) only stamped `revoked_at` on visited refresh rows; it never touched the `ApiToken` row each refresh row's `APITokenID` points at. A replay would correctly mark the whole refresh chain dead but leave the last legitimate access token minted by rotation still verifiable.
|
||||
- **Fix:** `RevokeLineage` now also row-locks and revokes each visited row's linked access token (if unrevoked), matching PHP `OAuthCodeManager::revokeLineage`'s own per-row access-token revoke
|
||||
- **Files modified:** `../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go`, `wristband/registration_test.go`
|
||||
- **Verification:** `TestOAuthRefreshReplayRevokesLineageAndBothAccessTokens`, `TestPhase8RedLifecycleFramework`, `TestServerRevokeKillsAccessAndLineage`
|
||||
- **Committed in:** `dab2b8f` (summercms.go in-memory double), `f62a952` (fonoteka.go GORM adapter)
|
||||
|
||||
**2. [Rule 1 - Bug] In-memory `Mint` test double minted colliding secrets across rotations**
|
||||
- **Found during:** Task 1, while designing the lifecycle RED anchor
|
||||
- **Issue:** `memoryTx.Mint`'s secret was `"mem_" + name` with no per-mint uniqueness; a rotation test minting a second access token for the same client name would receive the identical secret string as the first, making secret-based identification in tests silently ambiguous
|
||||
- **Fix:** The secret now embeds the freshly-allocated id (`"mem_<id>_<name>"`)
|
||||
- **Files modified:** `wristband/registration_test.go`
|
||||
- **Verification:** `TestPhase8RedLifecycleFramework` and the other rotation tests correctly distinguish old vs. new access tokens by secret
|
||||
- **Committed in:** `b2c2cc0`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2 auto-fixed (2 bugs, both pre-existing test-infrastructure/store gaps caught while building this plan's own verification)
|
||||
**Impact on plan:** No scope change beyond the plan's own stated D-04/D-17/D-08 behavior; both fixes were necessary for the plan's own tests to correctly prove T-08-REFRESH-REPLAY, not separate feature additions.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None beyond the auto-fixed items above. Full `go vet`/`go test ./...`/`go test -race ./...` are green in `summercms.go` (`wristband` and every other package) and in every `fonoteka.go` workspace module: the root `fonoteka`/`parity` module, `plugins/golem15/fonoteka` and its `classes`, `classes/auth`, `controllers/api`, `middleware`, `models`, `updates` subpackages, and `plugins/golem15/user`. `scripts/check-phase8-ui.mjs --contract-self-test` remains green (no Nuxt-facing change).
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None -- no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `wristband.Server.Revoke` is the established cascade-revoke seam; any future plan needing to kill an access token's OAuth lineage should call it rather than reimplementing refresh-table SQL.
|
||||
- `RefreshTokenStore`/`AuthCodeStore` now carry every method 08-02's own "no `DeleteExpired`-shaped method yet" note flagged as outstanding; no further store-interface changes are anticipated for the remaining Phase 8 plans' known scope.
|
||||
- AUTH-05/06/07 remain Pending in REQUIREMENTS.md: this plan closes the refresh-rotation and connected-apps pieces of their text, but each requirement's full text also depends on an "unchanged fonoteka-mcp install/auth flow" proof that 08-08/08-09 own, plus 08-10's security review reconciling AUTH-06's CSRF/rate-limit/cache-header claims against the complete threat register.
|
||||
- `08-07` (OAuth client command) and `08-08` (mcp-me prerequisite) can build directly on this plan's `Server.Revoke`/sweep seams without another wristband interface change.
|
||||
- No blockers.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: wristband/token.go, wristband/token_test.go, wristband/stores.go, wristband/registration_test.go
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go, oauth_store_test.go
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/connected_app_controller.go, connected_app_controller_test.go
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/routes.go, oauth_lifecycle_test.go
|
||||
- FOUND commits (summercms.go): b2c2cc0, dab2b8f
|
||||
- FOUND commits (fonoteka.go): b3c235d, f62a952, 3b5fda5
|
||||
|
||||
---
|
||||
*Phase: 08-oauth2-1-authorization-server*
|
||||
*Completed: 2026-09-23*
|
||||
Reference in New Issue
Block a user