@teamamw/auth
v0.2.0
Published
AMW shared auth middleware — JWT verify with cross-app claim-fallback + cache-revalidation
Downloads
705
Readme
@amw/auth
Shared auth middleware for AMW Express apps. Solves the recurring JWT-claim-divergence, cache-then-trust workspace leak, super-admin gate inconsistency, and timing-oracle bugs surfaced by autonomous-sweep r11/r12 (May 23 2026).
Install
npm install @amw/authQuick start
import { requireAuth, requireSuperAdmin, AmwAuthenticatedRequest } from '@amw/auth'
app.use('/api', requireAuth())
app.use('/api/admin', requireAuth(), requireSuperAdmin)
app.get('/api/me', (req: AmwAuthenticatedRequest, res) => {
res.json({ userId: req.user!.id, tenantId: req.tenantId })
})Why this package exists
Before centralization, every AMW Express app rolled its own auth middleware. Each had subtle divergence:
- amw-workers read
decoded.workspaceIdonly; CRM mintsdecoded.tenantId— silent fall-back to AMW data on AUDIT super-admin probes - amw-marketing trusted cached
users.workspace_idwithout re-validating against current JWT — verified live leak (commitecc0dae) - amw-content-factory had the same cache shape — verified live leak (commit
ce0366c) - inconsistent
isSuperAdminchecks across routes — some gated onrole === 'super_admin', some onisSuperAdmin === true
This package is the single source of truth. Memory: feedback_amw-jwt-claim-name-tenantId-vs-workspaceId, feedback_auto-provision-then-cache-workspace-leak.
API
verifyJwt(token, opts?)
Verify a JWT against JWT_SECRET. Returns a normalized AmwJwtPayload with both tenantId AND workspaceId set to the same resolved value (prevents the silent-fallback bug).
requireAuth(opts?) middleware
Reads token from Authorization: Bearer <t> OR ?token=<t> query param. Attaches req.user + req.tenantId. 401s on missing/invalid/expired. Set opts.optional: true to skip auth-required gating (useful for routes with optional personalization).
requireSuperAdmin middleware
Place AFTER requireAuth. Accepts BOTH isSuperAdmin: true claim AND legacy role === 'super_admin'.
revalidateCachedWorkspace(cached, jwt, resolveExpected)
For apps that cache users.workspace_id and look up by decoded.userId on every request. Re-validates that cached workspace matches what current JWT resolves to. Returns boolean — caller 403s on false.
timingSafeStringEqual(a, b)
Constant-time string compare. Use instead of === on any sensitive token (access codes, API keys, signatures).
Constants
AMW_TENANT_ID—00000000-0000-0000-0000-000000000001AUDIT_TENANT_ID—d9f7c173-221e-4b84-be85-690eda64aba1(test super-admin)
License
MIT
