Skip to content

feat(auth-permission): add expressRegisterPermissionAuth for Express-based services - #67

Merged
GabrielMachado11 merged 3 commits into
mainfrom
feat/express-auth-permission
Sep 16, 2026
Merged

GabrielMachado11 merged 3 commits into
mainfrom
feat/express-auth-permission

Conversation

@GabrielMachado11

Copy link
Copy Markdown
Contributor

Summary

wave-usage-api (BFF/API/Accounts, all Express) and potentially other wave-*-api services can't use registerPermissionAuth as-is β€” it's Fastify-only. This ships the Express-compatible sibling, expressRegisterPermissionAuth, on a new @wave-tech/framework/auth-permission/express subpath, sharing the same underlying permission/session-token/broker-scope logic instead of duplicating it.

  • Generalized shared layer: readApiKey/readBearerToken/readBrokerId/resolveBrokerScope (renamed from resolveBrokerContext), checkApiKeyPermission, authenticateWithSessionToken now operate on a minimal WaveRequest shape (headers, rawHeaders, query) instead of FastifyRequest. A real Express Request satisfies it natively; Fastify gets one small per-request adapter (to-wave-request.ts) since it nests rawHeaders under .raw.
  • Pure functions, not mutation: checkApiKeyPermission/authenticateWithSessionToken now return the resolved BrokerContext (or undefined) instead of writing request.brokerContext directly. Each framework's own orchestrator assigns it onto its own native request β€” no observable behavior change for the existing Fastify hook, covered by the unmodified register-permission-auth.spec.ts suite.
  • expressRegisterPermissionAuth: mirrors the Fastify hook's contract (public-path bypass, credential-before-routing, deny-by-default, brokerScoped/acceptsSessionToken support) with two documented, deliberate divergences:
    • publicPaths defaults to [], not Fastify's β€” this middleware is meant for a sub-router, where req.path is router-relative (GET /api arrives as /), so reusing Fastify's defaults would silently make a router's own root public.
    • An unmapped-but-real path answers 403, never a synthetic 404 β€” Express gives a .use()-mounted middleware no equivalent to Fastify's already-resolved route before real handlers run, and letting a miss fall through unauthenticated would defeat deny-by-default.
  • Route resolution uses path-to-regexp against the same "METHOD path" map, matching in the map's own insertion order β€” mirroring Express's real first-registered-wins tie-break on overlapping patterns (a real, unavoidable overlap between two fixed paths already exists in wave-usage-api's own bff module: /accounts/by-external-code/:externalCode vs /accounts/:accountId/balances).
  • Separate subpath (not the existing ./auth-permission barrel) specifically so an Express-only consumer never pulls in @fastify/rate-limit, which register-permission-auth.ts imports as a value. No rate-limiting option on the Express side β€” that's Fastify-only for now.
  • express/@types/express move from a regular dependency/devDependency to an optional peer dependency (mirroring the existing fastify pattern), @types/express aligned to the 4.x line to match the actual express runtime version.

This is Part 1 of a two-part effort (see internal plan) β€” wiring this into wave-usage-api itself is a separate, later PR.

Test plan

  • pnpm run lint
  • pnpm test β€” 237/237 passing, including 16 new tests (express-register-permission-auth.spec.ts, route-matcher.spec.ts) and the existing register-permission-auth.spec.ts suite unchanged and still green after the pure-function refactor
  • pnpm run build β€” confirmed dist/auth-permission/express/{index.js,index.d.ts} exist and that subpath's compiled output has zero require('fastify')/require('@fastify/rate-limit') in its dependency graph

πŸ€– Generated with Claude Code

GabrielMachado11 and others added 3 commits September 16, 2026 15:10
…e shared logic

wave-usage-api (and other Express-based wave-*-api services) can't use
registerPermissionAuth as-is: it's Fastify-only (app.addHook, request
.routeOptions.url). Generalizes the credential/broker-scope logic to a
minimal transport-agnostic WaveRequest shape (headers, rawHeaders, query)
that a real Express Request satisfies natively, and that Fastify's request
converts into via one small adapter (to-wave-request.ts) since it nests
rawHeaders under .raw. checkApiKeyPermission/authenticateWithSessionToken
become pure β€” they return a BrokerContext instead of mutating the request β€”
so each framework's own orchestrator assigns it onto its own native request
object.

Adds expressRegisterPermissionAuth as a new @wave-tech/framework/auth-permission/express
subpath (kept separate from the main auth-permission subpath specifically
so an Express-only consumer never pulls in @fastify/rate-limit, which
register-permission-auth.ts imports as a value). Since Express gives a
.use()-mounted middleware no equivalent to Fastify's already-resolved
routeOptions.url, route resolution is done with path-to-regexp against the
same "METHOD path" route map, matching in the map's own insertion order β€”
mirroring Express's own first-registered-wins tie-break on overlapping
patterns exactly, rather than trying to reject overlap as invalid (a real,
unavoidable overlap between two fixed API paths already exists in
wave-usage-api's own bff module). No rate-limiting option on the Express
side: @fastify/rate-limit is Fastify-only.

express and @types/express move from a regular dependency/devDependency to
an optional peer dependency (mirroring the existing fastify pattern), and
@types/express is aligned to the 4.x line to match the express runtime
version actually in use.

No observable behavior change for existing Fastify consumers β€” the pure-
function refactor is covered by the existing register-permission-auth.spec.ts
suite unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
register-permission-auth.ts(+spec), to-wave-request.ts and rate-limit/ only
ever work with Fastify β€” moves them into fastify/, mirroring express/, so
each transport's own pieces sit together and the shared/api-key/session-token
folders stay unambiguously framework-neutral. Import-path-only change, no
behavior difference β€” the public @wave-tech/framework/auth-permission export
is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@GabrielMachado11
GabrielMachado11 merged commit cabbdef into main Sep 16, 2026
6 checks passed
@GabrielMachado11
GabrielMachado11 deleted the feat/express-auth-permission branch September 16, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant