@exortek/apikey 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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/errors.d.ts +20 -0
- package/dist/index.cjs +1632 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.mjs +1617 -0
- package/dist/internal/guards.d.ts +4 -0
- package/dist/middleware/core.d.ts +86 -0
- package/dist/middleware/express.cjs +1363 -0
- package/dist/middleware/express.d.ts +4 -0
- package/dist/middleware/express.mjs +1361 -0
- package/dist/middleware/fastify.cjs +1374 -0
- package/dist/middleware/fastify.d.ts +1 -0
- package/dist/middleware/fastify.mjs +1372 -0
- package/dist/scopes.d.ts +21 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/memory.d.ts +6 -0
- package/dist/stores/redis.d.ts +13 -0
- package/dist/stores.cjs +880 -0
- package/dist/stores.mjs +877 -0
- package/dist/token.d.ts +88 -0
- package/package.json +98 -0
package/dist/token.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {string} prefix
|
|
3
|
+
* @param {string} name
|
|
4
|
+
* @param {(msg: string) => Error} invalidArg
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function assertPrefix(prefix: string, name: string, invalidArg: (msg: string) => Error): string;
|
|
8
|
+
/**
|
|
9
|
+
* Mint the raw pieces of a new key. Returns the wire token, the
|
|
10
|
+
* plaintext id (for DB lookup), and the storage hash of the secret.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} prefix
|
|
13
|
+
* @param {Buffer | null} pepper Newest pepper, or null when unconfigured.
|
|
14
|
+
* @returns {{ key: string, id: string, hash: string }}
|
|
15
|
+
*/
|
|
16
|
+
export function mint(prefix: string, pepper: Buffer | null): {
|
|
17
|
+
key: string;
|
|
18
|
+
id: string;
|
|
19
|
+
hash: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Compute the storage hash of a raw secret. With a pepper: HMAC-SHA256
|
|
23
|
+
* keyed by the pepper. Without: plain SHA-256. Both return the digest
|
|
24
|
+
* as a 43-char base64url string.
|
|
25
|
+
*
|
|
26
|
+
* @param {Buffer} secretBytes
|
|
27
|
+
* @param {Buffer | null} pepper
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function hashSecret(secretBytes: Buffer, pepper: Buffer | null): string;
|
|
31
|
+
/**
|
|
32
|
+
* Timing-safe compare of a candidate hash against a stored hash. Both
|
|
33
|
+
* inputs are base64url strings of equal length — a length mismatch is
|
|
34
|
+
* treated as a mismatch after a fixed decode step, so an outside
|
|
35
|
+
* observer cannot learn the stored hash's length via timing.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} candidate
|
|
38
|
+
* @param {string} stored
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
export function hashesMatch(candidate: string, stored: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Parse a raw key without verifying it. Returns the segments, or
|
|
44
|
+
* `null` when the shape is wrong. Never throws.
|
|
45
|
+
*
|
|
46
|
+
* Callers can use `parseApiKey(key).prefix` to route by key family
|
|
47
|
+
* in a middleware before hitting the store — but the parse result
|
|
48
|
+
* MUST NOT be trusted as authenticated. Only `verifyApiKey` proves
|
|
49
|
+
* the holder possesses the real secret.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} key
|
|
52
|
+
* @returns {{ prefix: string, id: string, secret: string } | null}
|
|
53
|
+
*/
|
|
54
|
+
export function parseApiKey(key: string): {
|
|
55
|
+
prefix: string;
|
|
56
|
+
id: string;
|
|
57
|
+
secret: string;
|
|
58
|
+
} | null;
|
|
59
|
+
/**
|
|
60
|
+
* Compute the storage hash for a candidate raw key's secret half,
|
|
61
|
+
* trying peppers newest-first. Returns the hash + which pepper matched
|
|
62
|
+
* (or null if no pepper array was supplied).
|
|
63
|
+
*
|
|
64
|
+
* Used by `verifyApiKey` — it hashes the incoming secret with each
|
|
65
|
+
* pepper in the rotation and lets the caller decide which one to
|
|
66
|
+
* compare against. The needsRehash signal (secret matched an older
|
|
67
|
+
* pepper) is exposed to consumers so they can silently upgrade
|
|
68
|
+
* storage on the next successful auth.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} rawSecret The base64url secret segment.
|
|
71
|
+
* @param {Buffer[] | null} peppers
|
|
72
|
+
* @returns {{ candidateHashes: string[] }}
|
|
73
|
+
*/
|
|
74
|
+
export function candidateHashesFor(rawSecret: string, peppers: Buffer[] | null): {
|
|
75
|
+
candidateHashes: string[];
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Log-safe display for a key. Returns `<prefix>_<first-N-of-id>…<last-M-of-secret>`
|
|
79
|
+
* so the value is recognisable in an audit log without disclosing the
|
|
80
|
+
* secret. The default masks all but the first 6 chars of the id and
|
|
81
|
+
* the last 4 chars of the secret.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} key
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
export function mask(key: string): string;
|
|
87
|
+
import { isBytes } from '@exortek/shared/predicates';
|
|
88
|
+
export { nodeTimingSafeEqual as _nodeTimingSafeEqual, isBytes as _isBytes };
|
package/package.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exortek/apikey",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Stripe-style prefixed API keys for Node.js — 3-segment tokens (prefix_id_secret), HMAC-SHA256 storage hash with pepper rotation, scope allowlists with wildcard match, opt-in lastUsedAt tracking, memory + Redis stores, Express + Fastify middleware. Server-only. Zero non-@exortek runtime dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./stores": {
|
|
17
|
+
"types": "./dist/stores/index.d.ts",
|
|
18
|
+
"import": "./dist/stores.mjs",
|
|
19
|
+
"require": "./dist/stores.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./middleware/express": {
|
|
22
|
+
"types": "./dist/middleware/express.d.ts",
|
|
23
|
+
"import": "./dist/middleware/express.mjs",
|
|
24
|
+
"require": "./dist/middleware/express.cjs"
|
|
25
|
+
},
|
|
26
|
+
"./middleware/fastify": {
|
|
27
|
+
"types": "./dist/middleware/fastify.d.ts",
|
|
28
|
+
"import": "./dist/middleware/fastify.mjs",
|
|
29
|
+
"require": "./dist/middleware/fastify.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "rm -rf dist tsconfig.tsbuildinfo && rollup -c rollup.config.js && tsc -p tsconfig.json",
|
|
39
|
+
"build:watch": "rollup -c rollup.config.js --watch",
|
|
40
|
+
"build:types": "tsc -p tsconfig.json",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"test": "node --test 'tests/**/*.test.js'",
|
|
43
|
+
"test:coverage": "node --test --experimental-test-coverage 'tests/**/*.test.js'",
|
|
44
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
45
|
+
"prepack": "cp ../../LICENSE ./LICENSE"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"backend",
|
|
49
|
+
"security",
|
|
50
|
+
"auth",
|
|
51
|
+
"api-key",
|
|
52
|
+
"apikey",
|
|
53
|
+
"stripe",
|
|
54
|
+
"prefixed-token",
|
|
55
|
+
"hmac",
|
|
56
|
+
"scopes",
|
|
57
|
+
"server-only",
|
|
58
|
+
"node-crypto"
|
|
59
|
+
],
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"homepage": "https://github.com/ExorTek/auth/tree/master/packages/apikey#readme",
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "git+https://github.com/ExorTek/auth.git",
|
|
65
|
+
"directory": "packages/apikey"
|
|
66
|
+
},
|
|
67
|
+
"bugs": {
|
|
68
|
+
"url": "https://github.com/ExorTek/auth/issues"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=22.0.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"express": ">=4",
|
|
75
|
+
"fastify": ">=4",
|
|
76
|
+
"fastify-plugin": ">=4"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"express": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"fastify": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"fastify-plugin": {
|
|
86
|
+
"optional": true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@exortek/shared": "0.0.0",
|
|
91
|
+
"express": "^5.2.1",
|
|
92
|
+
"fastify": "^5.10.0",
|
|
93
|
+
"fastify-plugin": "^6.0.0"
|
|
94
|
+
},
|
|
95
|
+
"publishConfig": {
|
|
96
|
+
"access": "public"
|
|
97
|
+
}
|
|
98
|
+
}
|