expediate 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/CONTRIBUTING.md +150 -0
- package/LICENSE +16 -16
- package/README.md +330 -444
- package/dist/apis.d.ts +504 -27
- package/dist/apis.d.ts.map +1 -1
- package/dist/apis.js +618 -107
- package/dist/apis.js.map +1 -1
- package/dist/cjs/index.js +4066 -0
- package/dist/cjs/package.json +1 -0
- package/dist/git.d.ts +72 -9
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +129 -74
- package/dist/git.js.map +1 -1
- package/dist/http-objects.d.ts +26 -0
- package/dist/http-objects.d.ts.map +1 -0
- package/dist/http-objects.js +588 -0
- package/dist/http-objects.js.map +1 -0
- package/dist/index.d.ts +18 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -24
- package/dist/index.js.map +1 -1
- package/dist/jwt-auth.d.ts +158 -57
- package/dist/jwt-auth.d.ts.map +1 -1
- package/dist/jwt-auth.js +447 -207
- package/dist/jwt-auth.js.map +1 -1
- package/dist/middleware.d.ts +476 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +647 -0
- package/dist/middleware.js.map +1 -0
- package/dist/mimetypes.json +882 -1
- package/dist/misc.d.ts +268 -25
- package/dist/misc.d.ts.map +1 -1
- package/dist/misc.js +449 -168
- package/dist/misc.js.map +1 -1
- package/dist/openapi.d.ts +433 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +624 -0
- package/dist/openapi.js.map +1 -0
- package/dist/router-types.d.ts +760 -0
- package/dist/router-types.d.ts.map +1 -0
- package/dist/router-types.js +23 -0
- package/dist/router-types.js.map +1 -0
- package/dist/router.d.ts +37 -201
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +502 -244
- package/dist/router.js.map +1 -1
- package/dist/static.d.ts +3 -3
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +164 -105
- package/dist/static.js.map +1 -1
- package/docs/THREAT_MODEL.md +52 -0
- package/docs/api-builder-v2-design.md +644 -0
- package/docs/api-builder-v3-design.md +397 -0
- package/docs/api-builder.md +454 -0
- package/docs/benchmark.md +27 -0
- package/docs/body-parsing.md +223 -0
- package/docs/errors.md +359 -0
- package/docs/expediate.png +0 -0
- package/docs/git.md +139 -0
- package/docs/jwt-auth.md +251 -0
- package/docs/logo.svg +12 -0
- package/docs/middleware.md +264 -0
- package/docs/openapi.md +180 -0
- package/docs/router.md +356 -0
- package/docs/static.md +128 -0
- package/docs/wiki.json +123 -0
- package/package.json +47 -8
- package/.npmignore +0 -16
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AAGH,OAAO,YAAY,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AAGH,OAAO,YAAY,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,YAAY,EACV,MAAM,EACN,aAAa,EACb,aAAa,EACb,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,KAAK,EACL,SAAS,EACT,YAAY,EACZ,aAAa,EACb,UAAU,EACV,SAAS,GACV,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EACV,aAAa,EACb,IAAI,EACL,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAChI,YAAY,EACV,WAAW,EACX,eAAe,EACf,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,OAAO,eAAe,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,UAAU,GACX,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EACR,iBAAiB,GACpB,MAAM,UAAU,CAAC;AAGlB,OAAO,UAAU,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,YAAY,EACR,QAAQ,EACR,aAAa,EACb,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,EACL,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,mBAAmB,EACnB,UAAU,GACb,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACnF,YAAY,EACR,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtH,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,sBAAsB,GACvB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/* Copyright 2021 Fabien Bavent
|
|
3
2
|
*
|
|
4
3
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
@@ -23,33 +22,25 @@
|
|
|
23
22
|
* @module expediate
|
|
24
23
|
* TypeScript package for web server routing.
|
|
25
24
|
*/
|
|
26
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
-
};
|
|
29
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.apiBuilder = exports.gitHandler = exports.createJwtPlugin = exports.logger = exports.parseBody = exports.formData = exports.json = exports.mime = exports.sendFile = exports.serveFile = exports.serveStatic = exports.createRouter = void 0;
|
|
31
25
|
// ── Router ────────────────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
import createRouter from './router.js';
|
|
27
|
+
export { createRouter };
|
|
34
28
|
// ── Static ────────────────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
Object.defineProperty(exports, "serveStatic", { enumerable: true, get: function () { return static_1.serveStatic; } });
|
|
37
|
-
Object.defineProperty(exports, "serveFile", { enumerable: true, get: function () { return static_1.serveFile; } });
|
|
38
|
-
Object.defineProperty(exports, "sendFile", { enumerable: true, get: function () { return static_1.sendFile; } });
|
|
39
|
-
Object.defineProperty(exports, "mime", { enumerable: true, get: function () { return static_1.mime; } });
|
|
29
|
+
export { serveStatic, serveFile, sendFile, mime } from './static.js';
|
|
40
30
|
// ── Miscallenous ──────────────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
Object.defineProperty(exports, "json", { enumerable: true, get: function () { return misc_1.json; } });
|
|
43
|
-
Object.defineProperty(exports, "formData", { enumerable: true, get: function () { return misc_1.formData; } });
|
|
44
|
-
Object.defineProperty(exports, "parseBody", { enumerable: true, get: function () { return misc_1.parseBody; } });
|
|
45
|
-
Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return misc_1.logger; } });
|
|
31
|
+
export { json, formData, formEncoded, raw, text, parseBody, logger, cors, streamFormData, parseMultipartBody } from './misc.js';
|
|
46
32
|
// ── JWT Authentication ────────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
import createJwtPlugin from './jwt-auth.js';
|
|
34
|
+
export { createJwtPlugin };
|
|
35
|
+
export { createMapTokenStore } from './jwt-auth.js';
|
|
49
36
|
// ── Git repository ────────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
exports.gitHandler = git_1.default;
|
|
37
|
+
export { gitHandler, gitCreate } from './git.js';
|
|
52
38
|
// ── API Service ───────────────────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
import apiBuilder from './apis.js';
|
|
40
|
+
export { apiBuilder };
|
|
41
|
+
export { defineController } from './apis.js';
|
|
42
|
+
// ── OpenAPI spec generation ───────────────────────────────────────────────────
|
|
43
|
+
export { describe, openApiSpec, serializeSpec, DESCRIBE_META } from './openapi.js';
|
|
44
|
+
// ── Middleware ────────────────────────────────────────────────────────────────
|
|
45
|
+
export { compress, requestId, rateLimit, cacheControl, csrf, securityHeaders, conditionalGet } from './middleware.js';
|
|
55
46
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;GAGG;AAEH,iFAAiF;AACjF,OAAO,YAAY,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,CAAA;AAmBvB,iFAAiF;AAEjF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAMrE,iFAAiF;AAEjF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAWhI,iFAAiF;AACjF,OAAO,eAAe,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAUpD,iFAAiF;AACjF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAKjD,iFAAiF;AACjF,OAAO,UAAU,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAiB7C,iFAAiF;AACjF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAiBnF,iFAAiF;AACjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/jwt-auth.d.ts
CHANGED
|
@@ -4,20 +4,35 @@
|
|
|
4
4
|
* JWT authentication plugin for the Expediate router.
|
|
5
5
|
*
|
|
6
6
|
* Provides:
|
|
7
|
-
* - Stateless access tokens
|
|
8
|
-
*
|
|
7
|
+
* - Stateless access tokens signed with HS256/HS384/HS512 (shared secret) or
|
|
8
|
+
* RS256/RS384/RS512/ES256/ES384/ES512 (asymmetric PEM key pairs).
|
|
9
|
+
* - Signed JWT refresh tokens with JTI-based server-side revocation.
|
|
9
10
|
* - Route handlers for login, token refresh, and logout.
|
|
10
11
|
* - Middleware for token validation, authorisation, role checks, and
|
|
11
12
|
* permission checks.
|
|
13
|
+
* - A `createMapTokenStore()` factory for in-process token storage.
|
|
12
14
|
*
|
|
13
15
|
* Security notes:
|
|
14
16
|
* - Passwords are hashed with SHA-256 for demonstration purposes only.
|
|
15
17
|
* Replace with bcrypt / argon2 in production.
|
|
16
18
|
* - The default secrets are placeholders — always override them in production.
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
+
* - `createMapTokenStore()` is an in-process store; replace with a Redis or
|
|
20
|
+
* database adapter for multi-instance deployments.
|
|
21
|
+
* - Refresh tokens are only issued when `refreshTokenStore` is configured.
|
|
22
|
+
* Absence of a store disables refresh-token support entirely.
|
|
19
23
|
*/
|
|
20
24
|
import type { Middleware } from './router.js';
|
|
25
|
+
declare module './router.js' {
|
|
26
|
+
interface RouterRequest {
|
|
27
|
+
/**
|
|
28
|
+
* The decoded access-token payload for the authenticated user, set by the
|
|
29
|
+
* JWT plugin's {@link JwtPlugin.authenticate} middleware (and cleared at the
|
|
30
|
+
* start of each `authenticate` run). `undefined` when the request is
|
|
31
|
+
* unauthenticated.
|
|
32
|
+
*/
|
|
33
|
+
user?: TokenPayload;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
/** A user record as stored in (or returned by) the user database. */
|
|
22
37
|
export interface UserRecord {
|
|
23
38
|
/** Stable unique identifier (used as JWT `sub` claim when present). */
|
|
@@ -28,7 +43,7 @@ export interface UserRecord {
|
|
|
28
43
|
* SHA-256 hex digest of the user's password.
|
|
29
44
|
* Replace with a bcrypt/argon2 hash in production.
|
|
30
45
|
*/
|
|
31
|
-
passwordHash
|
|
46
|
+
passwordHash?: string;
|
|
32
47
|
/** Role labels assigned to this user (e.g. `'admin'`, `'editor'`). */
|
|
33
48
|
roles?: string[];
|
|
34
49
|
/**
|
|
@@ -41,13 +56,13 @@ export interface UserRecord {
|
|
|
41
56
|
}
|
|
42
57
|
/**
|
|
43
58
|
* The decoded JWT access-token payload attached to `req.user` after
|
|
44
|
-
* successful authentication.
|
|
59
|
+
* successful authentication. The `sub` claim identifies the user (typically
|
|
60
|
+
* the username or a stable user ID). Custom claims returned by
|
|
61
|
+
* `config.payload` are carried in the index-signature field.
|
|
45
62
|
*/
|
|
46
63
|
export interface TokenPayload {
|
|
47
|
-
/** JWT subject —
|
|
64
|
+
/** JWT subject — the user identifier (username or stable ID). */
|
|
48
65
|
sub: string;
|
|
49
|
-
/** Username extracted from the user record. */
|
|
50
|
-
username: string;
|
|
51
66
|
/** Issuer claim, set to `config.issuer`. */
|
|
52
67
|
iss: string;
|
|
53
68
|
/** Issued-at timestamp (Unix seconds). */
|
|
@@ -61,41 +76,99 @@ export interface TokenPayload {
|
|
|
61
76
|
/** Any additional claims produced by `config.payload`. */
|
|
62
77
|
[key: string]: unknown;
|
|
63
78
|
}
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Metadata stored in the token store for each active refresh token.
|
|
81
|
+
* The key used to address this record is the token's `jti` (JWT ID) claim.
|
|
82
|
+
*/
|
|
83
|
+
export interface RefreshTokenRecord {
|
|
84
|
+
/** Subject the refresh token was issued to (`sub` from the access token). */
|
|
85
|
+
sub: string;
|
|
86
|
+
/** Unix millisecond timestamp of issuance. */
|
|
69
87
|
issuedAt: number;
|
|
70
|
-
/** Unix
|
|
88
|
+
/** Unix millisecond timestamp after which the token must be rejected. */
|
|
71
89
|
expiresAt: number;
|
|
72
90
|
}
|
|
73
91
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
92
|
+
* Async-compatible interface for the refresh-token store.
|
|
93
|
+
*
|
|
94
|
+
* The store is addressed by JWT ID (`jti`) — a UUID v4 unique to each
|
|
95
|
+
* issued refresh token. Every method may return its result either directly
|
|
96
|
+
* or as a Promise, enabling both synchronous (Map) and asynchronous (Redis,
|
|
97
|
+
* database) implementations.
|
|
98
|
+
*
|
|
99
|
+
* Cleanup of expired records is the store's responsibility. The built-in
|
|
100
|
+
* {@link createMapTokenStore} performs lazy cleanup on `get()`.
|
|
77
101
|
*/
|
|
78
102
|
export interface TokenStore {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Persist a new refresh-token record.
|
|
105
|
+
* @param jti - Unique JWT ID of the issued refresh token.
|
|
106
|
+
* @param record - Metadata to store alongside the token.
|
|
107
|
+
*/
|
|
108
|
+
set(jti: string, record: RefreshTokenRecord): void | Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Retrieve the record for a given JTI, or `undefined` if not found or
|
|
111
|
+
* expired. Implementations are encouraged to delete expired records lazily
|
|
112
|
+
* here rather than in a background job.
|
|
113
|
+
* @param jti - JWT ID to look up.
|
|
114
|
+
*/
|
|
115
|
+
get(jti: string): RefreshTokenRecord | undefined | Promise<RefreshTokenRecord | undefined>;
|
|
116
|
+
/**
|
|
117
|
+
* Remove a single record by JTI. Idempotent — deleting a non-existent
|
|
118
|
+
* key is not an error.
|
|
119
|
+
* @param jti - JWT ID to remove.
|
|
120
|
+
*/
|
|
121
|
+
delete(jti: string): void | Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Revoke **all** refresh tokens belonging to a given subject (user).
|
|
124
|
+
* Optional — when absent, per-JTI revocation still works but bulk logout
|
|
125
|
+
* ("log out all sessions") is not available.
|
|
126
|
+
* @param sub - The subject identifier to revoke tokens for.
|
|
127
|
+
*/
|
|
128
|
+
deleteBySubject?(sub: string): void | Promise<void>;
|
|
83
129
|
}
|
|
84
130
|
/**
|
|
85
|
-
* Supported
|
|
86
|
-
*
|
|
131
|
+
* Supported JWT signing algorithms.
|
|
132
|
+
*
|
|
133
|
+
* - **HS256 / HS384 / HS512** — HMAC-SHA family. Uses a shared secret string
|
|
134
|
+
* (`accessTokenSecret` / `refreshTokenSecret`).
|
|
135
|
+
* - **RS256 / RS384 / RS512** — RSA PKCS#1 v1.5 + SHA family. Requires PEM
|
|
136
|
+
* private key for signing and PEM public key for verification.
|
|
137
|
+
* - **ES256 / ES384 / ES512** — ECDSA + SHA family. Requires a PEM private
|
|
138
|
+
* key (P-256 / P-384 / P-521 curve respectively) for signing and the
|
|
139
|
+
* corresponding PEM public key for verification. Signatures are encoded in
|
|
140
|
+
* the compact IEEE P1363 (JOSE) format rather than ASN.1 DER.
|
|
87
141
|
*/
|
|
88
|
-
export type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
|
|
142
|
+
export type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512' | 'ES256' | 'ES384' | 'ES512';
|
|
89
143
|
/**
|
|
90
144
|
* Full configuration object for {@link createJwtPlugin}.
|
|
91
|
-
* All fields
|
|
145
|
+
* All fields except `accessTokenSecret` and `refreshTokenSecret` have defaults.
|
|
92
146
|
*/
|
|
93
147
|
export interface JwtConfig {
|
|
94
|
-
/** HMAC secret used to sign access tokens. **Change in production.** */
|
|
148
|
+
/** HMAC secret used to sign access tokens (HS* algorithms). **Change in production.** */
|
|
95
149
|
accessTokenSecret: string;
|
|
96
|
-
/** HMAC secret used to sign refresh tokens (
|
|
97
|
-
* tokens are opaque random strings, not JWTs). Reserved for future use. */
|
|
150
|
+
/** HMAC secret used to sign refresh tokens (HS* algorithms). **Change in production.** */
|
|
98
151
|
refreshTokenSecret: string;
|
|
152
|
+
/**
|
|
153
|
+
* PEM-encoded **private** key used to sign access tokens (RS* / ES* algorithms).
|
|
154
|
+
* Required when `alg` is RS* or ES*.
|
|
155
|
+
*/
|
|
156
|
+
accessTokenPrivateKey?: string;
|
|
157
|
+
/**
|
|
158
|
+
* PEM-encoded **public** key used to verify access tokens (RS* / ES* algorithms).
|
|
159
|
+
* Required when `alg` is RS* or ES*.
|
|
160
|
+
*/
|
|
161
|
+
accessTokenPublicKey?: string;
|
|
162
|
+
/**
|
|
163
|
+
* PEM-encoded **private** key used to sign refresh tokens (RS* / ES* algorithms).
|
|
164
|
+
* Falls back to `accessTokenPrivateKey` when absent.
|
|
165
|
+
*/
|
|
166
|
+
refreshTokenPrivateKey?: string;
|
|
167
|
+
/**
|
|
168
|
+
* PEM-encoded **public** key used to verify refresh tokens (RS* / ES* algorithms).
|
|
169
|
+
* Falls back to `accessTokenPublicKey` when absent.
|
|
170
|
+
*/
|
|
171
|
+
refreshTokenPublicKey?: string;
|
|
99
172
|
/** Access token lifetime in **seconds**. Defaults to 15 minutes. */
|
|
100
173
|
accessTokenExpiry: number;
|
|
101
174
|
/** Refresh token lifetime in **seconds**. Defaults to 7 days. */
|
|
@@ -112,14 +185,15 @@ export interface JwtConfig {
|
|
|
112
185
|
alg: JwtAlgorithm;
|
|
113
186
|
/**
|
|
114
187
|
* Extract the login username from a user record.
|
|
188
|
+
* Used as the fallback `sub` claim when `payload()` does not set one.
|
|
115
189
|
* Defaults to `(user) => user.username`.
|
|
116
190
|
*/
|
|
117
191
|
username: (user: UserRecord) => string;
|
|
118
192
|
/**
|
|
119
|
-
* Fetch a user record by username.
|
|
193
|
+
* Fetch a user record by subject (username or stable ID).
|
|
120
194
|
* Return `undefined` (or any falsy value) when the user does not exist.
|
|
121
195
|
*/
|
|
122
|
-
fetchUser: (
|
|
196
|
+
fetchUser: (sub: string) => UserRecord | undefined | Promise<UserRecord | undefined>;
|
|
123
197
|
/**
|
|
124
198
|
* Return `true` when the supplied plain-text `password` is valid for
|
|
125
199
|
* `user`, `false` otherwise.
|
|
@@ -127,19 +201,23 @@ export interface JwtConfig {
|
|
|
127
201
|
* The default implementation compares SHA-256 hashes; replace with a
|
|
128
202
|
* timing-safe bcrypt/argon2 check in production.
|
|
129
203
|
*/
|
|
130
|
-
isPasswordValid: (user: UserRecord, password: string) => boolean
|
|
204
|
+
isPasswordValid: (user: UserRecord, password: string) => boolean | Promise<boolean>;
|
|
131
205
|
/**
|
|
132
|
-
* Build the JWT payload for a user.
|
|
133
|
-
* The `iss`, `iat`,
|
|
134
|
-
*
|
|
206
|
+
* Build the JWT access-token payload for a user.
|
|
207
|
+
* The `iss`, `iat`, and `exp` claims are added automatically.
|
|
208
|
+
* When `sub` is absent from the returned object, `config.username(user)`
|
|
209
|
+
* is used as the fallback.
|
|
135
210
|
*/
|
|
136
|
-
payload: (user: UserRecord) => Partial<TokenPayload
|
|
211
|
+
payload: (user: UserRecord) => Partial<TokenPayload> | Promise<Partial<TokenPayload>>;
|
|
137
212
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
213
|
+
* Refresh-token store. When absent, refresh tokens are **not** issued:
|
|
214
|
+
* `POST /auth/login` omits `refreshToken` from its response, and
|
|
215
|
+
* `POST /auth/refresh` responds with `501 Not Implemented`.
|
|
216
|
+
*
|
|
217
|
+
* Use {@link createMapTokenStore} for a simple in-process store, or supply
|
|
218
|
+
* a custom adapter that implements {@link TokenStore}.
|
|
141
219
|
*/
|
|
142
|
-
refreshTokenStore
|
|
220
|
+
refreshTokenStore?: TokenStore;
|
|
143
221
|
}
|
|
144
222
|
/** Result returned by {@link verifyToken}. */
|
|
145
223
|
type VerifyResult = {
|
|
@@ -167,38 +245,48 @@ export declare function hashPassword(password: string): string;
|
|
|
167
245
|
* Replace or ignore this map entirely when you supply your own `fetchUser`.
|
|
168
246
|
*/
|
|
169
247
|
export declare const userDatabase: Map<string, UserRecord>;
|
|
248
|
+
/**
|
|
249
|
+
* Create an in-process {@link TokenStore} backed by a `Map`.
|
|
250
|
+
*
|
|
251
|
+
* Expired records are cleaned up lazily on `get()` — no background timer is
|
|
252
|
+
* needed. This store is **not** suitable for multi-instance deployments
|
|
253
|
+
* because it is local to the current Node.js process. Replace with a Redis
|
|
254
|
+
* or database adapter for production use.
|
|
255
|
+
*
|
|
256
|
+
* @returns A new {@link TokenStore} instance.
|
|
257
|
+
*/
|
|
258
|
+
export declare function createMapTokenStore(): TokenStore;
|
|
170
259
|
/**
|
|
171
260
|
* Sign a payload object and return a compact JWT string.
|
|
172
261
|
*
|
|
173
|
-
* Automatically adds the `iat` (issued-at) and `exp` (expiration) claims
|
|
174
|
-
*
|
|
175
|
-
* over `iat`/`exp` (use this to override expiry if needed).
|
|
262
|
+
* Automatically adds the `iat` (issued-at) and `exp` (expiration) claims,
|
|
263
|
+
* overriding any values already present in `payload`.
|
|
176
264
|
*
|
|
177
265
|
* @param payload - JWT payload claims (must be JSON-serialisable).
|
|
178
|
-
* @param
|
|
266
|
+
* @param key - HMAC secret (HS*) or PEM private key (RS* / ES*).
|
|
179
267
|
* @param expiresIn - Validity window in **seconds** from the current time.
|
|
180
268
|
* @param alg - Signing algorithm. Defaults to `'HS256'`.
|
|
181
269
|
* @returns A compact JWT string in the form `header.payload.signature`.
|
|
182
270
|
*/
|
|
183
|
-
declare function signToken(payload: Partial<TokenPayload>,
|
|
271
|
+
declare function signToken(payload: Partial<TokenPayload>, key: string, expiresIn: number, alg?: JwtAlgorithm): string;
|
|
184
272
|
/**
|
|
185
273
|
* Verify a compact JWT string and return its decoded payload on success.
|
|
186
274
|
*
|
|
187
275
|
* Performs the following checks in order:
|
|
188
276
|
* 1. Structural validity (exactly three dot-separated segments).
|
|
189
277
|
* 2. Algorithm consistency (header `alg` matches the expected `alg`).
|
|
190
|
-
* 3. Signature integrity
|
|
278
|
+
* 3. Signature integrity.
|
|
191
279
|
* 4. Expiration (`exp` claim is in the future).
|
|
192
280
|
*
|
|
193
281
|
* All errors are returned as `{ valid: false, error }` — no exception is
|
|
194
282
|
* thrown to the caller.
|
|
195
283
|
*
|
|
196
|
-
* @param token
|
|
197
|
-
* @param
|
|
198
|
-
* @param alg
|
|
284
|
+
* @param token - The compact JWT string to verify.
|
|
285
|
+
* @param key - HMAC secret (HS*) or PEM public key (RS* / ES*).
|
|
286
|
+
* @param alg - Expected signing algorithm.
|
|
199
287
|
* @returns A {@link VerifyResult} discriminated union.
|
|
200
288
|
*/
|
|
201
|
-
declare function verifyToken(token: string,
|
|
289
|
+
declare function verifyToken(token: string, key: string, alg: JwtAlgorithm): VerifyResult;
|
|
202
290
|
/**
|
|
203
291
|
* The object returned by {@link createJwtPlugin}.
|
|
204
292
|
*
|
|
@@ -206,7 +294,13 @@ declare function verifyToken(token: string, secret: string, alg: JwtAlgorithm):
|
|
|
206
294
|
* routes:
|
|
207
295
|
*
|
|
208
296
|
* ```ts
|
|
209
|
-
*
|
|
297
|
+
* import { createRouter, json, createJwtPlugin, createMapTokenStore } from 'expediate';
|
|
298
|
+
*
|
|
299
|
+
* const auth = createJwtPlugin({
|
|
300
|
+
* accessTokenSecret: process.env.JWT_ACCESS_SECRET!,
|
|
301
|
+
* refreshTokenSecret: process.env.JWT_REFRESH_SECRET!,
|
|
302
|
+
* refreshTokenStore: createMapTokenStore(),
|
|
303
|
+
* });
|
|
210
304
|
*
|
|
211
305
|
* app.post('/auth/login', json(), auth.login);
|
|
212
306
|
* app.post('/auth/refresh', json(), auth.refresh);
|
|
@@ -221,19 +315,21 @@ export interface JwtPlugin {
|
|
|
221
315
|
/**
|
|
222
316
|
* Route handler for `POST /auth/login`.
|
|
223
317
|
* Expects a JSON body with `{ username, password }`.
|
|
224
|
-
* On success: responds with `{ accessToken,
|
|
318
|
+
* On success: responds with `{ accessToken, expiresIn, tokenType }`.
|
|
319
|
+
* When a `refreshTokenStore` is configured, also includes `{ refreshToken }`.
|
|
225
320
|
*/
|
|
226
321
|
login: Middleware;
|
|
227
322
|
/**
|
|
228
323
|
* Route handler for `POST /auth/refresh`.
|
|
229
|
-
* Expects a JSON body with `{
|
|
324
|
+
* Expects a JSON body with `{ refreshToken }`.
|
|
325
|
+
* Returns `501` when no `refreshTokenStore` is configured.
|
|
230
326
|
* On success: responds with a new `{ accessToken, refreshToken, ... }` pair.
|
|
231
327
|
*/
|
|
232
328
|
refresh: Middleware;
|
|
233
329
|
/**
|
|
234
330
|
* Route handler for `POST /auth/logout`.
|
|
235
331
|
* Expects a JSON body with `{ refreshToken }` (optional).
|
|
236
|
-
* Always responds with 200; revokes the refresh token if provided.
|
|
332
|
+
* Always responds with 200; revokes the refresh token if provided and valid.
|
|
237
333
|
*/
|
|
238
334
|
logout: Middleware;
|
|
239
335
|
/**
|
|
@@ -267,12 +363,17 @@ export interface JwtPlugin {
|
|
|
267
363
|
/**
|
|
268
364
|
* Create a JWT authentication plugin pre-configured with the given options.
|
|
269
365
|
*
|
|
270
|
-
*
|
|
271
|
-
* `
|
|
272
|
-
*
|
|
366
|
+
* For **asymmetric algorithms** (RS* / ES*) you must provide at minimum
|
|
367
|
+
* `accessTokenPrivateKey` and `accessTokenPublicKey` in addition to setting
|
|
368
|
+
* `alg`. The refresh-token keys fall back to the access-token keys when
|
|
369
|
+
* `refreshTokenPrivateKey` / `refreshTokenPublicKey` are not set.
|
|
370
|
+
*
|
|
371
|
+
* Refresh tokens are only issued when `refreshTokenStore` is provided.
|
|
372
|
+
* Use {@link createMapTokenStore} for a simple in-process store.
|
|
273
373
|
*
|
|
274
374
|
* @param userConfig - Partial {@link JwtConfig} overrides.
|
|
275
375
|
* @returns A {@link JwtPlugin} object exposing handlers and middleware.
|
|
376
|
+
* @throws {Error} When `alg` is RS* or ES* but the required PEM keys are absent.
|
|
276
377
|
*/
|
|
277
378
|
export declare function createJwtPlugin(userConfig?: Partial<JwtConfig>): JwtPlugin;
|
|
278
379
|
export default createJwtPlugin;
|
package/dist/jwt-auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-auth.d.ts","sourceRoot":"","sources":["../src/jwt-auth.ts"],"names":[],"mappings":"AAoBA
|
|
1
|
+
{"version":3,"file":"jwt-auth.d.ts","sourceRoot":"","sources":["../src/jwt-auth.ts"],"names":[],"mappings":"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"}
|