diff --git a/.planning/phases/16.3-backend-admin-personal-api-tokens-with-scopes/16.3-01-SUMMARY.md b/.planning/phases/16.3-backend-admin-personal-api-tokens-with-scopes/16.3-01-SUMMARY.md new file mode 100644 index 0000000..5ad8710 --- /dev/null +++ b/.planning/phases/16.3-backend-admin-personal-api-tokens-with-scopes/16.3-01-SUMMARY.md @@ -0,0 +1,189 @@ +--- +phase: 16.3-backend-admin-personal-api-tokens-with-scopes +plan: 01 +subsystem: auth +tags: [api-tokens, scopes, cabana, bouncer, pact, party, lagoon, admin-cli, openapi, g15] + +requires: + - phase: 16.2-plugin-provided-field-types-and-the-blog-editor-formwidget + provides: cabana Activate, admin OpenAPI inventories, BackendAdminMigrations, My account routes +provides: + - "pact.TokenScope and pact.HasTokenScopes" + - "bouncer.ScopeLimit and Principal.Scope" + - "backend_api_tokens migration and cabana.BackendAPIToken / APITokenPrefix" + - "composite backend guard, interactiveOnly, tokenRouteGate, ResolveBackend, ErrUnauthenticated, ErrTokenScope" + - "account token API (list/create/revoke/scopes) with OpenAPI and phrase keys" + - "admin:token:* CLI via party.Activated and summer delegates" + - "framework READMEs and docs for tokens, scopes, ResolveBackend and CLI" +affects: [16.3-02, 16.3-03, 16.3-04, sm-journal-plugin] + +actuals: + tokens: 40262 + tasks: 4 + commits: 5 +plan_head_before: 4d107f4ccb3ed00bf12b680b07c2e7874b345198 +plan_head_after: e24e03c47d84f854b6c66c1b317940492b60f441 + +tech-stack: + added: [] + patterns: + - "Composite backend guard: Bearer g15_ → token branch; else unchanged JWT+cookie path" + - "party.Activated published at Activate for CLI catalog without changing RuntimeCommands(app)" + - "Scope before superuser/empty-list in Allows; interactive_only and token_scope reasons" + +key-files: + created: + - modules/cabana/api_tokens.go + - modules/cabana/token_scopes.go + - modules/cabana/token_guard.go + - modules/cabana/account_tokens.go + - modules/cabana/token_commands.go + - modules/cabana/token_smoke_test.go + - modules/cabana/token_postgres_smoke_test.go + - docs/examples/blog/token_scopes.go + modified: + - modules/pact/capabilities.go + - modules/bouncer/context.go + - modules/lagoon/backend_admin_migrations.go + - modules/cabana/contracts.go + - modules/cabana/http.go + - modules/cabana/registry.go + - modules/cabana/admin_openapi.go + - modules/party/registry.go + - cmd/summer/runtime.go + - admin/openapi/admin.json + - admin/src/api/schema.d.ts + - modules/phrasebook/backend/lang/en/lang.yaml + - modules/phrasebook/backend/lang/pl/lang.yaml + - docs/backend/users-and-permissions.md + - docs/services/authentication.md + - docs/console/setup-and-maintenance.md + - docs/console/introduction.md + - docs/plugins/registration.md + +key-decisions: + - "D-03 literal: one method-allowed scope must match every required code; ScopeLimit keeps per-scope pattern slices" + - "CLI uses party.Activated at Run time; cabana.RuntimeCommands(app) signature unchanged (Option A)" + - "ResolveBackend is Bearer-only (cookie-less JWT guard) so plugin writes cannot be CSRF-driven by summer_admin" + - "Exported names match the plan Interfaces section; no as-built renames" + +patterns-established: + - "Personal API token: g15_+40 hex, sha256+hint stored, plaintext once on create/CLI stdout" + - "Plugin scopes via HasTokenScopes; framework * and admin:read; boot fails loud on bad registration" + +requirements-completed: [SC-1, SC-2, SC-3, SC-5] + +coverage: + - id: D1 + description: "g15_ token authenticates through the composite backend guard as its owner, scoped by Allows" + requirement: SC-1 + verification: + - kind: unit + ref: modules/cabana/token_smoke_test.go#TestTokenTracerSmoke + status: pass + - kind: integration + ref: modules/cabana/token_postgres_smoke_test.go#TestTokenTracerPostgres + status: pass + human_judgment: false + - id: D2 + description: "Out-of-scope and non-Allows routes refuse tokens; interactive-only routes refuse all personal tokens; ResolveBackend and last_used debounce work" + requirement: SC-2 + verification: + - kind: unit + ref: modules/cabana/token_smoke_test.go#TestTokenGatesSmoke + status: pass + human_judgment: false + - id: D3 + description: "Account token API list/create/revoke/scopes with OpenAPI, inventories and phrase keys" + requirement: SC-3 + verification: + - kind: integration + ref: modules/cabana/token_postgres_smoke_test.go#TestAccountTokensSmoke + status: pass + - kind: other + ref: bash scripts/check-admin-openapi.sh --check + status: pass + human_judgment: false + - id: D4 + description: "admin:token:* CLI with party.Activated, summer delegates, READMEs and docs" + requirement: SC-5 + verification: + - kind: integration + ref: modules/cabana/token_postgres_smoke_test.go#TestTokenCommandsSmoke + status: pass + - kind: unit + ref: cmd/summer/runtime_test.go#TestDelegateCommandsHelp + status: pass + - kind: other + ref: go run ./cmd/summer docs:build --check + status: pass + human_judgment: false + +duration: 95min +completed: 2026-10-11 +status: complete +--- + +# Phase 16.3 Plan 01: Framework personal API tokens Summary + +**Scoped g15_ personal API tokens through a composite backend guard, account API, admin:token CLI and docs — owner ∩ scope, interactive-only gates, ResolveBackend for plugins.** + +## Performance + +- **Duration:** 95 min +- **Started:** 2026-10-10T23:41:00Z +- **Completed:** 2026-10-11T00:00:00Z +- **Tasks:** 4 +- **Files modified:** 43 + +## Accomplishments +- Tracer path: mint → hash store → composite guard → ScopeLimit → Allows (scope before superuser) +- Deny-by-default gates: interactiveOnly, field-types/markdown tokenRouteGate, published ResolveBackend, debounced last_used_at +- Account token API with OpenAPI/TS types, inventories and backend::lang.api_tokens phrase keys +- admin:token:* CLI via party.Activated, summer delegates, blog HasTokenScopes example, five module READMEs and five docs pages + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Tracer** - `b9273c1` (feat) +2. **Task 2: Gates, ResolveBackend, stamp** - `183583e` (feat) +3. **Task 3: Account token API + OpenAPI** - `ffdb5d0` (feat) +4. **Task 4: CLI, party.Activated, READMEs/docs** - `e24e03c` (feat) + +**Plan metadata:** (this commit) + +## Exported API as built + +Matches the plan Interfaces section: `pact.TokenScope` / `HasTokenScopes`; `bouncer.ScopeLimit` / `Principal.Scope`; `party.Activated`; `cabana.BackendAPIToken`, `APITokenPrefix`, `ResolveBackend`, `ErrUnauthenticated`, `ErrTokenScope`; OpenAPI types and swag stubs; CLI `admin:token:create|list|revoke|scopes`. No name differs from the plan. + +## Decisions Made +- Followed locked D-01–D-20 and Option A for CLI plugin access (`party.Activated`). +- `validateTokenRequest` + `validateTokenScopes` helper; `compilePermissionCatalog` shared by `compileContributions` and `compileTokenCatalog`. + +## Deviations from Plan + +None - plan executed as written. + +## Issues Encountered +None + +## Postgres verify status + +All Docker-host Postgres lines from this plan were **RUN** and passed on this host: + +- Task 1: `TestTokenTracerPostgres`, `TestBackendAdminMetadataColumn`, `TestPhase09MigrationsFreshRollback` +- Task 2: `TestTokenTracerPostgres`, `TestAdminAuthLifecycle`, `TestAdminLogoutRevokesExpiredRefreshableToken` +- Task 3: `TestAccountTokensSmoke`, `TestPhase10OpenAPIConformance` +- Task 4: `TestTokenCommandsSmoke`, `TestAdminCreateCommand` + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness +Ready for 16.3-02 (admin SPA API tokens card on My account). Framework half of SC-1/SC-2/SC-3/SC-5 is in place for Journal (16.3-03) and unit tests (16.3-04). + +--- +*Phase: 16.3-backend-admin-personal-api-tokens-with-scopes* +*Completed: 2026-10-11*