@peac/jwks-cache 0.10.9 → 0.10.10
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/LICENSE +1 -1
- package/dist/index.cjs +458 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +443 -12
- package/dist/index.js.map +1 -1
- package/package.json +16 -9
- package/dist/cache.js +0 -106
- package/dist/cache.js.map +0 -1
- package/dist/errors.js +0 -48
- package/dist/errors.js.map +0 -1
- package/dist/resolver.js +0 -309
- package/dist/resolver.js.map +0 -1
- package/dist/security.js +0 -88
- package/dist/security.js.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
package/dist/cache.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JWKS Cache error codes per execution pack specification.
|
|
3
|
-
*/
|
|
4
|
-
export const ErrorCodes = {
|
|
5
|
-
/** Network error fetching JWKS */
|
|
6
|
-
JWKS_FETCH_FAILED: 'E_JWKS_FETCH_FAILED',
|
|
7
|
-
/** Fetch timeout */
|
|
8
|
-
JWKS_TIMEOUT: 'E_JWKS_TIMEOUT',
|
|
9
|
-
/** Invalid JSON or structure */
|
|
10
|
-
JWKS_INVALID: 'E_JWKS_INVALID',
|
|
11
|
-
/** Response > 1MB */
|
|
12
|
-
JWKS_TOO_LARGE: 'E_JWKS_TOO_LARGE',
|
|
13
|
-
/** keys.length > 100 */
|
|
14
|
-
JWKS_TOO_MANY_KEYS: 'E_JWKS_TOO_MANY_KEYS',
|
|
15
|
-
/** Private IP or metadata URL blocked */
|
|
16
|
-
SSRF_BLOCKED: 'E_SSRF_BLOCKED',
|
|
17
|
-
/** Requested kid not in JWKS */
|
|
18
|
-
KEY_NOT_FOUND: 'E_KEY_NOT_FOUND',
|
|
19
|
-
/** All discovery paths failed */
|
|
20
|
-
ALL_PATHS_FAILED: 'E_ALL_PATHS_FAILED',
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* HTTP status codes for each error.
|
|
24
|
-
*/
|
|
25
|
-
export const ErrorHttpStatus = {
|
|
26
|
-
[ErrorCodes.JWKS_FETCH_FAILED]: 502,
|
|
27
|
-
[ErrorCodes.JWKS_TIMEOUT]: 504,
|
|
28
|
-
[ErrorCodes.JWKS_INVALID]: 502,
|
|
29
|
-
[ErrorCodes.JWKS_TOO_LARGE]: 502,
|
|
30
|
-
[ErrorCodes.JWKS_TOO_MANY_KEYS]: 502,
|
|
31
|
-
[ErrorCodes.SSRF_BLOCKED]: 403,
|
|
32
|
-
[ErrorCodes.KEY_NOT_FOUND]: 401,
|
|
33
|
-
[ErrorCodes.ALL_PATHS_FAILED]: 502,
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* JWKS error with code and HTTP status.
|
|
37
|
-
*/
|
|
38
|
-
export class JwksError extends Error {
|
|
39
|
-
code;
|
|
40
|
-
httpStatus;
|
|
41
|
-
constructor(code, message) {
|
|
42
|
-
super(message);
|
|
43
|
-
this.name = 'JwksError';
|
|
44
|
-
this.code = code;
|
|
45
|
-
this.httpStatus = ErrorHttpStatus[code];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,kCAAkC;IAClC,iBAAiB,EAAE,qBAAqB;IACxC,oBAAoB;IACpB,YAAY,EAAE,gBAAgB;IAC9B,gCAAgC;IAChC,YAAY,EAAE,gBAAgB;IAC9B,qBAAqB;IACrB,cAAc,EAAE,kBAAkB;IAClC,wBAAwB;IACxB,kBAAkB,EAAE,sBAAsB;IAC1C,yCAAyC;IACzC,YAAY,EAAE,gBAAgB;IAC9B,gCAAgC;IAChC,aAAa,EAAE,iBAAiB;IAChC,iCAAiC;IACjC,gBAAgB,EAAE,oBAAoB;CAC9B,CAAC;AAIX;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA8B;IACxD,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,GAAG;IACnC,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,GAAG;IAC9B,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,GAAG;IAC9B,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,GAAG;IAChC,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,GAAG;IACpC,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,GAAG;IAC9B,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,GAAG;IAC/B,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,GAAG;CACnC,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,IAAI,CAAY;IAChB,UAAU,CAAS;IAE5B,YAAY,IAAe,EAAE,OAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
package/dist/resolver.js
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JWKS resolver with multi-path discovery and caching.
|
|
3
|
-
*/
|
|
4
|
-
import { ErrorCodes, JwksError } from './errors.js';
|
|
5
|
-
import { validateUrl } from './security.js';
|
|
6
|
-
import { InMemoryCache, buildCacheKey, parseCacheControlMaxAge } from './cache.js';
|
|
7
|
-
const DEFAULT_TTL_SECONDS = 3600; // 1 hour
|
|
8
|
-
const MAX_TTL_SECONDS = 86400; // 24 hours
|
|
9
|
-
const MIN_TTL_SECONDS = 60;
|
|
10
|
-
const DEFAULT_TIMEOUT_MS = 5000;
|
|
11
|
-
const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024; // 1MB
|
|
12
|
-
const DEFAULT_MAX_KEYS = 100;
|
|
13
|
-
const DEFAULT_MAX_STALE_AGE_SECONDS = 172800; // 48 hours
|
|
14
|
-
/**
|
|
15
|
-
* Create a JWKS key resolver with singleflight dedup.
|
|
16
|
-
*
|
|
17
|
-
* Concurrent calls for the same issuer+keyid coalesce into a single
|
|
18
|
-
* network request, preventing thundering herd on cache miss.
|
|
19
|
-
*
|
|
20
|
-
* @param options - Resolver options
|
|
21
|
-
* @returns Key resolver function
|
|
22
|
-
*/
|
|
23
|
-
export function createResolver(options = {}) {
|
|
24
|
-
const cache = options.cache ?? new InMemoryCache();
|
|
25
|
-
const { defaultTtlSeconds = DEFAULT_TTL_SECONDS, maxTtlSeconds = MAX_TTL_SECONDS, minTtlSeconds = MIN_TTL_SECONDS, timeoutMs = DEFAULT_TIMEOUT_MS, maxResponseBytes = DEFAULT_MAX_RESPONSE_BYTES, maxKeys = DEFAULT_MAX_KEYS, isAllowedHost, allowLocalhost = false, allowStale = false, maxStaleAgeSeconds = DEFAULT_MAX_STALE_AGE_SECONDS, } = options;
|
|
26
|
-
// Singleflight: dedup concurrent resolves for the same issuer+keyid
|
|
27
|
-
const inflight = new Map();
|
|
28
|
-
return async (issuer, keyid) => {
|
|
29
|
-
const flightKey = `${issuer}:${keyid}`;
|
|
30
|
-
const existing = inflight.get(flightKey);
|
|
31
|
-
if (existing) {
|
|
32
|
-
const resolvedKey = await existing;
|
|
33
|
-
return resolvedKey ? createJwkVerifier(resolvedKey.jwk) : null;
|
|
34
|
-
}
|
|
35
|
-
const promise = resolveKey(issuer, keyid, {
|
|
36
|
-
cache,
|
|
37
|
-
defaultTtlSeconds,
|
|
38
|
-
maxTtlSeconds,
|
|
39
|
-
minTtlSeconds,
|
|
40
|
-
timeoutMs,
|
|
41
|
-
maxResponseBytes,
|
|
42
|
-
maxKeys,
|
|
43
|
-
isAllowedHost,
|
|
44
|
-
allowLocalhost,
|
|
45
|
-
allowStale,
|
|
46
|
-
maxStaleAgeSeconds,
|
|
47
|
-
}).finally(() => {
|
|
48
|
-
inflight.delete(flightKey);
|
|
49
|
-
});
|
|
50
|
-
inflight.set(flightKey, promise);
|
|
51
|
-
const resolvedKey = await promise;
|
|
52
|
-
if (!resolvedKey) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
return createJwkVerifier(resolvedKey.jwk);
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Resolve a key by issuer and key ID.
|
|
60
|
-
*
|
|
61
|
-
* Discovery order (per TAP spec):
|
|
62
|
-
* 1. /.well-known/jwks
|
|
63
|
-
* 2. /keys?keyID=<kid>
|
|
64
|
-
* 3. /.well-known/jwks.json (fallback)
|
|
65
|
-
*/
|
|
66
|
-
export async function resolveKey(issuer, keyid, options) {
|
|
67
|
-
const { cache, isAllowedHost, allowLocalhost, allowStale, maxStaleAgeSeconds } = options;
|
|
68
|
-
// Normalize issuer to origin
|
|
69
|
-
const issuerOrigin = new URL(issuer).origin;
|
|
70
|
-
const cacheKey = buildCacheKey(issuerOrigin, keyid);
|
|
71
|
-
// Check cache first
|
|
72
|
-
const cached = await cache.get(cacheKey);
|
|
73
|
-
if (cached) {
|
|
74
|
-
return {
|
|
75
|
-
jwk: cached.jwk,
|
|
76
|
-
source: '/.well-known/jwks',
|
|
77
|
-
cached: true,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
// Discovery paths in order
|
|
81
|
-
const paths = [
|
|
82
|
-
{
|
|
83
|
-
url: `${issuerOrigin}/.well-known/jwks`,
|
|
84
|
-
source: '/.well-known/jwks',
|
|
85
|
-
isSingleKey: false,
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
url: `${issuerOrigin}/keys?keyID=${encodeURIComponent(keyid)}`,
|
|
89
|
-
source: '/keys',
|
|
90
|
-
isSingleKey: true,
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
url: `${issuerOrigin}/.well-known/jwks.json`,
|
|
94
|
-
source: '/.well-known/jwks.json',
|
|
95
|
-
isSingleKey: false,
|
|
96
|
-
},
|
|
97
|
-
];
|
|
98
|
-
const errors = [];
|
|
99
|
-
for (const path of paths) {
|
|
100
|
-
try {
|
|
101
|
-
// Validate URL for SSRF
|
|
102
|
-
validateUrl(path.url, { isAllowedHost, allowLocalhost });
|
|
103
|
-
const result = await fetchWithTimeout(path.url, options.timeoutMs);
|
|
104
|
-
// Check response size
|
|
105
|
-
const contentLength = result.headers.get('content-length');
|
|
106
|
-
if (contentLength && parseInt(contentLength, 10) > options.maxResponseBytes) {
|
|
107
|
-
throw new JwksError(ErrorCodes.JWKS_TOO_LARGE, `Response too large: ${contentLength} bytes`);
|
|
108
|
-
}
|
|
109
|
-
// Parse response
|
|
110
|
-
const text = await result.text();
|
|
111
|
-
if (text.length > options.maxResponseBytes) {
|
|
112
|
-
throw new JwksError(ErrorCodes.JWKS_TOO_LARGE, `Response too large: ${text.length} bytes`);
|
|
113
|
-
}
|
|
114
|
-
let data;
|
|
115
|
-
try {
|
|
116
|
-
data = JSON.parse(text);
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
throw new JwksError(ErrorCodes.JWKS_INVALID, 'Invalid JSON response');
|
|
120
|
-
}
|
|
121
|
-
// Extract JWK
|
|
122
|
-
let jwk = null;
|
|
123
|
-
if (path.isSingleKey) {
|
|
124
|
-
// Single key endpoint returns JWK directly
|
|
125
|
-
jwk = validateJwk(data);
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
// JWKS endpoint returns key set
|
|
129
|
-
const jwks = validateJwks(data, options.maxKeys);
|
|
130
|
-
jwk = findKey(jwks, keyid);
|
|
131
|
-
}
|
|
132
|
-
if (!jwk) {
|
|
133
|
-
continue; // Try next path
|
|
134
|
-
}
|
|
135
|
-
// Calculate TTL
|
|
136
|
-
const cacheControlMaxAge = parseCacheControlMaxAge(result.headers.get('cache-control'));
|
|
137
|
-
const ttl = calculateTtl(cacheControlMaxAge, options.defaultTtlSeconds, options.minTtlSeconds, options.maxTtlSeconds);
|
|
138
|
-
// Cache the key
|
|
139
|
-
const now = Math.floor(Date.now() / 1000);
|
|
140
|
-
await cache.set(cacheKey, {
|
|
141
|
-
jwk,
|
|
142
|
-
expiresAt: now + ttl,
|
|
143
|
-
etag: result.headers.get('etag') ?? undefined,
|
|
144
|
-
});
|
|
145
|
-
return {
|
|
146
|
-
jwk,
|
|
147
|
-
source: path.source,
|
|
148
|
-
cached: false,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
errors.push(error);
|
|
153
|
-
// Continue to next path
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// All paths failed -- try stale fallback if allowed.
|
|
157
|
-
// Only fall back on transient network errors. Fail closed on security policy
|
|
158
|
-
// violations (SSRF), semantic errors (invalid JWKS, too many keys, too large),
|
|
159
|
-
// and parse failures -- these indicate the server is misconfigured or
|
|
160
|
-
// compromised, not that the network is temporarily unreachable.
|
|
161
|
-
if (errors.length > 0) {
|
|
162
|
-
// Fail closed: stale only if every error is a known-transient JwksError.
|
|
163
|
-
// Non-JwksError (bugs, unexpected throws) must NOT widen stale eligibility.
|
|
164
|
-
const allTransient = errors.every((e) => e instanceof JwksError &&
|
|
165
|
-
(e.code === ErrorCodes.JWKS_FETCH_FAILED || e.code === ErrorCodes.JWKS_TIMEOUT));
|
|
166
|
-
if (allowStale && allTransient && 'getStale' in cache && typeof cache.getStale === 'function') {
|
|
167
|
-
const staleEntry = await cache.getStale(cacheKey);
|
|
168
|
-
if (staleEntry) {
|
|
169
|
-
const now = Math.floor(Date.now() / 1000);
|
|
170
|
-
const staleAge = now - staleEntry.expiresAt;
|
|
171
|
-
if (staleAge >= 0 && staleAge <= maxStaleAgeSeconds) {
|
|
172
|
-
return {
|
|
173
|
-
jwk: staleEntry.jwk,
|
|
174
|
-
source: '/.well-known/jwks',
|
|
175
|
-
cached: true,
|
|
176
|
-
stale: true,
|
|
177
|
-
staleAgeSeconds: staleAge,
|
|
178
|
-
keyExpiredAt: staleEntry.expiresAt,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
const lastError = errors[errors.length - 1];
|
|
184
|
-
if (lastError instanceof JwksError) {
|
|
185
|
-
throw lastError;
|
|
186
|
-
}
|
|
187
|
-
throw new JwksError(ErrorCodes.ALL_PATHS_FAILED, `All discovery paths failed for ${issuerOrigin}`);
|
|
188
|
-
}
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Fetch with timeout.
|
|
193
|
-
*/
|
|
194
|
-
async function fetchWithTimeout(url, timeoutMs) {
|
|
195
|
-
const controller = new AbortController();
|
|
196
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
197
|
-
try {
|
|
198
|
-
const response = await fetch(url, {
|
|
199
|
-
signal: controller.signal,
|
|
200
|
-
redirect: 'error', // No redirect following (fail-closed)
|
|
201
|
-
headers: {
|
|
202
|
-
Accept: 'application/json',
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
if (!response.ok) {
|
|
206
|
-
throw new JwksError(ErrorCodes.JWKS_FETCH_FAILED, `HTTP ${response.status}: ${response.statusText}`);
|
|
207
|
-
}
|
|
208
|
-
return response;
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
if (error.name === 'AbortError') {
|
|
212
|
-
throw new JwksError(ErrorCodes.JWKS_TIMEOUT, `Fetch timeout after ${timeoutMs}ms`);
|
|
213
|
-
}
|
|
214
|
-
if (error instanceof JwksError) {
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
throw new JwksError(ErrorCodes.JWKS_FETCH_FAILED, `Fetch failed: ${error.message}`);
|
|
218
|
-
}
|
|
219
|
-
finally {
|
|
220
|
-
clearTimeout(timeout);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Validate and extract JWK from response data.
|
|
225
|
-
*/
|
|
226
|
-
function validateJwk(data) {
|
|
227
|
-
if (!data || typeof data !== 'object') {
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
const jwk = data;
|
|
231
|
-
if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519' || typeof jwk.x !== 'string') {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
return {
|
|
235
|
-
kty: jwk.kty,
|
|
236
|
-
crv: jwk.crv,
|
|
237
|
-
x: jwk.x,
|
|
238
|
-
kid: typeof jwk.kid === 'string' ? jwk.kid : undefined,
|
|
239
|
-
use: typeof jwk.use === 'string' ? jwk.use : undefined,
|
|
240
|
-
alg: typeof jwk.alg === 'string' ? jwk.alg : undefined,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Validate JWKS structure.
|
|
245
|
-
*/
|
|
246
|
-
function validateJwks(data, maxKeys) {
|
|
247
|
-
if (!data || typeof data !== 'object') {
|
|
248
|
-
throw new JwksError(ErrorCodes.JWKS_INVALID, 'Invalid JWKS structure');
|
|
249
|
-
}
|
|
250
|
-
const jwks = data;
|
|
251
|
-
if (!Array.isArray(jwks.keys)) {
|
|
252
|
-
throw new JwksError(ErrorCodes.JWKS_INVALID, 'JWKS must have keys array');
|
|
253
|
-
}
|
|
254
|
-
if (jwks.keys.length > maxKeys) {
|
|
255
|
-
throw new JwksError(ErrorCodes.JWKS_TOO_MANY_KEYS, `Too many keys: ${jwks.keys.length} > ${maxKeys}`);
|
|
256
|
-
}
|
|
257
|
-
return { keys: jwks.keys };
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Find key by ID in JWKS.
|
|
261
|
-
*/
|
|
262
|
-
function findKey(jwks, keyid) {
|
|
263
|
-
for (const key of jwks.keys) {
|
|
264
|
-
if (key.kid === keyid) {
|
|
265
|
-
return validateJwk(key);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Calculate TTL from Cache-Control and options.
|
|
272
|
-
*/
|
|
273
|
-
function calculateTtl(cacheControlMaxAge, defaultTtl, minTtl, maxTtl) {
|
|
274
|
-
let ttl = cacheControlMaxAge ?? defaultTtl;
|
|
275
|
-
ttl = Math.max(minTtl, ttl);
|
|
276
|
-
ttl = Math.min(maxTtl, ttl);
|
|
277
|
-
return ttl;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Import a JWK as Ed25519 public key.
|
|
281
|
-
*
|
|
282
|
-
* Returns an opaque key object (runtime-neutral).
|
|
283
|
-
* Use createJwkVerifier() for a complete SignatureVerifier.
|
|
284
|
-
*/
|
|
285
|
-
export async function importJwkAsEd25519(jwk) {
|
|
286
|
-
return globalThis.crypto.subtle.importKey('jwk', {
|
|
287
|
-
kty: jwk.kty,
|
|
288
|
-
crv: jwk.crv,
|
|
289
|
-
x: jwk.x,
|
|
290
|
-
}, { name: 'Ed25519' }, false, ['verify']);
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Create a SignatureVerifier from a JWK.
|
|
294
|
-
*
|
|
295
|
-
* Convenience function for TAP integration.
|
|
296
|
-
*
|
|
297
|
-
* @param jwk - Ed25519 JWK
|
|
298
|
-
* @returns SignatureVerifier function
|
|
299
|
-
*/
|
|
300
|
-
export async function createJwkVerifier(jwk) {
|
|
301
|
-
const key = await importJwkAsEd25519(jwk);
|
|
302
|
-
return async (data, signature) => {
|
|
303
|
-
// Create proper ArrayBuffer views to satisfy TypeScript
|
|
304
|
-
const sigBuffer = new Uint8Array(signature).buffer;
|
|
305
|
-
const dataBuffer = new Uint8Array(data).buffer;
|
|
306
|
-
return globalThis.crypto.subtle.verify('Ed25519', key, sigBuffer, dataBuffer);
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
//# sourceMappingURL=resolver.js.map
|
package/dist/resolver.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/security.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSRF protection for JWKS fetching.
|
|
3
|
-
*
|
|
4
|
-
* Edge runtimes cannot reliably block private IPs via DNS resolution.
|
|
5
|
-
* This module implements what IS possible at the edge.
|
|
6
|
-
*/
|
|
7
|
-
import { ErrorCodes, JwksError } from './errors.js';
|
|
8
|
-
/**
|
|
9
|
-
* Validate URL for SSRF protection.
|
|
10
|
-
*
|
|
11
|
-
* @param url - URL to validate
|
|
12
|
-
* @param options - Validation options
|
|
13
|
-
* @throws JwksError if URL is blocked
|
|
14
|
-
*/
|
|
15
|
-
export function validateUrl(url, options = {}) {
|
|
16
|
-
let parsed;
|
|
17
|
-
try {
|
|
18
|
-
parsed = new URL(url);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
throw new JwksError(ErrorCodes.SSRF_BLOCKED, `Invalid URL: ${url}`);
|
|
22
|
-
}
|
|
23
|
-
// HTTPS required (except localhost in dev)
|
|
24
|
-
if (parsed.protocol !== 'https:') {
|
|
25
|
-
if (parsed.protocol === 'http:' && options.allowLocalhost && isLocalhostHost(parsed.hostname)) {
|
|
26
|
-
// Allow http://localhost in dev mode
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
throw new JwksError(ErrorCodes.SSRF_BLOCKED, `HTTPS required, got ${parsed.protocol}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// Block localhost variants (unless explicitly allowed)
|
|
33
|
-
if (isLocalhostHost(parsed.hostname) && !options.allowLocalhost) {
|
|
34
|
-
throw new JwksError(ErrorCodes.SSRF_BLOCKED, `Localhost blocked: ${parsed.hostname}`);
|
|
35
|
-
}
|
|
36
|
-
// Block literal IP addresses in URL
|
|
37
|
-
if (isLiteralIp(parsed.hostname)) {
|
|
38
|
-
throw new JwksError(ErrorCodes.SSRF_BLOCKED, `Literal IP addresses blocked: ${parsed.hostname}`);
|
|
39
|
-
}
|
|
40
|
-
// Check enterprise allowlist if provided
|
|
41
|
-
if (options.isAllowedHost && !options.isAllowedHost(parsed.hostname)) {
|
|
42
|
-
throw new JwksError(ErrorCodes.SSRF_BLOCKED, `Host not in allowlist: ${parsed.hostname}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Check if hostname is localhost variant.
|
|
47
|
-
*/
|
|
48
|
-
function isLocalhostHost(hostname) {
|
|
49
|
-
const lower = hostname.toLowerCase();
|
|
50
|
-
return (lower === 'localhost' ||
|
|
51
|
-
lower === '127.0.0.1' ||
|
|
52
|
-
lower === '::1' ||
|
|
53
|
-
lower === '[::1]' ||
|
|
54
|
-
lower.endsWith('.localhost'));
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Check if hostname is a literal IP address.
|
|
58
|
-
*/
|
|
59
|
-
function isLiteralIp(hostname) {
|
|
60
|
-
// IPv4 pattern
|
|
61
|
-
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname)) {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
// IPv6 pattern (with or without brackets)
|
|
65
|
-
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
// Colon indicates IPv6 (no brackets)
|
|
69
|
-
if (hostname.includes(':')) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Check if hostname is a metadata IP.
|
|
76
|
-
*/
|
|
77
|
-
export function isMetadataIp(hostname) {
|
|
78
|
-
// AWS/GCP/Azure metadata service
|
|
79
|
-
if (hostname === '169.254.169.254') {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
// Link-local range (169.254.x.x)
|
|
83
|
-
if (hostname.startsWith('169.254.')) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=security.js.map
|
package/dist/security.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,UAGI,EAAE;IAEN,IAAI,MAAW,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,gBAAgB,GAAG,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,2CAA2C;IAC3C,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,cAAc,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9F,qCAAqC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,uBAAuB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IAED,uDAAuD;IACvD,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,sBAAsB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,oCAAoC;IACpC,IAAI,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CACjB,UAAU,CAAC,YAAY,EACvB,iCAAiC,MAAM,CAAC,QAAQ,EAAE,CACnD,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,IAAI,OAAO,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,0BAA0B,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,CACL,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,WAAW;QACrB,KAAK,KAAK,KAAK;QACf,KAAK,KAAK,OAAO;QACjB,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,QAAgB;IACnC,eAAe;IACf,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0CAA0C;IAC1C,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qCAAqC;IACrC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,iCAAiC;IACjC,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iCAAiC;IACjC,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/types.js
DELETED
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|