Files
suaveplan/openspec/archive/2026-04-24-enterprise-auth/tasks.md
2026-05-27 23:29:53 +01:00

10 KiB

Tasks: enterprise-auth

Change ID: enterprise-auth Package: @suaveplan/auth Target path: universal/auth/auth Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/crypto, @suaveplan/crypto-keys, @suaveplan/logger, @suaveplan/retry, @suaveplan/security-codes, @suaveplan/idempotency


1. Scaffold

  • Create directory universal/auth/auth/ with src/, tests/, src/__benchmarks__/, and all feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0 with:
    • "type": "module", "sideEffects": false
    • exports map with a top-level "." barrel plus one subpath per feature module ("./adapters/auth-adapter/auth-adapter", "./adapters/memory-adapter/memory-adapter", "./api-keys/api-keys/api-keys", "./jwt/token-lifecycle/token-lifecycle", "./middleware/auth-middleware/auth-middleware", "./schemas/auth.schemas", "./types/auth.types")
    • dependencies: @suaveplan/error workspace:^, @suaveplan/types workspace:^, @suaveplan/events workspace:^, @suaveplan/crypto workspace:^, @suaveplan/crypto-keys workspace:^, @suaveplan/logger workspace:^, @suaveplan/retry workspace:^, @suaveplan/security-codes workspace:^, @suaveplan/idempotency workspace:^, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build
    • devDependencies: vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, fast-check catalog:testing, mitata catalog:testing, @suaveplan/testing workspace:^
  • Write tsconfig.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig() from @suaveplan/testing/config (pool: forks).
  • Add lint, typecheck, test, test:coverage, build, docs:check, bench scripts to package.json.

2. Types and errors

2a. Types (src/types/auth.types.ts + src/types/auth.types.md)

  • Define all public interfaces: ApiKey, ApiKeyRequest, ApiKeyGeneration, ApiKeyValidationResult, JWTPayload, AuthenticatedJWTRequest, TokenValidationResult, UserContext, AuthenticatedRequest, PermissionCheckResult, SessionInfo, ApiKeyOptions, JWTOptions, MiddlewareOptions, ApiKeyMiddlewareOptions, MiddlewareFn.
  • Add genesis-only optional fields: clock?: Clock and idGenerator?: IdGenerator on all factory option interfaces; bus?: EventBusLike on ApiKeyOptions, JWTOptions, MiddlewareOptions.
  • Write src/types/auth.types.md (≥200 words, simple + advanced examples, full API reference for every type).

2b. Error codes (src/error-codes.ts)

  • Define AuthErrorCode union — ten codes: AUTH_TOKEN_INVALID, AUTH_TOKEN_EXPIRED, AUTH_TOKEN_BLACKLISTED, AUTH_SESSION_NOT_FOUND, AUTH_SESSION_EXPIRED, AUTH_API_KEY_INVALID, AUTH_API_KEY_EXPIRED, AUTH_API_KEY_REVOKED, AUTH_PERMISSION_DENIED, AUTH_STORAGE_ERROR.
  • Define AUTH_ERROR_METADATA with httpStatus, severity, retryable, userMessageKey, category, description for each code.
  • Export registerAuthErrorCodes() — calls ErrorCodes.registerMany(AUTH_ERROR_METADATA). Call as eager side-effect at file bottom.
  • Export AuthError class extending SuaveplanError<AuthErrorCode>.

2c. Zod schemas (src/schemas/auth.schemas.ts + src/schemas/auth.schemas.md)

  • Write Zod schemas: ApiKeyRequestSchema, JWTOptionsSchema, ApiKeyOptionsSchema, MiddlewareOptionsSchema, AuthAdapterConfigSchema.
  • Write src/schemas/auth.schemas.md.
  • Write src/schemas/auth.schemas.test.ts — parse valid/invalid for each schema.

3. AuthAdapter interface

  • Implement AuthAdapter interface in src/adapters/auth-adapter/auth-adapter.ts with the full contract from proposal.md.
  • Write auth-adapter.md (≥200 words, integration guide, expected error behaviour for each method).
  • Write auth-adapter.types.ts.

4. MemoryAuthAdapter

  • Implement MemoryAuthAdapter in src/adapters/memory-adapter/memory-adapter.ts:
    • Map-backed storage for API keys (by hash), token blacklist (by jti + TTL), sessions (by sessionId).
    • TTL cleanup: expired tokens removed lazily on lookup (no setInterval at module scope).
    • Accepts optional { clock?: Clock } constructor option for deterministic expiry checks.
  • Write memory-adapter.md.
  • Write memory-adapter.test.ts — store/find/revoke API keys, blacklist tokens, create/find/delete sessions, TTL expiry with injected FakeClock, list operations. Each it has { timeout: 5000 }.

5. API key management

File: src/api-keys/api-keys/api-keys.ts

  • Implement createApiKey(request, options?):
    • Generate rawKey using @suaveplan/crypto's randomBytes (32 bytes, base58-encoded).
    • Compute HMAC-SHA256 of rawKey with a secret from options; store the hash (never the plaintext).
    • Accept idGenerator for key ID, clock for createdAt.
    • Emit auth:apikey:created on optional bus.
    • Instrument with auth.apikey.create OTel span.
  • Implement validateApiKey(rawKey, options?):
    • Hash the incoming key and look up via adapter; check revoked/expired status.
    • Update lastUsedAt on successful validation.
    • Instrument with auth.apikey.validate OTel span, auth.apikey_validate_total counter.
  • Implement revokeApiKey, listApiKeys, rotateApiKey, updateApiKeyPermissions, hasPermission, validateApiKeyMiddleware.
  • Write api-keys.md (≥200 words, security model explanation, rotation guide).
  • Write api-keys.test.ts (≤500 lines): creation/validation round-trip, revocation, rotation, expiry, permission check, middleware path. Each it has { timeout: 5000 }.

6. JWT token lifecycle

File: src/jwt/token-lifecycle/token-lifecycle.ts

  • Implement validateSessionToken(token, options?):
    • Decode and verify JWT signature using @suaveplan/crypto-keys.
    • Check expiry via injectable clock; check blacklist via adapter.
    • Return TokenValidationResult with structured error reason.
    • Instrument with auth.token.verify span.
  • Implement refreshToken(refreshToken, options?):
    • Validate refresh token; issue new access + refresh token pair.
    • Blacklist the old refresh token jti.
    • Emit auth:token:issued event on bus.
  • Implement revokeToken, revokeAllUserTokens, isTokenBlacklisted, getUserSessions, verifyTokenMiddleware, optionalTokenMiddleware.
  • Write token-lifecycle.md (≥200 words, refresh flow diagram, blacklist strategy).
  • Write token-lifecycle.test.ts (≤500 lines): issue/verify round-trip, expired token, blacklisted token, refresh flow, revoke all, middleware verify/optional paths. { timeout: 5000 } on sync, { timeout: 30000 } on async.

7. Permission middleware

File: src/middleware/auth-middleware/auth-middleware.ts

  • Implement getUserFromSession, checkPermission, requirePermission, requireAllPermissions, requireAnyPermission, withPermissions.
  • checkPermission uses exact string match and wildcard * (e.g. resource:* grants all actions on resource).
  • All middleware functions return framework-agnostic MiddlewareFn; include a createMiddlewareAdapter bridge for Hono and Node.
  • Instrument auth.permission.check histogram (duration_ms).
  • Write auth-middleware.md (≥200 words, wildcard semantics, framework adapter guide).
  • Write auth-middleware.test.ts (≤500 lines): exact match, wildcard match, requireAll/requireAny combinations, denied path, session lookup failures. { timeout: 5000 } on each it.

8. OTel instrumentation (cross-cutting review)

  • Confirm all spans, counters, and histograms listed in proposal.md §5 are wired in each module.
  • Add tests/otel.test.ts: wire in-memory OTel exporter; assert counter increments for key create, key validate, token issue, token verify, session create, permission check.

9. Property-based tests (tests/property.test.ts)

Use fast-check. All test timeouts { timeout: 30000 }.

  • API key round-trip: for any valid ApiKeyRequest, createApiKey then validateApiKey returns valid === true.
  • Blacklist monotonicity: once a jti is blacklisted via revokeToken, all subsequent isTokenBlacklisted calls return true regardless of adapter state mutations.
  • Permission check AND: checkPermission with requireAllPermissions([a, b]) is true iff both individual checks are true for arbitrary permission strings and user contexts.
  • MemoryAdapter eviction: after TTL elapses (injected FakeClock advanced), isBlacklisted returns false for expired entries.

10. Benchmarks (src/__benchmarks__/auth.bench.ts)

Use mitata.

  • createApiKey_throughput: 1 000 iterations against MemoryAuthAdapter.
  • validateApiKey_warm: 10 000 lookups on an adapter with 1 000 pre-populated keys.
  • validateSessionToken_hmac: 10 000 verifications of a pre-issued token.
  • Document placeholder numbers in README.md under ## Benchmarks.

11. Docs

Per-file .md (all ≥200 words, simple + advanced examples, API reference):

  • src/types/auth.types.md
  • src/adapters/auth-adapter/auth-adapter.md
  • src/adapters/memory-adapter/memory-adapter.md
  • src/api-keys/api-keys/api-keys.md
  • src/jwt/token-lifecycle/token-lifecycle.md
  • src/middleware/auth-middleware/auth-middleware.md
  • src/schemas/auth.schemas.md

Root README:

  • Write universal/auth/auth/README.md: purpose, install, quick-start (API key + JWT round-trip), modules table, dependencies, API listing, benchmarks placeholder.

12. Verify and publish

  • Run bunx turbo lint --filter=@suaveplan/auth — zero errors.
  • Run bunx turbo typecheck --filter=@suaveplan/auth — zero errors.
  • Run bunx turbo test --filter=@suaveplan/auth — all tests pass.
  • Run bunx turbo test:coverage --filter=@suaveplan/auth — 100/100/100/100.
  • Run bun run docs:check --filter=@suaveplan/auth — zero errors.
  • Confirm no TODO, FIXME, @deprecated, untyped any, ts-ignore, or .skip in src/ or tests/.
  • Confirm "sideEffects": false in package.json.
  • Run bun scripts/publish.ts --package @suaveplan/auth.
  • Update CLAUDE.md package listing to include @suaveplan/auth.
  • Archive: move openspec/changes/enterprise-auth/ to openspec/archive/2026-04-24-enterprise-auth/ and fold spec delta into openspec/specs/auth/spec.md.