expediate 0.0.3 → 1.0.0

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/dist/git.js ADDED
@@ -0,0 +1,244 @@
1
+ /* Copyright 2021 Fabien Bavent
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a
4
+ * copy of this software and associated documentation files (the "Software"),
5
+ * to deal in the Software without restriction, including without limitation
6
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ * and/or sell copies of the Software, and to permit persons to whom the
8
+ * Software is furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included
11
+ * in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ * DEALINGS IN THE SOFTWARE.
20
+ */
21
+ 'use strict';
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.gitHandler = gitHandler;
24
+ const child_process_1 = require("child_process");
25
+ const zlib_1 = require("zlib");
26
+ // ---------------------------------------------------------------------------
27
+ // PKT-LINE helpers
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Encode a string as a Git PKT-LINE frame.
31
+ *
32
+ * The Git Smart HTTP protocol wraps each line in a 4-hex-digit length prefix
33
+ * that counts the total frame length (4 bytes for the prefix itself plus the
34
+ * payload bytes, **not** characters).
35
+ *
36
+ * @param str - The plain-text payload to wrap (must be ASCII or UTF-8).
37
+ * @returns The framed string, e.g. `"001e# service=git-upload-pack\n"`.
38
+ *
39
+ * @see https://git-scm.com/docs/pack-protocol#_pkt_line_format
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * pktLine('# service=git-upload-pack\n')
44
+ * // → '001e# service=git-upload-pack\n'
45
+ * // ^^^^ 4 + 26 = 30 = 0x1e
46
+ * ```
47
+ */
48
+ function pktLine(str) {
49
+ // BUG FIX: the original used `str.length` (character count / UTF-16 code
50
+ // units) instead of the actual byte length. The Git PKT-LINE spec requires
51
+ // the 4-hex-digit prefix to represent the *byte* count of the whole frame
52
+ // (prefix + payload). For ASCII-only service names this difference is zero,
53
+ // but using Buffer.byteLength is correct and future-proof.
54
+ const byteLen = Buffer.byteLength(str, 'utf8') + 4; // +4 for the 4-char hex prefix itself
55
+ return byteLen.toString(16).padStart(4, '0') + str;
56
+ }
57
+ /**
58
+ * The Git PKT-LINE flush packet.
59
+ * Signals the end of a list of PKT-LINE records.
60
+ */
61
+ const PKT_FLUSH = '0000';
62
+ // ---------------------------------------------------------------------------
63
+ // Middleware factory
64
+ // ---------------------------------------------------------------------------
65
+ /**
66
+ * Middleware factory that exposes a Git repository over the **Git Smart HTTP
67
+ * protocol** (read-only fetch / clone, via `git-upload-pack`).
68
+ *
69
+ * Mount this handler at the root of a repository-scoped path so that the two
70
+ * sub-paths it handles (`/info/refs` and `/git-upload-pack`) are reachable:
71
+ *
72
+ * ```ts
73
+ * app.use('/repos/:repo', gitHandler({
74
+ * repository: (req) => path.join('/srv/git', req.params.repo + '.git'),
75
+ * }));
76
+ * ```
77
+ *
78
+ * **Supported endpoints:**
79
+ *
80
+ * | Method | Path | Purpose |
81
+ * |--------|-------------------|----------------------------------------------|
82
+ * | GET | `/info/refs` | Smart HTTP capability advertisement |
83
+ * | POST | `/git-upload-pack`| Pack-file negotiation and transfer |
84
+ *
85
+ * Only `git-upload-pack` (fetch / clone) is implemented.
86
+ * `git-receive-pack` (push) is intentionally excluded.
87
+ *
88
+ * **Compression:** gzip-compressed POST bodies are transparently decompressed
89
+ * before being piped to `git-upload-pack`.
90
+ *
91
+ * @param opt - Handler configuration (see {@link GitHandlerOptions}).
92
+ * @returns An Express-compatible middleware function `(req, res) => void`.
93
+ * @throws {TypeError} When `opt.repository` is not a function.
94
+ */
95
+ function gitHandler(opt) {
96
+ if (typeof opt.repository !== 'function')
97
+ throw new TypeError('gitHandler: opt.repository must be a function');
98
+ const gitBin = (opt.gitPath ?? '') + 'git-upload-pack';
99
+ return (req, res) => {
100
+ // Resolve the repository path for this request.
101
+ const gitDirectory = opt.repository(req);
102
+ if (!gitDirectory)
103
+ return void res.status(404).send('Repository not found');
104
+ const urlPath = req.path; // sub-path after the mount prefix
105
+ // ── GET /info/refs?service=git-upload-pack ──────────────────────────
106
+ if (req.method === 'GET' && urlPath === '/info/refs') {
107
+ // BUG FIX: `req.queries.url` can be undefined when no query parameters
108
+ // are present, causing `req.queries.url.service` to throw a TypeError.
109
+ const service = req.queries?.url?.service;
110
+ if (service !== 'git-upload-pack')
111
+ return void res.status(403).send('Only git-upload-pack is supported');
112
+ res.setHeader('Content-Type', `application/x-${service}-advertisement`);
113
+ res.setHeader('Cache-Control', 'no-cache');
114
+ // The Smart HTTP advertisement starts with a PKT-LINE service banner
115
+ // followed by a flush packet (0000), then the git-upload-pack output.
116
+ res.write(pktLine(`# service=${service}\n`));
117
+ res.write(PKT_FLUSH);
118
+ const args = buildArgs(opt, ['--stateless-rpc', '--advertise-refs', gitDirectory]);
119
+ const proc = (0, child_process_1.spawn)(gitBin, args, {
120
+ env: { ...process.env, GIT_PROTOCOL: req.headers['git-protocol'] || '' },
121
+ });
122
+ // BUG FIX: the original did not listen for spawn errors (e.g. ENOENT
123
+ // when git is not installed). Without this handler, a missing binary
124
+ // causes an uncaught exception that crashes the server process.
125
+ proc.on('error', (err) => {
126
+ console.error('[git-upload-pack refs] spawn error:', err.message);
127
+ if (!res.writableEnded)
128
+ res.status(500).send(`git-upload-pack unavailable: ${err.message}`);
129
+ });
130
+ proc.stdout.pipe(res);
131
+ proc.stdout.on('error', (err) => {
132
+ console.warn('[git-upload-pack refs] stdout error:', err.message);
133
+ });
134
+ proc.stderr.on('data', (d) => console.error('[git-upload-pack refs]', d.toString()));
135
+ proc.on('close', (code) => {
136
+ if (code !== 0) {
137
+ console.error(`[git-upload-pack refs] exited with code ${code}`);
138
+ if (!res.writableEnded)
139
+ res.status(500).send('git-upload-pack failed');
140
+ }
141
+ // When code === 0, proc.stdout has already piped all data and called
142
+ // res.end() automatically (default pipe behaviour).
143
+ });
144
+ return;
145
+ }
146
+ // ── POST /git-upload-pack ───────────────────────────────────────────
147
+ if (req.method === 'POST' && urlPath === '/git-upload-pack') {
148
+ const contentType = req.headers['content-type'] || '';
149
+ // BUG FIX: the original called `res.send(415).send(...)`. Our router's
150
+ // `res.send()` signature is `send(body?)` — passing 415 as the body
151
+ // writes the number as a string, then the chained `.send()` fails
152
+ // because `res.send()` already ended the response. Corrected to
153
+ // `res.status(415).send(...)`.
154
+ if (contentType !== 'application/x-git-upload-pack-request')
155
+ return void res.status(415).send('Unsupported Media Type');
156
+ res.setHeader('Content-Type', 'application/x-git-upload-pack-result');
157
+ res.setHeader('Cache-Control', 'no-cache');
158
+ const args = buildArgs(opt, ['--stateless-rpc', gitDirectory]);
159
+ const proc = (0, child_process_1.spawn)(gitBin, args, {
160
+ env: { ...process.env, GIT_PROTOCOL: req.headers['git-protocol'] || '' },
161
+ });
162
+ // BUG FIX: same missing spawn-error handler as the GET branch.
163
+ proc.on('error', (err) => {
164
+ console.error('[git-upload-pack pack] spawn error:', err.message);
165
+ if (!res.writableEnded)
166
+ res.status(500).send(`git-upload-pack unavailable: ${err.message}`);
167
+ });
168
+ // Transparently decompress gzip-encoded request bodies.
169
+ const encoding = req.headers['content-encoding'];
170
+ if (encoding === 'gzip') {
171
+ const gunzip = (0, zlib_1.createGunzip)();
172
+ gunzip.on('error', (err) => {
173
+ console.warn('[git-upload-pack pack] gunzip error:', err.message);
174
+ if (!res.writableEnded)
175
+ res.status(400).send('Failed to decompress request body');
176
+ });
177
+ req.pipe(gunzip).pipe(proc.stdin);
178
+ }
179
+ else {
180
+ req.pipe(proc.stdin);
181
+ }
182
+ proc.stdout.pipe(res);
183
+ proc.stdout.on('error', (err) => {
184
+ console.warn('[git-upload-pack pack] stdout error:', err.message);
185
+ });
186
+ // BUG FIX: the original tagged the POST stderr with the same label as
187
+ // the GET branch ('[git-upload-pack refs]'), making log messages from
188
+ // the two branches indistinguishable. Corrected to '[git-upload-pack pack]'.
189
+ proc.stderr.on('data', (d) => console.error('[git-upload-pack pack]', d.toString()));
190
+ proc.on('close', (code) => {
191
+ if (code !== 0) {
192
+ console.error(`[git-upload-pack pack] exited with code ${code}`);
193
+ if (!res.writableEnded)
194
+ res.status(500).send('git-upload-pack failed');
195
+ }
196
+ });
197
+ proc.stdin.on('error', (err) => {
198
+ // EPIPE is expected when the client disconnects mid-stream; it is not
199
+ // a server-side fault and does not require an error response.
200
+ if (err.code !== 'EPIPE')
201
+ console.warn('[git-upload-pack pack] stdin error:', err.message);
202
+ });
203
+ return;
204
+ }
205
+ // Unrecognised path inside the repository mount.
206
+ res.status(404).send('Not found');
207
+ };
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // Internal helpers
211
+ // ---------------------------------------------------------------------------
212
+ /**
213
+ * Build the argument list for `git-upload-pack`.
214
+ *
215
+ * Prepends an optional `--timeout=<n>` argument when configured, then
216
+ * inserts the `--strict` / `--no-strict` flag, followed by any additional
217
+ * arguments the caller provides.
218
+ *
219
+ * @param opt - Handler options.
220
+ * @param trailing - Arguments appended after the strict flag
221
+ * (e.g. `['--stateless-rpc', '--advertise-refs', dir]`).
222
+ * @returns The complete argument array ready for `spawn`.
223
+ */
224
+ function buildArgs(opt, trailing) {
225
+ const args = [];
226
+ if (opt.timeout) {
227
+ // BUG FIX: `parseInt` without an explicit radix may misinterpret strings
228
+ // that start with '0' as octal in some environments. Always pass base 10.
229
+ const seconds = parseInt(String(opt.timeout), 10);
230
+ if (!isNaN(seconds) && seconds > 0)
231
+ args.push(`--timeout=${seconds}`);
232
+ }
233
+ // BUG FIX: the original used `opt.bareOnly ? '--strict' : '--no-strict'`.
234
+ // `git-upload-pack` does not accept `--strict` — that flag belongs to
235
+ // `git-receive-pack`. The correct option for upload-pack is `--no-strict`
236
+ // (which relaxes the requirement that the path must be a bare repository).
237
+ // When the caller sets `strict: true` we simply omit `--no-strict`; when
238
+ // `strict` is false (default) we pass `--no-strict` to allow non-bare repos.
239
+ if (!opt.strict)
240
+ args.push('--no-strict');
241
+ return [...args, ...trailing];
242
+ }
243
+ exports.default = gitHandler;
244
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @module expediate
3
+ * TypeScript package for web server routing.
4
+ */
5
+ import createRouter from './router';
6
+ export { createRouter };
7
+ export type { Router, RouterRequest, RouterResponse, Middleware, MiddlewareArg, NextFunction, Layer, CookieOptions, TlsOptions, StringMap, } from './router';
8
+ export { serveStatic, serveFile, sendFile, mime } from './static';
9
+ export type { StaticOptions, Mime } from './static';
10
+ export { json, formData, parseBody, logger } from './misc';
11
+ export type { BodyOptions, LoggerOptions, FormPart, } from './misc';
12
+ import createJwtPlugin from './jwt-auth';
13
+ export { createJwtPlugin };
14
+ export type { JwtPlugin, JwtConfig } from './jwt-auth';
15
+ import gitHandler from './git';
16
+ export { gitHandler };
17
+ export type { GitHandlerOptions, } from './git';
18
+ import apiBuilder from './apis';
19
+ export { apiBuilder };
20
+ export type { ApiError, ServiceMethod, ServiceInstance, ServiceMethods, RouteMap, ServiceDefinition } from './apis';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AAGH,OAAO,YAAY,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,YAAY,EACV,MAAM,EACN,aAAa,EACb,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,KAAK,EACL,aAAa,EACb,UAAU,EACV,SAAS,GACV,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAClE,YAAY,EACV,aAAa,EACb,IAAI,EACL,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3D,YAAY,EACV,WAAW,EACX,aAAa,EACb,QAAQ,GACT,MAAM,QAAQ,CAAC;AAGhB,OAAO,eAAe,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,CAAA;AAC1B,YAAY,EACV,SAAS,EACT,SAAS,EACV,MAAM,YAAY,CAAC;AAGpB,OAAQ,UAAU,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,YAAY,EACR,iBAAiB,GACpB,MAAM,OAAO,CAAA;AAGd,OAAO,UAAU,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,YAAY,EACR,QAAQ,EACR,aAAa,EACb,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACpB,MAAM,QAAQ,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /* Copyright 2021 Fabien Bavent
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a
5
+ * copy of this software and associated documentation files (the "Software"),
6
+ * to deal in the Software without restriction, including without limitation
7
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ * and/or sell copies of the Software, and to permit persons to whom the
9
+ * Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included
12
+ * in all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ * DEALINGS IN THE SOFTWARE.
21
+ */
22
+ /**
23
+ * @module expediate
24
+ * TypeScript package for web server routing.
25
+ */
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
+ // ── Router ────────────────────────────────────────────────────────────────────
32
+ const router_1 = __importDefault(require("./router"));
33
+ exports.createRouter = router_1.default;
34
+ // ── Static ────────────────────────────────────────────────────────────────────
35
+ var static_1 = require("./static");
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; } });
40
+ // ── Miscallenous ──────────────────────────────────────────────────────────────
41
+ var misc_1 = require("./misc");
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; } });
46
+ // ── JWT Authentication ────────────────────────────────────────────────────────
47
+ const jwt_auth_1 = __importDefault(require("./jwt-auth"));
48
+ exports.createJwtPlugin = jwt_auth_1.default;
49
+ // ── Git repository ────────────────────────────────────────────────────────────
50
+ const git_1 = __importDefault(require("./git"));
51
+ exports.gitHandler = git_1.default;
52
+ // ── API Service ───────────────────────────────────────────────────────────────
53
+ const apis_1 = __importDefault(require("./apis"));
54
+ exports.apiBuilder = apis_1.default;
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;GAGG;;;;;;AAEH,iFAAiF;AACjF,sDAAoC;AAC3B,uBADF,gBAAY,CACE;AAcrB,iFAAiF;AAEjF,mCAAkE;AAAzD,qGAAA,WAAW,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,kGAAA,QAAQ,OAAA;AAAE,8FAAA,IAAI,OAAA;AAM/C,iFAAiF;AAEjF,+BAA2D;AAAlD,4FAAA,IAAI,OAAA;AAAE,gGAAA,QAAQ,OAAA;AAAE,iGAAA,SAAS,OAAA;AAAE,8FAAA,MAAM,OAAA;AAO1C,iFAAiF;AACjF,0DAAwC;AAC/B,0BADF,kBAAe,CACE;AAMxB,iFAAiF;AACjF,gDAA+B;AACtB,qBADD,aAAU,CACC;AAKnB,iFAAiF;AACjF,kDAA+B;AACtB,qBADF,cAAU,CACE"}
@@ -0,0 +1,280 @@
1
+ /**
2
+ * jwt-auth.ts
3
+ *
4
+ * JWT authentication plugin for the Expediate router.
5
+ *
6
+ * Provides:
7
+ * - Stateless access tokens (HS256 / HS384 / HS512 HMAC-signed JWTs).
8
+ * - Opaque refresh tokens with server-side storage and automatic rotation.
9
+ * - Route handlers for login, token refresh, and logout.
10
+ * - Middleware for token validation, authorisation, role checks, and
11
+ * permission checks.
12
+ *
13
+ * Security notes:
14
+ * - Passwords are hashed with SHA-256 for demonstration purposes only.
15
+ * Replace with bcrypt / argon2 in production.
16
+ * - The default secrets are placeholders — always override them in production.
17
+ * - Refresh token storage defaults to an in-process Map; replace with a
18
+ * persistent store (Redis, database) for multi-instance deployments.
19
+ */
20
+ import type { Middleware } from './router.js';
21
+ /** A user record as stored in (or returned by) the user database. */
22
+ export interface UserRecord {
23
+ /** Stable unique identifier (used as JWT `sub` claim when present). */
24
+ id?: string;
25
+ /** Login username. */
26
+ username: string;
27
+ /**
28
+ * SHA-256 hex digest of the user's password.
29
+ * Replace with a bcrypt/argon2 hash in production.
30
+ */
31
+ passwordHash: string;
32
+ /** Role labels assigned to this user (e.g. `'admin'`, `'editor'`). */
33
+ roles?: string[];
34
+ /**
35
+ * Fine-grained permission strings assigned to this user
36
+ * (e.g. `'read'`, `'write'`, `'delete'`).
37
+ */
38
+ permissions?: string[];
39
+ /** Any additional fields the application wants to carry. */
40
+ [key: string]: unknown;
41
+ }
42
+ /**
43
+ * The decoded JWT access-token payload attached to `req.user` after
44
+ * successful authentication.
45
+ */
46
+ export interface TokenPayload {
47
+ /** JWT subject — typically the user's stable ID. */
48
+ sub: string;
49
+ /** Username extracted from the user record. */
50
+ username: string;
51
+ /** Issuer claim, set to `config.issuer`. */
52
+ iss: string;
53
+ /** Issued-at timestamp (Unix seconds). */
54
+ iat: number;
55
+ /** Expiration timestamp (Unix seconds). */
56
+ exp: number;
57
+ /** Roles copied from the user record. */
58
+ roles?: string[];
59
+ /** Permissions copied from the user record. */
60
+ permissions?: string[];
61
+ /** Any additional claims produced by `config.payload`. */
62
+ [key: string]: unknown;
63
+ }
64
+ /** Internal metadata stored alongside each active refresh token. */
65
+ interface RefreshTokenData {
66
+ /** Username the refresh token was issued to. */
67
+ username: string;
68
+ /** Unix ms timestamp of issuance. */
69
+ issuedAt: number;
70
+ /** Unix ms timestamp after which the token must be rejected. */
71
+ expiresAt: number;
72
+ }
73
+ /**
74
+ * Minimal interface for the refresh-token store.
75
+ * Any object implementing these four methods is accepted (Map, Redis client
76
+ * adapter, database wrapper, etc.).
77
+ */
78
+ export interface TokenStore {
79
+ set(key: string, value: RefreshTokenData): void;
80
+ get(key: string): RefreshTokenData | undefined;
81
+ delete(key: string): void;
82
+ has(key: string): boolean;
83
+ }
84
+ /**
85
+ * Supported HMAC-SHA signing algorithms for JWT.
86
+ * RS*, ES*, and PS* families are not yet implemented.
87
+ */
88
+ export type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
89
+ /**
90
+ * Full configuration object for {@link createJwtPlugin}.
91
+ * All fields have defaults; override only what you need.
92
+ */
93
+ export interface JwtConfig {
94
+ /** HMAC secret used to sign access tokens. **Change in production.** */
95
+ accessTokenSecret: string;
96
+ /** HMAC secret used to sign refresh tokens (currently unused — refresh *
97
+ * tokens are opaque random strings, not JWTs). Reserved for future use. */
98
+ refreshTokenSecret: string;
99
+ /** Access token lifetime in **seconds**. Defaults to 15 minutes. */
100
+ accessTokenExpiry: number;
101
+ /** Refresh token lifetime in **seconds**. Defaults to 7 days. */
102
+ refreshTokenExpiry: number;
103
+ /** Value placed in the JWT `iss` claim. */
104
+ issuer: string;
105
+ /**
106
+ * When `true`, the `authenticate` middleware rejects tokens whose `iss`
107
+ * claim does not match `config.issuer`.
108
+ * Defaults to `false` (issuer not checked).
109
+ */
110
+ checkIssuer: boolean;
111
+ /** JWT signing algorithm. Defaults to `'HS256'`. */
112
+ alg: JwtAlgorithm;
113
+ /**
114
+ * Extract the login username from a user record.
115
+ * Defaults to `(user) => user.username`.
116
+ */
117
+ username: (user: UserRecord) => string;
118
+ /**
119
+ * Fetch a user record by username.
120
+ * Return `undefined` (or any falsy value) when the user does not exist.
121
+ */
122
+ fetchUser: (username: string) => UserRecord | undefined;
123
+ /**
124
+ * Return `true` when the supplied plain-text `password` is valid for
125
+ * `user`, `false` otherwise.
126
+ *
127
+ * The default implementation compares SHA-256 hashes; replace with a
128
+ * timing-safe bcrypt/argon2 check in production.
129
+ */
130
+ isPasswordValid: (user: UserRecord, password: string) => boolean;
131
+ /**
132
+ * Build the JWT payload for a user.
133
+ * The `iss`, `iat`, `exp`, and `sub` claims are added automatically.
134
+ * Returning a partial object is fine — the plugin merges the rest.
135
+ */
136
+ payload: (user: UserRecord) => Partial<TokenPayload>;
137
+ /**
138
+ * Active refresh-token store.
139
+ * Defaults to an in-process `Map` (lost on restart; not suitable for
140
+ * multi-instance deployments).
141
+ */
142
+ refreshTokenStore: TokenStore;
143
+ }
144
+ /** Result returned by {@link verifyToken}. */
145
+ type VerifyResult = {
146
+ valid: true;
147
+ payload: TokenPayload;
148
+ } | {
149
+ valid: false;
150
+ error: string;
151
+ };
152
+ /**
153
+ * Hash a plain-text password with SHA-256 and return the hex digest.
154
+ *
155
+ * > **⚠ Warning:** SHA-256 is fast and therefore unsuitable for password
156
+ * > hashing in production. Use bcrypt or argon2 instead.
157
+ *
158
+ * @param password - The plain-text password to hash.
159
+ * @returns A 64-character lowercase hex string.
160
+ */
161
+ export declare function hashPassword(password: string): string;
162
+ /**
163
+ * Default in-memory user database used when no custom `fetchUser` is
164
+ * provided. Contains three demo accounts: alice (admin), bob (editor),
165
+ * charlie (viewer).
166
+ *
167
+ * Replace or ignore this map entirely when you supply your own `fetchUser`.
168
+ */
169
+ export declare const userDatabase: Map<string, UserRecord>;
170
+ /**
171
+ * Sign a payload object and return a compact JWT string.
172
+ *
173
+ * Automatically adds the `iat` (issued-at) and `exp` (expiration) claims.
174
+ * Any claims already present in `payload` are preserved and take precedence
175
+ * over `iat`/`exp` (use this to override expiry if needed).
176
+ *
177
+ * @param payload - JWT payload claims (must be JSON-serialisable).
178
+ * @param secret - HMAC secret used to sign the token.
179
+ * @param expiresIn - Validity window in **seconds** from the current time.
180
+ * @param alg - Signing algorithm. Defaults to `'HS256'`.
181
+ * @returns A compact JWT string in the form `header.payload.signature`.
182
+ */
183
+ declare function signToken(payload: Partial<TokenPayload>, secret: string, expiresIn: number, alg?: JwtAlgorithm): string;
184
+ /**
185
+ * Verify a compact JWT string and return its decoded payload on success.
186
+ *
187
+ * Performs the following checks in order:
188
+ * 1. Structural validity (exactly three dot-separated segments).
189
+ * 2. Algorithm consistency (header `alg` matches the expected `alg`).
190
+ * 3. Signature integrity (timing-safe HMAC comparison).
191
+ * 4. Expiration (`exp` claim is in the future).
192
+ *
193
+ * All errors are returned as `{ valid: false, error }` — no exception is
194
+ * thrown to the caller.
195
+ *
196
+ * @param token - The compact JWT string to verify.
197
+ * @param secret - HMAC secret that was used to sign the token.
198
+ * @param alg - Expected signing algorithm.
199
+ * @returns A {@link VerifyResult} discriminated union.
200
+ */
201
+ declare function verifyToken(token: string, secret: string, alg: JwtAlgorithm): VerifyResult;
202
+ /**
203
+ * The object returned by {@link createJwtPlugin}.
204
+ *
205
+ * Mount the handlers on your router and apply the middleware to protected
206
+ * routes:
207
+ *
208
+ * ```ts
209
+ * const auth = createJwtPlugin({ accessTokenSecret: process.env.JWT_SECRET! });
210
+ *
211
+ * app.post('/auth/login', json(), auth.login);
212
+ * app.post('/auth/refresh', json(), auth.refresh);
213
+ * app.post('/auth/logout', json(), auth.logout);
214
+ *
215
+ * app.get('/me', auth.authenticate, auth.authorize, getProfile);
216
+ * app.delete('/admin', ...auth.requireRole('admin'), deleteHandler);
217
+ * app.put('/posts', ...auth.requirePermission('write'), updateHandler);
218
+ * ```
219
+ */
220
+ export interface JwtPlugin {
221
+ /**
222
+ * Route handler for `POST /auth/login`.
223
+ * Expects a JSON body with `{ username, password }`.
224
+ * On success: responds with `{ accessToken, refreshToken, expiresIn, tokenType }`.
225
+ */
226
+ login: Middleware;
227
+ /**
228
+ * Route handler for `POST /auth/refresh`.
229
+ * Expects a JSON body with `{ username, refreshToken }`.
230
+ * On success: responds with a new `{ accessToken, refreshToken, ... }` pair.
231
+ */
232
+ refresh: Middleware;
233
+ /**
234
+ * Route handler for `POST /auth/logout`.
235
+ * Expects a JSON body with `{ refreshToken }` (optional).
236
+ * Always responds with 200; revokes the refresh token if provided.
237
+ */
238
+ logout: Middleware;
239
+ /**
240
+ * Middleware that validates the `Authorization: Bearer <token>` header.
241
+ * Sets `req.user` to the decoded {@link TokenPayload} on success.
242
+ * Calls `next()` silently (without error) when the token is absent or invalid,
243
+ * allowing the route to decide how to handle unauthenticated requests.
244
+ */
245
+ authenticate: Middleware;
246
+ /**
247
+ * Middleware that rejects unauthenticated requests with 401.
248
+ * Should be placed **after** {@link authenticate}:
249
+ * `router.get('/me', auth.authenticate, auth.authorize, handler)`.
250
+ */
251
+ authorize: Middleware;
252
+ /**
253
+ * Middleware factory that requires the authenticated user to have at least
254
+ * one of the specified roles. Returns `[authenticate, roleCheck]` so it
255
+ * can be spread directly into a route registration:
256
+ * `router.get('/admin', ...auth.requireRole('admin'), handler)`.
257
+ * Responds with 401 when unauthenticated, 403 when the role is missing.
258
+ */
259
+ requireRole: (...roles: string[]) => Middleware[];
260
+ /**
261
+ * Middleware factory that requires the authenticated user to have **all**
262
+ * of the specified permissions. Returns `[authenticate, permCheck]`.
263
+ * Responds with 401 when unauthenticated, 403 when any permission is missing.
264
+ */
265
+ requirePermission: (...permissions: string[]) => Middleware[];
266
+ }
267
+ /**
268
+ * Create a JWT authentication plugin pre-configured with the given options.
269
+ *
270
+ * All config fields have safe defaults for development. At minimum, set
271
+ * `accessTokenSecret` (and `refreshTokenSecret` if you plan to use it) to
272
+ * random values in production.
273
+ *
274
+ * @param userConfig - Partial {@link JwtConfig} overrides.
275
+ * @returns A {@link JwtPlugin} object exposing handlers and middleware.
276
+ */
277
+ export declare function createJwtPlugin(userConfig?: Partial<JwtConfig>): JwtPlugin;
278
+ export default createJwtPlugin;
279
+ export { signToken, verifyToken, hashPassword as _hashPassword };
280
+ //# sourceMappingURL=jwt-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-auth.d.ts","sourceRoot":"","sources":["../src/jwt-auth.ts"],"names":[],"mappings":"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"}