Conversation
The copepod worktree running this OpenSpec change was deleted before the proposal was committed. Reconstructed proposal.md, design.md, tasks.md and specs/tenancy/rbac/spec.md from the agent session transcript — no code was implemented yet, only the proposal.
Add TenantPermissionEnum, role→permission mapping, RequiresPermission decorator and TenantPermissionGuard; wire them onto every tenant-scoped REST/GraphQL endpoint. UpdateTenantCommandHandler now relies solely on the guard for authorization instead of the stricter owner-only check, so ADMIN callers (who hold manage-tenant) can update a tenant per spec.
…onsibility Add a worked Gardenia example showing the permission enum, role->permission map, and guard/decorator every consumer app must implement itself on top of the tenants JWT claim — account-api's tenancy context and nestjs-kit ship none of this by design. Also fixes the stale lowercase "owner" role example to match the actual TenantRoleEnum values.
…s-kit/rbac Bump @sisques-labs/nestjs-kit to 1.9.0, which ships the tenant-permission guard mechanism (createTenantPermissionGuard()/RequiresTenantPermission()) extracted from this context's own guard. TenantPermissionGuard and RequiresPermission are now thin, TenantPermissionEnum-typed aliases built from the kit's factory instead of a hand-rolled CanActivate class — the permission enum and TENANT_ROLE_PERMISSIONS mapping stay local, since what each TenantRole grants is this app's own policy, not the kit's. Update tenancy/README.md and docs/integration-guide.md §5 to point other consumer apps (Gardenia, Nexora, ...) at the same kit factory instead of telling them to hand-roll their own guard from scratch.
feat(tenancy): tenant-scoped RBAC permission checks
… JWKS endpoint Signs access tokens with an RSA key pair sourced from JWT_PRIVATE_KEY (base64 PEM), or an ephemeral dev keypair outside production, and publishes the public key at GET /.well-known/jwks.json so consumer apps can verify tokens without a shared secret. env.validation.ts now fails closed when NODE_ENV=production and JWT_PRIVATE_KEY is absent. Pre-cutover HS256 tokens are intentionally left unverifiable — no dual-algorithm compatibility bridge.
…on login Reconciles each user's platformAdmin flag against PLATFORM_ADMIN_EMAILS on every successful login. auth reaches user exclusively through a new IUserPlatformAdminPort/UserPlatformAdminAdapter pair dispatching the new public SetUserPlatformAdminCommand, since changePlatformAdmin() stays private on UserAggregate. Tri-state handling: unset skips reconciliation entirely, empty string revokes every admin, a populated list grants/revokes by email membership.
Replace the single-session-per-user model with a linked-list session chain: SessionEntity drops UQ_session_user_id in favor of nullable revoked_at/replaced_by_session_id (self-FK), SessionAggregate gains revoke()/isRevoked()/markReuseDetected(), and ISessionWriteRepository gains rotate() (pessimistic_write, successor inserted before predecessor is revoked) and revokeAllByUserId(). This is schema/domain/persistence scaffolding only — the live refresh endpoint still uses the deprecated in-place SessionAggregate.rotate() until WU-3b wires the locked chain rotation and reuse-detection into RefreshSessionCommandHandler in a follow-up PR.
…sh/login (WU-3b) RefreshSessionCommandHandler now rotates through the chain-aware locked ISessionWriteRepository.rotate(): a replayed/already-consumed refresh token invalidates the whole session chain via revokeAllByUserId and returns 401 (RefreshTokenReuseDetectedException), with no grace window. LoginUserCommandHandler always starts a brand-new chain-root session instead of rotating an existing row in place. Removes the deprecated in-place SessionAggregate.rotate(), now that this handler is its only caller and has been migrated.
…3b-rotation-wiring feat(auth): wire locked chain rotation and reuse-detection into refresh/login (WU-3b)
…3a-session-chain feat(auth): add session chain schema, domain, and persistence (WU-3a)
…2-platform-admin feat(auth,user): bootstrap platform_admin from PLATFORM_ADMIN_EMAILS on login
…min-emails docs(env): document PLATFORM_ADMIN_EMAILS in .env.example
…tion Closes the CRITICAL verify gap in auth-token-signing/spec.md's 'Pre-cutover token presented after deploy' scenario: no test previously proved that an HS256-signed token is rejected once the service verifies RS256-only. Crafts an HS256 token with an arbitrary secret and asserts 401 against a JwtAuthGuard-protected endpoint.
… main Sync three new capability specs to openspec/specs/: - auth-session-rotation: locked refresh rotation with reuse-chain invalidation - auth-token-signing: RS256 asymmetric signing and JWKS publication - platform-admin-bootstrap: env-driven platform admin reconciliation Move account-platform-mvp change to openspec/changes/archive/2026-09-09-account-platform-mvp/. All 40 tasks complete, 14/14 spec scenarios passing (HS256 rejection gap closed by commit 44901fd). Archive report persisted to both filesystem and Engram (topic: sdd/account-platform-mvp/archive-report).
…rchive chore(openspec): close out account-platform-mvp — verify gap fix + archive
…1-jwks feat(security): migrate access token signing from HS256 to RS256 with JWKS endpoint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.