Identity & Auth
The @json-express/plugin-identity package turns JSONExpress from an open API into a zero-trust backend. It is auto-discovered — install it (along with its peers) and the json-express runtime registers it for you. There is no manual new IdentityPlugin(...) wiring.
What it does on boot
- Contributes a strict
usersschema that blockspasswordHashandtokenVersionfrom ever appearing in API responses. - Hashes passwords with argon2id through
beforeCreate/beforeUpdatehooks — the plain-text value never reaches the database. - Mounts the
/auth/*route group (login, register, refresh, logout, password change, plus the email-driven flows when an email provider is installed). - Auto-seeds an admin when the
userscollection is empty — the generated password is logged once on first boot. - Validates installed peers: it throws on boot if
middleware-author anIKvStoreis missing.
The Zero-Knowledge Provisioning Flow
Plain-text passwords are never accepted via generic REST POST / PATCH /users requests:
- An admin creates a user via
POST /userswith onlyemailandrole. - The user is stored with
emailVerified: falseand no password. - A TTL-bound reset token is written to the
IKvStore. - The reset email is enqueued on the
IQueueAdapterand sent asynchronously. - The user follows the link and submits their password to
POST /auth/password/reset, which performs the argon2id hashing.
Administrators and API logs never see user passwords.
JWT Verification & Security
The plugin partners with @json-express/middleware-auth to verify the tokens it issues. Both sides read the same keys from the config provider — there is no cross-plugin import.
Symmetric (HMAC) vs Asymmetric (JWKS)
For local development, jex.auth.secret is enough. For production, point both plugins at a JWKS endpoint and JSONExpress acts as a pure resource server validating tokens issued by an external IdP (Auth0, Cognito, Okta, …).
# .env
# Local development — HMAC
jex.auth.secret=a-strong-32-byte-secret
# Production — JWKS
jex.auth.jwksUri=https://your-tenant.us.auth0.com/.well-known/jwks.json
jex.auth.algorithms=RS256
# Public paths the verifier must skip
jex.auth.exclude=/auth,/publicThe tokenVersion Pattern (instant revocation)
Stateless JWTs cannot normally be revoked without a centralized blocklist. JSONExpress instead stamps each token with the user's current tokenVersion integer (a non-readable field on the user record). When you need to revoke, increment that user's tokenVersion — every previously issued JWT becomes invalid on the next request because the stamped version no longer matches the database value. No blocklist, no cache invalidation, no extra storage.
Admin Auto-Seeding
When the plugin boots and the users collection is empty, it creates an admin@local account with role admin and a randomly generated password. The password is printed once to the configured logger.
WARNING
Change the auto-generated admin password immediately on first boot via POST /auth/password/change.
There is no ROOT_ADMIN_EMAIL / ROOT_ADMIN_PASSWORD environment variable — the admin email is fixed at admin@local and the password is randomly generated each fresh boot.
Common Questions
Where are refresh tokens stored?
Refresh tokens, email verification tokens, and password reset tokens are not stored in your primary database. They live in the IKvStore (@json-express/kv-memory for development; a Redis-backed implementation in production). This keeps the primary database free of ephemeral records and offloads TTL expiration to native KV commands.
Can I use Auth0 / Firebase / Cognito instead of issuing my own tokens?
Yes — set jex.auth.jwksUri to your IdP's JWKS endpoint. middleware-auth will validate every incoming JWT against those keys. You can keep plugin-identity installed for the users schema and admin endpoints, or remove it entirely and rely on the IdP's user store.
How do I install the whole stack in one command?
npm install @json-express/preset-identityThe @json-express/preset-identity preset bundles plugin-identity, middleware-auth, kv-memory, queue-memory, and email-console.