dauth-node
v6.3.3
Published
Express middleware for JWT verification and session management against the Dauth authentication service
Maintainers
Readme
dauth-node
⚠️ Package renamed — this is now published as
dauth-node(wasdauth-md-node). The old namedauth-md-nodewas topped at v5.3.2 and is no longer maintained. If yourpackage.jsonsaysdauth-md-node, replace it withdauth-node@^6— the old package does not sendredirectUriinPOST /exchange-codeand causes silent400 code-invalidlogin loops that are invisible in the browser. The fix lives only indauth-node@^6. Migration:pnpm remove dauth-md-node pnpm add dauth-node@^6 # then update imports in your source: "from 'dauth-md-node'" → "from 'dauth-node'" # "from 'dauth-md-node/router'" → "from 'dauth-node/router'"No API surface change — same exports, same options, same route paths. Only the name and the redirectUri forwarding differ.
Before you integrate: read the Integration Guide — New Tenant End-to-End. It covers the six pieces every integration needs to get right: using
dauth-nodeinstead ofdauth-md-node(section 0), environment matrix (see below), TSK handling (never print to stdout), router mount order (/api/authopen →/api/v1protected),credentials: 'include'on consumer fetches, andallowedOriginson the Domain. 90% of integration bugs are one of those six missing.
Express middleware and router for JWT-based authentication against the dauth service. Two exports: dauth() middleware that verifies JWTs (via Authorization header or encrypted session cookies) and attaches req.user, and dauthRouter() that provides pre-built session-based auth routes with encrypted cookies, CSRF protection, and automatic token refresh.
Installation
npm install dauth-node
# or
pnpm add dauth-node@^6Do not install dauth-md-node — that is the old name, stuck at v5.3.2, and will silently break login via 400 code-invalid loops. See the banner at the top of this README.
For session cookie mode or the router, also install cookie-parser:
pnpm add cookie-parserTech Stack
| Technology | Version | Purpose | | ------------- | --------- | ------------------------------------------------ | | Node.js | >= 18 | Runtime (native fetch required) | | TypeScript | 5.9 | Type safety | | jsonwebtoken | 9 | JWT verification (runtime dependency) | | Express | 4.18+ / 5 | Peer dependency | | cookie-parser | 1.4+ | Optional peer dependency (session/router mode) | | tsup | 8 | Build tool (CJS + ESM bundles, two entry points) | | vitest | 4 | Testing framework | | size-limit | 12 | Bundle size budget (10KB per entry) |
Usage
Middleware (Authorization header mode)
import express from 'express';
import { dauth, IRequestDauth } from 'dauth-node';
const app = express();
const dauthMiddleware = dauth({
domainName: 'your-domain-name',
tsk: 'your-tenant-secret-key',
});
app.get('/api/protected', dauthMiddleware, (req, res) => {
res.json({ user: (req as IRequestDauth).user });
});Middleware (session cookie mode)
import express from 'express';
import cookieParser from 'cookie-parser';
import { dauth } from 'dauth-node';
const app = express();
app.use(cookieParser());
const dauthMiddleware = dauth({
domainName: 'your-domain-name',
tsk: 'your-tenant-secret-key',
session: {},
});
app.get('/api/protected', dauthMiddleware, (req, res) => {
res.json({ user: req.user });
});Router (session-based auth routes)
import express from 'express';
import cookieParser from 'cookie-parser';
import { dauthRouter } from 'dauth-node/router';
const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(
'/api/auth',
dauthRouter({
domainName: 'your-domain-name',
tsk: 'your-tenant-secret-key',
})
);
// Available routes:
// POST /api/auth/exchange-code
// GET /api/auth/session
// POST /api/auth/logout (CSRF required)
// PATCH /api/auth/user (CSRF required)
// DELETE /api/auth/user (CSRF required)
// GET /api/auth/profile-redirect (CSRF required)API Reference
dauth(options: DauthOptions)
Returns an Express middleware.
| Parameter | Type | Required | Default | Description |
| ------------ | ------------------- | -------- | ------- | -------------------------------------- |
| domainName | string | yes | - | Tenant identifier (used in API path) |
| tsk | string | yes | - | Tenant Secret Key for JWT verification |
| cache | { ttlMs: number } | no | - | Enable in-memory user cache with TTL |
| session | SessionOptions | no | - | Enable session cookie mode |
SessionOptions:
| Parameter | Type | Default | Description |
| ------------- | --------- | ----------------------------------------------------- | ------------------------------------------- |
| cookieName | string | __Host-dauth-session (prod) / dauth-session (dev) | Session cookie name |
| secure | boolean | true (except NODE_ENV=development) | Cookie Secure flag |
| previousTsk | string | - | Previous TSK for zero-downtime key rotation |
| sessionSalt | string | built-in default | Custom HKDF salt (hex string) |
Without session: extracts Authorization header, verifies JWT with tsk, fetches user from dauth backend, attaches to req.user.
With session: reads encrypted session cookie, decrypts with key derived from tsk (falls back to previousTsk), extracts access token, verifies JWT, fetches user, attaches to req.user.
dauthRouter(options: DauthRouterOptions)
Returns an Express Router. Requires cookie-parser middleware. Import from dauth-node/router.
| Parameter | Type | Default | Description |
| ---------------- | --------- | ---------------------------------------- | ----------------------------- |
| domainName | string | (required) | Tenant identifier |
| tsk | string | (required) | Tenant Secret Key |
| dauthUrl | string | auto-detected | Override dauth backend URL |
| cookieName | string | __Host-dauth-session / dauth-session | Session cookie name |
| csrfCookieName | string | __Host-csrf / csrf-token | CSRF cookie name |
| maxAge | number | 2592000 (30 days) | Cookie max age in seconds |
| secure | boolean | true (except dev) | Cookie Secure flag |
| previousTsk | string | - | Previous TSK for key rotation |
| sessionSalt | string | built-in default | Custom HKDF salt (hex string) |
Router routes:
| Method | Path | CSRF | Description |
| -------- | ------------------- | ---- | ----------------------------------------------------------------------------------- |
| POST | /exchange-code | No | Exchange auth code for session, set cookies, return { user, domain, isNewUser } |
| GET | /session | No | Check session, auto-refresh if token expires within 5min, return { user, domain } |
| POST | /logout | Yes | Revoke refresh token (fire-and-forget), clear cookies |
| PATCH | /user | Yes | Proxy user update to dauth backend with auto-refresh |
| DELETE | /user | Yes | Delete user account, clear cookies |
| GET | /profile-redirect | Yes | Generate profile code, return { redirectUrl } |
CSRF uses the double-submit cookie pattern: POST /exchange-code sets a CSRF cookie (httpOnly: false), and subsequent state-changing requests must include the cookie value as the x-csrf-token header.
Exported types (from dauth-node)
IDauthUser-- user object (name, email, avatar, role, language, authMethods, etc.)AuthMethodType--'magic-link' | 'passkey'IRequestDauth-- extends ExpressRequestwithuser: IDauthUserDauthOptions-- middleware factory optionsSessionOptions-- session cookie configurationUserCache-- cache class (for direct usage/testing)CacheOptions-- cache configuration type
Exported types (from dauth-node/router)
DauthRouterOptions-- router factory options
Environment detection
The middleware resolves the dauth backend URL in this order:
DAUTH_URLenvironment variableNODE_ENV=development->http://localhost:4012/api/v1- Default ->
https://dauth.ovh/api/v1
The router also accepts dauthUrl in options, which takes priority over the env var.
Environment matrix
| Consumer env | NODE_ENV | DAUTH_URL | dauth instance used |
| ------------ | --------------------------- | ---------------------------------------- | ------------------- |
| dev | development | unset (or http://localhost:4012) | local dauth-api |
| staging | staging (or production) | https://dev.dauth.ovh | dauth-staging |
| production | production | https://dauth.ovh (or unset — default) | dauth-production |
Warning — staging without
DAUTH_URLhits production dauth. IfNODE_ENVis notdevelopmentandDAUTH_URLis unset, the middleware defaults tohttps://dauth.ovh. A staging backend with these defaults exchanges codes against production dauth, which does not know your staging tenant. Always setDAUTH_URLexplicitly in staging (and ideally in production too, so a typo is obvious). The full env checklist (DAUTH_DOMAIN_NAME,DAUTH_TSK,DAUTH_URL,DAUTH_PREVIOUS_TSK,COOKIE_DOMAIN,NODE_ENV) is in the Integration Guide.
Error responses (middleware)
| Scenario | Status | Status Field |
| -------------------------------------------- | ------ | ----------------------------------- |
| Missing Authorization header (header mode) | 403 | token-not-found |
| Missing session cookie (session mode) | 401 | no-session |
| Invalid/undecryptable session cookie | 401 | session-invalid |
| JWT expired | 401 | token-expired |
| Invalid JWT or bad TSK | 401 | tsk-not-invalid / token-invalid |
| User not found | 404 | user-not-found |
| Backend server error | 500 | error |
| Other backend status | 501 | request-error |
Scripts
| Script | Description |
| -------------------- | ------------------------------------------------------ |
| pnpm start | Watch mode (tsup --watch) |
| pnpm build | Production build (CJS + ESM + types, two entry points) |
| pnpm test | Run vitest tests (80 tests) |
| pnpm test:coverage | Coverage report (v8, 70% threshold) |
| pnpm typecheck | TypeScript type checking |
| pnpm size | Check bundle size (10KB budget per entry) |
| pnpm format | Prettier formatting |
Testing
80 tests across 7 files using vitest 4 (node environment).
pnpm test
pnpm test:coveragePublishing
Automated via GitHub Actions. Push a v* tag to trigger build, test, and npm publish.
# 1. Bump version in package.json
# 2. Commit and tag
git tag v4.0.1
git push origin main --tags
# 3. GitHub Actions publishes to npm automaticallyRequires NPM_TOKEN secret configured in the GitHub repo.
Code Style
Prettier: 80 char width, single quotes, semicolons, trailing commas (es5), 2-space indent.
License
MIT
