@mi9-identity/auth-middleware-express 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.
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Narrowing type guards, duplicated per package by design (see CLAUDE.md) —
3
+ * mirrored from the identity service's canonical `utils/type-guards.ts`, plus
4
+ * a package-local `isFunction` (the canonical set has no function guard).
5
+ */
6
+ /**
7
+ * Strict null check.
8
+ *
9
+ * @param { unknown } value Value to test.
10
+ * @returns { value is null } True iff `value === null`.
11
+ * @example
12
+ * isNull(null); // true
13
+ * isNull(undefined); // false
14
+ * isNull(0); // false
15
+ * isNull(''); // false
16
+ */
17
+ export declare const isNull: (value: unknown) => value is null;
18
+ /**
19
+ * Strict undefined check.
20
+ *
21
+ * @param { unknown } value Value to test.
22
+ * @returns { value is undefined } True iff `value === undefined`.
23
+ * @example
24
+ * isUndefined(undefined); // true
25
+ * isUndefined(null); // false
26
+ * isUndefined(0); // false
27
+ */
28
+ export declare const isUndefined: (value: unknown) => value is undefined;
29
+ /**
30
+ * Nullish check (null or undefined).
31
+ *
32
+ * @param { unknown } value Value to test.
33
+ * @returns { value is null | undefined } True iff `value` is null or undefined.
34
+ * @example
35
+ * isNil(null); // true
36
+ * isNil(undefined); // true
37
+ * isNil(0); // false
38
+ * isNil(''); // false
39
+ */
40
+ export declare const isNil: (value: unknown) => value is null | undefined;
41
+ /**
42
+ * Primitive string check.
43
+ *
44
+ * @param { unknown } value Value to test.
45
+ * @returns { value is string } True iff `typeof value === 'string'`.
46
+ * @example
47
+ * isString('hello'); // true
48
+ * isString(''); // true
49
+ * isString(42); // false
50
+ * isString(new String('hello')); // false (boxed String, not primitive)
51
+ */
52
+ export declare const isString: (value: unknown) => value is string;
53
+ /**
54
+ * Narrowing return type for `isEmpty`. Listed unions are the structural
55
+ * "empty" shapes; the negation `!isEmpty(x)` strips `null | undefined`
56
+ * (and the empty literal forms where TS retains discrimination), so a
57
+ * `string | undefined` source narrows to `string` after a non-empty check.
58
+ */
59
+ type EmptyValue = null | undefined | '' | readonly never[] | Map<unknown, never> | Set<never> | Record<string, never>;
60
+ /**
61
+ * Empty-shape check (nullish, empty string, array, Map, Set, or plain object). Numbers and booleans are never empty.
62
+ *
63
+ * @param { unknown } value Value to test.
64
+ * @returns { value is EmptyValue } True iff `value` is an empty shape.
65
+ * @example
66
+ * isEmpty(null); // true
67
+ * isEmpty(undefined); // true
68
+ * isEmpty(''); // true
69
+ * isEmpty([]); // true
70
+ * isEmpty({}); // true
71
+ * isEmpty(new Map()); // true
72
+ * isEmpty(new Set()); // true
73
+ * isEmpty('hello'); // false
74
+ * isEmpty(0); // false
75
+ * isEmpty(false); // false
76
+ * isEmpty([0]); // false
77
+ * isEmpty({ a: undefined }); // false (key still counted)
78
+ */
79
+ export declare const isEmpty: (value: unknown) => value is EmptyValue;
80
+ /**
81
+ * Callable check. Narrowing preserves the callable member of a union so the
82
+ * value stays invocable with its original signature.
83
+ *
84
+ * @param { unknown } value Value to test.
85
+ * @returns { value is (...args: never[]) => unknown } True iff `typeof value === 'function'`.
86
+ * @example
87
+ * isFunction(() => 1); // true
88
+ * isFunction(class {}); // true (classes are callable)
89
+ * isFunction(42); // false
90
+ */
91
+ export declare const isFunction: (value: unknown) => value is (...args: never[]) => unknown;
92
+ export {};
93
+ //# sourceMappingURL=type-guards.d.ts.map
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Narrowing type guards, duplicated per package by design (see CLAUDE.md) —
3
+ * mirrored from the identity service's canonical `utils/type-guards.ts`, plus
4
+ * a package-local `isFunction` (the canonical set has no function guard).
5
+ */
6
+ /**
7
+ * Strict null check.
8
+ *
9
+ * @param { unknown } value Value to test.
10
+ * @returns { value is null } True iff `value === null`.
11
+ * @example
12
+ * isNull(null); // true
13
+ * isNull(undefined); // false
14
+ * isNull(0); // false
15
+ * isNull(''); // false
16
+ */
17
+ export const isNull = (value) => value === null;
18
+ /**
19
+ * Strict undefined check.
20
+ *
21
+ * @param { unknown } value Value to test.
22
+ * @returns { value is undefined } True iff `value === undefined`.
23
+ * @example
24
+ * isUndefined(undefined); // true
25
+ * isUndefined(null); // false
26
+ * isUndefined(0); // false
27
+ */
28
+ export const isUndefined = (value) => value === undefined;
29
+ /**
30
+ * Nullish check (null or undefined).
31
+ *
32
+ * @param { unknown } value Value to test.
33
+ * @returns { value is null | undefined } True iff `value` is null or undefined.
34
+ * @example
35
+ * isNil(null); // true
36
+ * isNil(undefined); // true
37
+ * isNil(0); // false
38
+ * isNil(''); // false
39
+ */
40
+ export const isNil = (value) => isNull(value) || isUndefined(value);
41
+ /**
42
+ * Primitive string check.
43
+ *
44
+ * @param { unknown } value Value to test.
45
+ * @returns { value is string } True iff `typeof value === 'string'`.
46
+ * @example
47
+ * isString('hello'); // true
48
+ * isString(''); // true
49
+ * isString(42); // false
50
+ * isString(new String('hello')); // false (boxed String, not primitive)
51
+ */
52
+ export const isString = (value) => typeof value === 'string';
53
+ /**
54
+ * Empty-shape check (nullish, empty string, array, Map, Set, or plain object). Numbers and booleans are never empty.
55
+ *
56
+ * @param { unknown } value Value to test.
57
+ * @returns { value is EmptyValue } True iff `value` is an empty shape.
58
+ * @example
59
+ * isEmpty(null); // true
60
+ * isEmpty(undefined); // true
61
+ * isEmpty(''); // true
62
+ * isEmpty([]); // true
63
+ * isEmpty({}); // true
64
+ * isEmpty(new Map()); // true
65
+ * isEmpty(new Set()); // true
66
+ * isEmpty('hello'); // false
67
+ * isEmpty(0); // false
68
+ * isEmpty(false); // false
69
+ * isEmpty([0]); // false
70
+ * isEmpty({ a: undefined }); // false (key still counted)
71
+ */
72
+ export const isEmpty = (value) => {
73
+ if (isNil(value)) {
74
+ return true;
75
+ }
76
+ if (isString(value)) {
77
+ return value.length === 0;
78
+ }
79
+ if (Array.isArray(value)) {
80
+ return value.length === 0;
81
+ }
82
+ if (value instanceof Map || value instanceof Set) {
83
+ return value.size === 0;
84
+ }
85
+ if (typeof value === 'object') {
86
+ return Object.keys(value).length === 0;
87
+ }
88
+ return false;
89
+ };
90
+ /**
91
+ * Callable check. Narrowing preserves the callable member of a union so the
92
+ * value stays invocable with its original signature.
93
+ *
94
+ * @param { unknown } value Value to test.
95
+ * @returns { value is (...args: never[]) => unknown } True iff `typeof value === 'function'`.
96
+ * @example
97
+ * isFunction(() => 1); // true
98
+ * isFunction(class {}); // true (classes are callable)
99
+ * isFunction(42); // false
100
+ */
101
+ export const isFunction = (value) => typeof value === 'function';
102
+ //# sourceMappingURL=type-guards.js.map
@@ -0,0 +1,44 @@
1
+ import type { Verifier } from '@mi9-identity/jwt-verifier';
2
+ import type { VerifierError } from './errors.js';
3
+ /**
4
+ * Structured event the middleware emits on every auth attempt. Field shape
5
+ * is a CONTRACT — BigQuery sinks consume it directly, and the issuer's audit
6
+ * pipeline mirrors these field names. Don't rename or drop.
7
+ */
8
+ export interface AuthEvent {
9
+ event: 'auth_succeeded' | 'auth_failed';
10
+ request_id: string;
11
+ client_id?: string;
12
+ retailer_code?: string;
13
+ product?: string;
14
+ success: boolean;
15
+ latency_ms: number;
16
+ jti?: string;
17
+ failure_reason?: 'missing_token' | 'invalid_token' | 'token_expired' | 'issuer_not_allowed' | 'audience_mismatch' | 'algorithm_not_allowed' | 'missing_required_claim' | 'insufficient_scope' | 'retailer_mismatch' | 'jwks_unavailable';
18
+ }
19
+ /**
20
+ * Shape returned by `onError`. The adapter writes `status`, optionally sets
21
+ * `headers`, and JSON-encodes `body`.
22
+ */
23
+ export interface ErrorResponse {
24
+ status: number;
25
+ body: unknown;
26
+ headers?: Record<string, string>;
27
+ }
28
+ /**
29
+ * Configuration for `mi9Auth`. The verifier is required; everything else is
30
+ * defaulted. Pass `onError` to override the RFC 6750 mapping with your own
31
+ * envelope (e.g. matching an existing API error contract).
32
+ */
33
+ export interface Mi9AuthOptions {
34
+ verifier: Verifier;
35
+ /** Defaults to `'authorization'`. Case-insensitive — Express normalises. */
36
+ headerName?: string;
37
+ /** Defaults to `'X-Request-ID'`. Read on inbound (lookup is lowercased per Express convention), propagated to `AuthEvent.request_id`. */
38
+ requestIdHeader?: string;
39
+ /** Defaults to no-op. Wire your structured logger here. */
40
+ onAuthEvent?: (e: AuthEvent) => void;
41
+ /** Defaults to RFC 6750 §3 mapping (see `default-error-mapping.ts`). */
42
+ onError?: (err: VerifierError) => ErrorResponse;
43
+ }
44
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@mi9-identity/auth-middleware-express",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "Express adapter for the Mi9 JWT verifier — drop-in middleware that verifies Mi9-issued JWTs and attaches AuthContext to req.auth.",
6
+ "keywords": [
7
+ "mi9",
8
+ "identity",
9
+ "oauth2",
10
+ "jwt",
11
+ "rs256",
12
+ "jwks",
13
+ "m2m",
14
+ "express"
15
+ ],
16
+ "license": "Apache-2.0",
17
+ "author": "Mi9 Retail",
18
+ "homepage": "https://mi9retail.com",
19
+ "type": "module",
20
+ "engines": {
21
+ "node": ">=24.0.0"
22
+ },
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "!dist/**/*.map",
34
+ "!dist/.tsbuildinfo",
35
+ "README.md",
36
+ "CHANGELOG.md",
37
+ "LICENSE"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "peerDependencies": {
43
+ "@types/express": "^5.0.0",
44
+ "express": ">=5.0.0",
45
+ "@mi9-identity/jwt-verifier": "^1.0.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@types/express": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "@types/express": "5.0.6",
54
+ "@types/node": "24.13.3",
55
+ "@types/supertest": "7.2.1",
56
+ "@vitest/coverage-v8": "4.1.11",
57
+ "express": "5.2.1",
58
+ "supertest": "7.2.2",
59
+ "vitest": "4.1.11",
60
+ "@mi9-identity/jwt-verifier": "^1.0.0",
61
+ "@mi9-identity/tsconfig": "^1.0.0"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc -b",
65
+ "typecheck": "tsc --noEmit",
66
+ "test": "vitest run",
67
+ "test:watch": "vitest",
68
+ "test:cov": "vitest run --coverage",
69
+ "lint": "biome lint .",
70
+ "lint:fix": "biome lint --write .",
71
+ "clean": "rimraf dist .turbo"
72
+ }
73
+ }