docs(08-05): complete consent plan
This commit is contained in:
@@ -341,7 +341,7 @@ Plans:
|
||||
|
||||
**Wave 5** *(blocked on 08-04)*
|
||||
|
||||
- [ ] 08-05-PLAN.md — Wire JWT consent and prove the unchanged Nuxt UI contract
|
||||
- [x] 08-05-PLAN.md — Wire JWT consent and prove the unchanged Nuxt UI contract
|
||||
|
||||
**Wave 6** *(blocked on 08-05)*
|
||||
|
||||
@@ -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 | 4/10 | In Progress| |
|
||||
| 8. OAuth2.1 authorization server | 5/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-04-PLAN.md
|
||||
last_updated: "2026-09-23T18:35:28.069Z"
|
||||
stopped_at: Completed 08-05-PLAN.md
|
||||
last_updated: "2026-09-23T19:11:27.527Z"
|
||||
last_activity: 2026-09-23
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 7
|
||||
total_plans: 55
|
||||
completed_plans: 49
|
||||
completed_plans: 50
|
||||
percent: 47
|
||||
---
|
||||
|
||||
@@ -26,11 +26,11 @@ See: .planning/PROJECT.md (updated 2026-09-16)
|
||||
## Current Position
|
||||
|
||||
Phase: 08 (oauth2-1-authorization-server) — EXECUTING
|
||||
Plan: 5 of 10
|
||||
Plan: 6 of 10
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-09-23
|
||||
|
||||
Progress: [█████████░] 89%
|
||||
Progress: [█████████░] 91%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -95,6 +95,7 @@ Progress: [█████████░] 89%
|
||||
| Phase 08 P02 | 30min | 3 tasks | 14 files |
|
||||
| Phase 08 P03 | 20min | 2 tasks | 6 files |
|
||||
| Phase 08 P04 | 15min | 2 tasks | 5 files |
|
||||
| Phase 08 P05 | 55min | 3 tasks | 12 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -227,6 +228,11 @@ Recent decisions affecting current work:
|
||||
- [Phase 08]: [Phase 08 P04]: Token dispatch accepts grant_type=refresh_token per PHP's exact validity check but rotateRefreshToken always returns invalid_grant in this plan's scope; full rotation/lineage-kill (T-08-REFRESH-REPLAY) is 08-06's job per ROADMAP.md Wave 6
|
||||
- [Phase 08]: [Phase 08 P04]: No routes.go/plugin.go changes -- POST /oauth/mcp/token is not mounted on the assembled app this plan; mounting happens once 08-05 wires consent and produces a real issued code
|
||||
- [Phase 08]: [Phase 08 P04]: MintablePrefix changed from const to var (D-11 groundwork) with no config wiring added yet; default stays byte-identical inv_
|
||||
- [Phase 08]: [Phase 08 P05] AuthCodeStore.MarkIssued gained scopes/collectionIDs/expiresAt params; ClientStore gained MarkConsented — PHP issueCode overwrites six columns in one UPDATE, not just code_hash/user_id; the narrower 08-02 signature could not persist consent's granted scopes or server-resolved collection pin
|
||||
- [Phase 08]: [Phase 08 P05] wristband.Options gained CodeTTL (600s default), wired from the previously-unconsumed code_ttl_seconds config key — PHP OAuthCodeManager::CODE_TTL_SECONDS is a distinct constant from PENDING_TTL_SECONDS; IssueCode needs a fresh expiry from consent time
|
||||
- [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
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -248,6 +254,6 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-09-23T18:35:28.050Z
|
||||
Stopped at: Completed 08-04-PLAN.md
|
||||
Last session: 2026-09-23T19:11:27.509Z
|
||||
Stopped at: Completed 08-05-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
phase: 08-oauth2-1-authorization-server
|
||||
plan: 05
|
||||
subsystem: auth
|
||||
tags: [oauth2, rfc6749, consent, wristband, gorm, surf, jwt-group, playwright]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 08-oauth2-1-authorization-server
|
||||
plan: 04
|
||||
provides: "wristband.Server.Token (authorization_code grant), the post-consent AuthCodeRecord shape (CodeHash set, RequestID nil, UserID set), and the unmounted POST /oauth/mcp/token route"
|
||||
provides:
|
||||
- "wristband.Server.PendingRequest/IssueCode/DenyPending: app-agnostic consent operations (owner-bound lookup, code issuance, denial) that collapse missing/foreign/used/expired/already-issued pending rows to one ErrPendingNotFound"
|
||||
- "fonoteka JWT-group consent API: GET oauth/request/{request_id}, POST oauth/consent, POST oauth/deny, wired on the existing jwt.auth/locale.from-principal/inv.must-change-password group"
|
||||
- "POST /oauth/mcp/token mounted on the raw group with its named throttle, closing the 08-04 D-09 gap -- a full authorize->consent->token PKCE round trip now works end to end against the assembled app"
|
||||
- "scripts/check-phase8-ui.mjs: a self-validating, read-only 32-scenario UI-contract harness for the unchanged Nuxt consent/connected-app surfaces, with a --final-gate seam reserved for 08-10"
|
||||
affects: [08-06-lifecycle-and-sweeps, 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:
|
||||
- "Protocol-owned mutation + app-owned policy split: wristband.IssueCode trusts the caller's already-computed granted-scope intersection and server-resolved collection ids (persist + redirect only); the app controller owns MINTABLE_SCOPES intersection and ActiveCollectionResolver, matching the PHP OAuthConsentController/OAuthCodeManager boundary exactly"
|
||||
- "surf Where() binds to the immediately preceding route only, not the whole enclosing group -- constraints on a group with multiple path-parameterized routes must be interleaved route-by-route, not batched at the end"
|
||||
- "RED/GREEN staged via a saved-then-reverted working tree (stub handlers + reverted routes.go/plugin.go for the RED commit, restored for GREEN) so both scripts/check-phase8-red.sh sentinels observe a genuine fail-closed failure even though the full implementation was written and verified before either commit"
|
||||
- "check-phase8-ui.mjs: a locked per-category scenario-count manifest (EXPECTED_COUNTS) catches accidental UI-SPEC coverage drift at self-test time rather than relying on manual review"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- wristband/consent.go
|
||||
- wristband/consent_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/oauth_connect_test.go
|
||||
- scripts/check-phase8-ui.mjs
|
||||
modified:
|
||||
- wristband/stores.go
|
||||
- wristband/server.go
|
||||
- wristband/registration_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/plugin.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/routes.go
|
||||
|
||||
key-decisions:
|
||||
- "AuthCodeStore.MarkIssued gained scopes/collectionIDs/expiresAt parameters (was codeHash/userID only) because PHP's issueCode overwrites all of code_hash/request_id/user_id/scopes/collection_ids/expires_at in one UPDATE, not just the first two; the narrower 08-02 signature could not persist consent's granted-scope subset or the server-resolved collection pin"
|
||||
- "ClientStore gained MarkConsented (idempotent via a WHERE consented_at IS NULL guard) to stamp OAuthClient.consented_at once, matching D-08 and DCR's SweepUnconsented dependency on that column"
|
||||
- "wristband.Options gained CodeTTL (600s default) so IssueCode sets a fresh code expiry from consent time rather than reusing the pending row's original 08-03 expiry, matching PHP's independent CODE_TTL_SECONDS constant; plugin.go wires it from the already-declared-but-previously-unconsumed golem15.fonoteka.oauth.code_ttl_seconds config key"
|
||||
- "Consent's canonical scope ordering (read -> write -> ai) is computed app-side against auth.MintableScopes's own declared order, not by replicating PHP's array_intersect (which preserves the first array's order); 08-UI-SPEC.md's explicit 'canonical read -> write -> ai order' language is treated as the authoritative contract over the PHP source's incidental ordering"
|
||||
- "POST /oauth/mcp/token is mounted in this plan (not 08-04) per 08-04-SUMMARY.md's own explicit deferral: only once consent produces a real issued code does an end-to-end /token call through the real route have anything to exchange"
|
||||
- "check-phase8-ui.mjs's --final-gate mode is scaffolded (verify:oauth-return-path, verify:oauth-i18n, and a Playwright-matrix seam) but deliberately fatal-errors unless PHASE8_UI_ALLOW_FINAL_GATE=1 is set, and is never invoked by this plan; 08-10 is its sole execution site per the plan's own success criteria"
|
||||
- "AUTH-05/06/07 remain Pending in REQUIREMENTS.md: this plan ships consent plus the token mount, but refresh rotation (08-06) and connected-apps list/revoke (also deferred, not in this plan's file list despite the phase directory's 'consent-and-connected-apps' name) are still outstanding pieces of those requirements"
|
||||
|
||||
patterns-established:
|
||||
- "oauthDeps(w, r, app) is the JWT-group consent controller's shared dependency resolver (gdb + bouncer.Principal + *wristband.Server via app.Lookup), the seam any later connected-apps controller in this package should reuse"
|
||||
|
||||
requirements-completed: []
|
||||
|
||||
# Metrics
|
||||
duration: ~55min
|
||||
completed: 2026-09-23
|
||||
---
|
||||
|
||||
# Phase 08 Plan 05: Consent and Connected Apps (Consent Slice) Summary
|
||||
|
||||
**Owner-bound JWT consent (show/allow/deny) backed by new wristband.Server.PendingRequest/IssueCode/DenyPending operations, POST /oauth/mcp/token finally mounted to close 08-04's deferred gap, and a self-validating 32-scenario read-only Playwright-contract harness for the unchanged Nuxt consent/connected-app UI.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~55 min
|
||||
- **Started:** ~2026-09-23T19:05:00Z (approx., continuing from 08-04)
|
||||
- **Completed:** 2026-09-23T21:05:00Z (approx.)
|
||||
- **Tasks:** 3 completed (5 commits: RED/GREEN pair in fonoteka.go for Task 1/2, one feat commit each in summercms.go for the wristband prerequisite and the UI harness)
|
||||
- **Files modified:** 12 (5 created + 3 modified in summercms.go's wristband/scripts; 3 created + 3 modified in fonoteka.go)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `wristband.Server.PendingRequest`/`IssueCode`/`DenyPending` port PHP `OAuthConsentController::pendingFor`/`OAuthCodeManager::issueCode` as app-agnostic protocol operations: every missing, foreign-owner, used, expired, or already-issued pending row collapses to the identical `ErrPendingNotFound` (T-08-CROSS-USER/T-08-REQUEST-LEAK); `IssueCode` trusts the caller's already-computed granted scopes/collection ids and returns the ordered `redirect_to` URL through the existing RFC 3986 encoder
|
||||
- `AuthCodeStore.MarkIssued` was extended (scopes/collectionIDs/expiresAt) and `ClientStore.MarkConsented` was added because the narrower 08-02 store interface could not express PHP's full single-UPDATE `issueCode` semantics; both changes are covered by real-Postgres assertions in `oauth_store.go`'s existing test package
|
||||
- `fonoteka`'s JWT-group `ConsentShow`/`ConsentStore`/`ConsentDeny` grant exactly `submitted ∩ pending-request ∩ MINTABLE_SCOPES`, pin collection ids exclusively through `ResolveActiveCollection`, and never accept a client-supplied collection id; an empty granted intersection is an exact 422 `{"error":"No grantable scopes"}`
|
||||
- `POST /oauth/mcp/token` is now mounted on the raw group with `throttle:fonoteka-oauth-token`, closing the gap 08-04 deliberately left open -- a full `authorize -> consent(JWT) -> token` PKCE round trip now produces a real `inv_`-prefixed access token through the assembled app and real Postgres
|
||||
- `scripts/check-phase8-ui.mjs` encodes the complete `08-UI-SPEC.md` consent/connected-app state/accessibility/responsive/i18n matrix as a 32-scenario, 7-category catalog; `--contract-self-test` validates catalog completeness, guarded-Nuxt-file hash stability, and `@playwright/test` resolvability in ~50ms without booting anything; `--final-gate` is scaffolded but refuses to run without an explicit opt-in env var, reserved for 08-10
|
||||
- Both `scripts/check-phase8-red.sh` sentinel gates (`PHASE8_RED:consent-controller`, `PHASE8_RED:consent-app`) were verified fail-closed against genuine 501-stub/unmounted-route RED states before GREEN was restored; the full GREEN suite (`go vet`/`go test`/`go test -race`) is green in both `fonoteka.go/plugins/golem15/fonoteka` and `summercms.go`
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically (TDD RED then GREEN where applicable, split per repo):
|
||||
|
||||
1. **Task 1: consent RED anchor + wristband prerequisite** -- `a1fa9c6` (feat, summercms.go): `wristband.Server.PendingRequest/IssueCode/DenyPending`, the `MarkIssued`/`MarkConsented` interface extensions, `Options.CodeTTL`; `306b06e` (test, fonoteka.go): `TestPhase8RedConsentController`/`TestPhase8RedConsentApp` fail against 501 stub handlers and unmounted routes (`PHASE8_RED:consent-controller`/`PHASE8_RED:consent-app`), plus the compile-forced `oauth_store.go` adapter update. Both sentinels verified fail-closed via `scripts/check-phase8-red.sh`.
|
||||
2. **Task 2: implement owner-bound JWT consent and mount raw token route** -- `a52e8fa` (feat, fonoteka.go): the real `ConsentShow`/`ConsentStore`/`ConsentDeny` handlers, `routes.go`'s consent-route and `/oauth/mcp/token` mounts, `plugin.go`'s `*wristband.Server` publish and `code_ttl_seconds` wiring, and the full GREEN test matrix (T-08-CROSS-USER, T-08-SCOPE-CEILING, empty-scope 422, deny redirect + single-use, missing-handle 404, JWT/raw route-surface isolation).
|
||||
3. **Task 3: read-only UI-contract harness** -- `fac9648` (feat, summercms.go): `scripts/check-phase8-ui.mjs`.
|
||||
|
||||
**Plan metadata:** committed as part of this summary/state-update commit.
|
||||
|
||||
_Note: Tasks 1/2 carry `tdd="true"`; the RED commit's fonoteka.go changes required including `oauth_store.go` (a hard compile dependency on wristband's extended `Tx` interface) even though the plan's Task 1 file list names only the two test files -- see Deviations._
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `wristband/consent.go` -- `Server.PendingRequest`, `Server.IssueCode`, `Server.DenyPending`, `lookupOwnedPending`, `ErrPendingNotFound`, `ErrNoGrantableScopes`
|
||||
- `wristband/consent_test.go` -- in-memory-backend unit matrix: client/scope resolution, foreign-owner/missing-handle not-found, ordered redirect construction, empty-grant rejection, deny consumption + single-use
|
||||
- `wristband/stores.go` -- `AuthCodeStore.MarkIssued` signature extension, `ClientStore.MarkConsented`
|
||||
- `wristband/server.go` -- `Options.CodeTTL` (600s default)
|
||||
- `wristband/registration_test.go` -- `memoryTx.MarkIssued`/`MarkConsented` updated to satisfy the extended interface
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go` -- `oauthTx.MarkIssued`/`MarkConsented` GORM implementations
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go` -- `ConsentShow`/`ConsentStore`/`ConsentDeny`, `oauthDeps`, `canonicalMintableIntersection`, `intersectStrings`, `readConsentScopes`, `untrustedName`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller_test.go` -- package-local real-Postgres harness (`TestMain`, `apiDB`) plus `TestPhase8RedConsentController`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/plugin.go` -- publishes `*wristband.Server`; wires `code_ttl_seconds`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/routes.go` -- mounts the three JWT-group consent routes and `POST /oauth/mcp/token`
|
||||
- `../fonoteka.go/plugins/golem15/fonoteka/oauth_connect_test.go` -- `TestPhase8RedConsentApp` plus `TestOAuthConsentCrossUserIsNotFound`, `TestOAuthConsentScopeCeilingViaShow`, `TestOAuthConsentEmptySubmittedScopeIntersectionIs422`, `TestOAuthDenyRedirectsAccessDeniedAndConsumesRequest`, `TestOAuthConsentMissingHandleIsNotFoundWithNoRequest`, `TestOAuthConsentSurfaceIsolation`
|
||||
- `scripts/check-phase8-ui.mjs` -- the read-only UI-contract harness (Task 3)
|
||||
|
||||
## Decisions Made
|
||||
|
||||
See frontmatter `key-decisions`. The two most load-bearing: (1) `MarkIssued`'s signature had to grow beyond 08-02's original codeHash/userID-only shape because PHP's `issueCode` is a single UPDATE touching six columns, not two -- consent could not otherwise persist the user's actual granted-scope subset or the server-resolved collection pin; (2) canonical scope ordering in the consent show/allow response follows `auth.MintableScopes`'s declared order (read, write, ai) per `08-UI-SPEC.md`'s explicit language, not PHP's `array_intersect`, which happens to preserve first-array order and would leak submission-order-dependent behavior into a contract the UI-SPEC documents as fixed.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 2 - Missing functionality] Extended `wristband.AuthCodeStore.MarkIssued` and added `ClientStore.MarkConsented`**
|
||||
- **Found during:** Task 1, while designing `wristband.Server.IssueCode`
|
||||
- **Issue:** 08-02's `MarkIssued(ctx, id, codeHash, userID)` could not express PHP `OAuthCodeManager::issueCode`'s full write (it also overwrites `scopes`, `collection_ids`, and `expires_at`), and no store method existed to stamp `OAuthClient.consented_at` once (D-08's explicit "consented_at stamping" requirement, and a precondition for DCR's `SweepUnconsented` to correctly skip consented clients)
|
||||
- **Fix:** Extended `MarkIssued`'s signature to accept `scopes []string, collectionIDs []uint, expiresAt time.Time`; added `MarkConsented(ctx, clientID) error` with an idempotent `WHERE consented_at IS NULL` guard. Updated the GORM adapter (`oauth_store.go`) and the framework's in-memory test double (`registration_test.go`'s `memoryTx`) to match
|
||||
- **Files modified:** `wristband/stores.go`, `wristband/registration_test.go`, `../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go`
|
||||
- **Verification:** `wristband/consent_test.go`'s `TestIssueCodeGrantsOnlySubmittedScopesAndReturnsOrderedRedirect`; `classes/auth` package's real-Postgres suite stays green (`go test ./...`, `go test -race ./...`)
|
||||
- **Committed in:** `a1fa9c6` (summercms.go), `306b06e` (fonoteka.go, compile-forced alongside the RED test commit)
|
||||
|
||||
**2. [Rule 2 - Missing functionality] Added `wristband.Options.CodeTTL`**
|
||||
- **Found during:** Task 1, implementing `IssueCode`'s fresh expiry
|
||||
- **Issue:** PHP's `OAuthCodeManager::CODE_TTL_SECONDS` is a distinct constant from `PENDING_TTL_SECONDS`; wristband's `Options` had no field for it, and 08-02's `config.yaml` already declared `code_ttl_seconds` with no Go consumer (flagged as an open seam in 08-02-SUMMARY.md)
|
||||
- **Fix:** Added `Options.CodeTTL` (600s PHP-parity default) and wired `golem15.fonoteka.oauth.code_ttl_seconds` into it in `plugin.go`
|
||||
- **Files modified:** `wristband/server.go`, `../fonoteka.go/plugins/golem15/fonoteka/plugin.go`
|
||||
- **Verification:** `wristband/consent_test.go` exercises the default; no config-key behavior change to any currently-passing test
|
||||
- **Committed in:** `a1fa9c6` (summercms.go), `a52e8fa` (fonoteka.go)
|
||||
|
||||
**3. [Rule 1 - Bug] Fixed `surf.Where("request_id", ...)` binding to the wrong route**
|
||||
- **Found during:** Task 2, first GREEN test run (`TestPhase8RedConsentApp` failed with `surf: Where("request_id") is not a path parameter of /_fonoteka/api/v1/oauth/deny`)
|
||||
- **Issue:** `g.Where()` constrains only the immediately preceding registered route (matching PHP's `->where()`), not every route registered since the last `Where()` call; the three consent routes were registered before a single trailing `Where("request_id", ...)`, so the constraint attached to `/oauth/deny` (the last-added route, which has no `{request_id}` parameter) instead of `/oauth/request/{request_id}`
|
||||
- **Fix:** Moved `g.Where("request_id", ...)` to sit immediately after `g.Get("/oauth/request/{request_id}", ...)`, before the two POST routes
|
||||
- **Files modified:** `../fonoteka.go/plugins/golem15/fonoteka/routes.go`
|
||||
- **Verification:** Full GREEN suite (`go test ./...`, `go test -race ./...`) passes in `fonoteka.go/plugins/golem15/fonoteka`
|
||||
- **Committed in:** `a52e8fa`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 3 auto-fixed (2 missing functionality, 1 bug)
|
||||
**Impact on plan:** No scope change beyond the plan's own stated D-08 behavior (consent's scope/collection persistence and consented_at stamping); the `Where()` bug was self-caught by the plan's own GREEN test suite before commit.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None beyond the auto-fixed items above. Full `go vet`/`go test ./...`/`go test -race ./...` are green in `fonoteka.go/plugins/golem15/fonoteka` (and its sibling `classes`, `classes/auth`, `controllers/api`, `middleware`, `models`, `updates` packages) and in `summercms.go`. The root `fonoteka.go`/`parity` module and `plugins/golem15/user` module were also checked (`go vet`/`go test`) and remain green; no new failures were introduced outside this plan's files.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None -- no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `POST /oauth/mcp/token` is now live end to end through the assembled app; 08-06 (lifecycle and sweeps) can build refresh rotation directly against the same mounted route rather than needing to mount it itself.
|
||||
- `wristband.Server.IssueCode`/`DenyPending`'s pattern (protocol persists + redirects, app computes policy) is the established seam for any future consent-adjacent operation.
|
||||
- Connected-apps list/revoke (`GET/DELETE .../oauth/connected-apps`) and `fonoteka-mcp`'s `/api/v1/fonoteka/me` prerequisite remain unbuilt -- neither was in this plan's file list despite the phase directory's "consent-and-connected-apps" name; 08-06/08-08 (per ROADMAP.md) own them.
|
||||
- `scripts/check-phase8-ui.mjs`'s scenario catalog and guarded-file list are ready for 08-10 to wire into a real Playwright spec via `--final-gate`; no further catalog changes should be needed unless a new UI-SPEC state is added.
|
||||
- AUTH-05/06/07 remain Pending in REQUIREMENTS.md, continuing 08-01 through 08-04's decision: refresh rotation and connected-apps list/revoke are still outstanding pieces of those requirements.
|
||||
- No blockers.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: wristband/consent.go, wristband/consent_test.go, wristband/stores.go, wristband/server.go, wristband/registration_test.go
|
||||
- FOUND: scripts/check-phase8-ui.mjs
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go, oauth_consent_controller_test.go
|
||||
- FOUND: ../fonoteka.go/plugins/golem15/fonoteka/plugin.go, routes.go, oauth_connect_test.go
|
||||
- FOUND commits (summercms.go): a1fa9c6, fac9648
|
||||
- FOUND commits (fonoteka.go): 306b06e, a52e8fa
|
||||
|
||||
---
|
||||
*Phase: 08-oauth2-1-authorization-server*
|
||||
*Completed: 2026-09-23*
|
||||
Reference in New Issue
Block a user