Skip to content

Develop - #29

Open
JSisques wants to merge 21 commits into
stagingfrom
develop
Open

JSisques wants to merge 21 commits into
stagingfrom
develop

Conversation

@JSisques

@JSisques JSisques commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

No description provided.

JSisques and others added 7 commits September 4, 2026 13:34
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
@github-actions github-actions Bot added documentation Docs-only changes tests Test-only changes dependencies Dependency updates labels Sep 6, 2026
JSisques and others added 14 commits September 9, 2026 12:50
… 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Dependency updates documentation Docs-only changes tests Test-only changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant