vouchly 0.0.0 → 0.1.1
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/README.md +323 -3
- package/dist/index.d.mts +365 -0
- package/dist/index.mjs +722 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -4
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { CryptoKey, JWK, JWTPayload, ProtectedHeaderParameters } from "jose";
|
|
2
|
+
//#region src/cache/hash.d.ts
|
|
3
|
+
export declare function hashToken(token: string): Promise<string>;
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/cache/types.d.ts
|
|
6
|
+
type TokenCacheEntry = {
|
|
7
|
+
claims: JWTPayload;
|
|
8
|
+
/**
|
|
9
|
+
* Issuer.id, or the issuer's index in the verifier's list when it has no id.
|
|
10
|
+
*/
|
|
11
|
+
issuer: string;
|
|
12
|
+
source: 'local' | 'remote';
|
|
13
|
+
/**
|
|
14
|
+
* The token header's `typ`, for a locally verified token that carried one.
|
|
15
|
+
*/
|
|
16
|
+
typ?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The token header's `alg`, for a locally verified token.
|
|
19
|
+
*/
|
|
20
|
+
alg?: string;
|
|
21
|
+
};
|
|
22
|
+
interface ITokenCache {
|
|
23
|
+
get(key: string): Promise<TokenCacheEntry | undefined>;
|
|
24
|
+
/**
|
|
25
|
+
* @param ttl seconds; a value <= 0 stores nothing.
|
|
26
|
+
*/
|
|
27
|
+
set(key: string, entry: TokenCacheEntry, ttl: number): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
type MemoryTokenCacheOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of entries, default 10000.
|
|
32
|
+
*/
|
|
33
|
+
max?: number;
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/cache/memory.d.ts
|
|
37
|
+
type MemoryTokenCacheItem = {
|
|
38
|
+
entry: TokenCacheEntry;
|
|
39
|
+
expiresAt: number;
|
|
40
|
+
};
|
|
41
|
+
export declare class MemoryTokenCache implements ITokenCache {
|
|
42
|
+
protected max: number;
|
|
43
|
+
protected items: Map<string, MemoryTokenCacheItem>;
|
|
44
|
+
constructor(options?: MemoryTokenCacheOptions);
|
|
45
|
+
get(key: string): Promise<TokenCacheEntry | undefined>;
|
|
46
|
+
set(key: string, entry: TokenCacheEntry, ttl: number): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/discovery/types.d.ts
|
|
50
|
+
type ProviderMetadata = {
|
|
51
|
+
issuer: string;
|
|
52
|
+
/**
|
|
53
|
+
* Required by the discovery key source, not by introspection.
|
|
54
|
+
*/
|
|
55
|
+
jwks_uri?: string;
|
|
56
|
+
introspection_endpoint?: string;
|
|
57
|
+
};
|
|
58
|
+
type DiscoveryOptions = {
|
|
59
|
+
/**
|
|
60
|
+
* Seconds the provider metadata of an issuer is reused, default 600.
|
|
61
|
+
*/
|
|
62
|
+
cacheMaxAge?: number;
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/discovery/module.d.ts
|
|
66
|
+
/**
|
|
67
|
+
* Reads `<issuer>/.well-known/openid-configuration`. The issuer must come
|
|
68
|
+
* from configuration, never from a token. Answers are reused for
|
|
69
|
+
* cacheMaxAge seconds; after a failure, the issuer is not asked again for
|
|
70
|
+
* 30 seconds and calls fail fast instead.
|
|
71
|
+
*/
|
|
72
|
+
export declare function fetchProviderMetadata(issuer: string, fetchFn: typeof globalThis.fetch, options?: DiscoveryOptions): Promise<ProviderMetadata>;
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/error/constants.d.ts
|
|
75
|
+
export declare enum VouchlyErrorCode {
|
|
76
|
+
TOKEN_MALFORMED = "token_malformed",
|
|
77
|
+
TOKEN_EXPIRED = "token_expired",
|
|
78
|
+
TOKEN_INACTIVE = "token_inactive",
|
|
79
|
+
ISSUER_UNTRUSTED = "issuer_untrusted",
|
|
80
|
+
KEY_UNAVAILABLE = "key_unavailable",
|
|
81
|
+
SIGNATURE_INVALID = "signature_invalid",
|
|
82
|
+
CLAIM_INVALID = "claim_invalid",
|
|
83
|
+
INTROSPECTION_FAILED = "introspection_failed",
|
|
84
|
+
BINDING_MISMATCH = "binding_mismatch"
|
|
85
|
+
}
|
|
86
|
+
export declare const VOUCHLY_ERROR_MARKER: unique symbol;
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/error/types.d.ts
|
|
89
|
+
type VouchlyErrorOptions = {
|
|
90
|
+
cause?: unknown;
|
|
91
|
+
/**
|
|
92
|
+
* The HTTP status of a failed upstream answer, e.g. an introspection endpoint's.
|
|
93
|
+
*/
|
|
94
|
+
status?: number;
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/error/module.d.ts
|
|
98
|
+
export declare class VouchlyError extends Error {
|
|
99
|
+
readonly code: VouchlyErrorCode;
|
|
100
|
+
readonly status?: number;
|
|
101
|
+
readonly [VOUCHLY_ERROR_MARKER] = true;
|
|
102
|
+
constructor(code: VouchlyErrorCode, message?: string, options?: VouchlyErrorOptions);
|
|
103
|
+
}
|
|
104
|
+
export declare function isVouchlyError(input: unknown): input is VouchlyError;
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/token/types.d.ts
|
|
107
|
+
type ParsedToken = {
|
|
108
|
+
raw: string;
|
|
109
|
+
header: ProtectedHeaderParameters;
|
|
110
|
+
claims: JWTPayload;
|
|
111
|
+
};
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/token/module.d.ts
|
|
114
|
+
export declare function isCompactToken(token: string): boolean;
|
|
115
|
+
export declare function parseToken(token: string): ParsedToken;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/key/types.d.ts
|
|
118
|
+
type KeySourceContext = {
|
|
119
|
+
token: ParsedToken;
|
|
120
|
+
issuer: Issuer;
|
|
121
|
+
fetch: typeof fetch;
|
|
122
|
+
};
|
|
123
|
+
type KeySource = (context: KeySourceContext) => Promise<CryptoKey | JWK>;
|
|
124
|
+
type JWKSOptions = {
|
|
125
|
+
/**
|
|
126
|
+
* Milliseconds, see jose createRemoteJWKSet.
|
|
127
|
+
*/
|
|
128
|
+
cacheMaxAge?: number;
|
|
129
|
+
/**
|
|
130
|
+
* Milliseconds, see jose createRemoteJWKSet.
|
|
131
|
+
*/
|
|
132
|
+
cooldownDuration?: number;
|
|
133
|
+
/**
|
|
134
|
+
* Milliseconds, see jose createRemoteJWKSet.
|
|
135
|
+
*/
|
|
136
|
+
timeoutDuration?: number;
|
|
137
|
+
};
|
|
138
|
+
type CachedKeysOptions = {
|
|
139
|
+
/**
|
|
140
|
+
* The cache key of the context; one load is shared per key.
|
|
141
|
+
*/
|
|
142
|
+
key: (context: KeySourceContext) => string;
|
|
143
|
+
/**
|
|
144
|
+
* Seconds, default 300; 0 caches nothing.
|
|
145
|
+
*/
|
|
146
|
+
ttl?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Seconds, default 10, at most 2147483.
|
|
149
|
+
*/
|
|
150
|
+
timeout?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Maximum number of entries, default 100.
|
|
153
|
+
*/
|
|
154
|
+
max?: number;
|
|
155
|
+
};
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/key/cached.d.ts
|
|
158
|
+
export declare function cachedKeys(load: (context: KeySourceContext) => Promise<JWK>, options: CachedKeysOptions): KeySource;
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/key/discovery.d.ts
|
|
161
|
+
/**
|
|
162
|
+
* Resolves keys through the configured issuer's OIDC discovery document.
|
|
163
|
+
* A function issuer is refused: its only URL would be the token's own `iss`.
|
|
164
|
+
*/
|
|
165
|
+
export declare function discovery(options?: DiscoveryOptions): KeySource;
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/key/jwks.d.ts
|
|
168
|
+
export declare function jwks(url: string | URL, options?: JWKSOptions): KeySource;
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/key/module.d.ts
|
|
171
|
+
/**
|
|
172
|
+
* Resolves the key a token is verified with. Every failure, including a key
|
|
173
|
+
* the policy refuses, is a KEY_UNAVAILABLE VouchlyError.
|
|
174
|
+
*/
|
|
175
|
+
export declare function resolveVerificationKey(source: KeySource, context: KeySourceContext): Promise<CryptoKey>;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/key/static.d.ts
|
|
178
|
+
export declare function staticKeys(keys: JWK[]): KeySource;
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/issuer/types.d.ts
|
|
181
|
+
type IntrospectionAuthorizationContext = {
|
|
182
|
+
/**
|
|
183
|
+
* true when the previous attempt was answered with 401.
|
|
184
|
+
*/
|
|
185
|
+
retry: boolean;
|
|
186
|
+
};
|
|
187
|
+
type IntrospectionOptions = {
|
|
188
|
+
/**
|
|
189
|
+
* Default: the issuer's discovered introspection_endpoint.
|
|
190
|
+
*/
|
|
191
|
+
endpoint?: string;
|
|
192
|
+
/**
|
|
193
|
+
* Client credentials (sent as HTTP Basic), or a function returning the
|
|
194
|
+
* whole Authorization header value. After a 401 a function is called
|
|
195
|
+
* again with `retry: true` and the request is repeated once.
|
|
196
|
+
*/
|
|
197
|
+
authorization?: {
|
|
198
|
+
clientId: string;
|
|
199
|
+
clientSecret: string;
|
|
200
|
+
} | ((context: IntrospectionAuthorizationContext) => string | Promise<string>);
|
|
201
|
+
/**
|
|
202
|
+
* Upper bound, in seconds, for caching an introspection result.
|
|
203
|
+
*/
|
|
204
|
+
maxCacheTTL?: number;
|
|
205
|
+
};
|
|
206
|
+
type Issuer = {
|
|
207
|
+
id?: string;
|
|
208
|
+
/**
|
|
209
|
+
* A string matches `iss` exactly. A function sees UNVERIFIED claims:
|
|
210
|
+
* it only selects this config and never replaces verification.
|
|
211
|
+
*/
|
|
212
|
+
issuer: string | ((claims: JWTPayload) => boolean);
|
|
213
|
+
/**
|
|
214
|
+
* Default: discovery() when issuer is a string.
|
|
215
|
+
*/
|
|
216
|
+
keys?: KeySource;
|
|
217
|
+
/**
|
|
218
|
+
* Default: 'local'.
|
|
219
|
+
*/
|
|
220
|
+
verification?: 'local' | 'remote';
|
|
221
|
+
introspection?: IntrospectionOptions;
|
|
222
|
+
audience?: string | string[];
|
|
223
|
+
/**
|
|
224
|
+
* Default: the resolved key's algorithm only. A JWK without `alg` is
|
|
225
|
+
* imported with the token header's `alg`, restricted to what its key
|
|
226
|
+
* type supports; set this to pin the algorithm.
|
|
227
|
+
*/
|
|
228
|
+
algorithms?: string[];
|
|
229
|
+
typ?: string;
|
|
230
|
+
/**
|
|
231
|
+
* Seconds, default 0.
|
|
232
|
+
*/
|
|
233
|
+
clockTolerance?: number;
|
|
234
|
+
/**
|
|
235
|
+
* Default: ['exp'].
|
|
236
|
+
*/
|
|
237
|
+
requiredClaims?: string[];
|
|
238
|
+
claims?: (claims: JWTPayload) => boolean | Promise<boolean>;
|
|
239
|
+
};
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/issuer/match.d.ts
|
|
242
|
+
/**
|
|
243
|
+
* Whether an issuer config trusts a token by its UNVERIFIED claims. This only
|
|
244
|
+
* selects a config: verification still runs against it afterwards. A
|
|
245
|
+
* function matcher counts only when it returns exactly `true`; one that
|
|
246
|
+
* throws, or returns anything else (a Promise included), does not match.
|
|
247
|
+
*/
|
|
248
|
+
export declare function matchIssuer(issuer: Issuer, claims: JWTPayload): boolean;
|
|
249
|
+
/**
|
|
250
|
+
* The index of the first issuer that trusts the claims, or -1.
|
|
251
|
+
*/
|
|
252
|
+
export declare function selectIssuer(issuers: Issuer[], claims: JWTPayload): number;
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/introspection/module.d.ts
|
|
255
|
+
/**
|
|
256
|
+
* RFC 7662: asks the issuer's introspection endpoint about a token and
|
|
257
|
+
* answers the response as its claims. Claim rules are the caller's.
|
|
258
|
+
*/
|
|
259
|
+
export declare function introspect(token: string, issuer: Issuer, fetchFn: typeof globalThis.fetch): Promise<JWTPayload>;
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/introspection/types.d.ts
|
|
262
|
+
type IntrospectionResponse = {
|
|
263
|
+
active: boolean;
|
|
264
|
+
[key: string]: unknown;
|
|
265
|
+
};
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/verifier/types.d.ts
|
|
268
|
+
type VerifierOptions = {
|
|
269
|
+
issuers: Issuer[];
|
|
270
|
+
/**
|
|
271
|
+
* Default: a MemoryTokenCache; false disables caching.
|
|
272
|
+
*/
|
|
273
|
+
cache?: ITokenCache | false;
|
|
274
|
+
/**
|
|
275
|
+
* Names the group of verifiers sharing a cache passed in; required then.
|
|
276
|
+
* Every verifier using one namespace must have the same issuer
|
|
277
|
+
* configuration, since a cache hit skips the key lookup and the
|
|
278
|
+
* signature check. Must not contain NUL. The cache key is the SHA-256 of
|
|
279
|
+
* the namespace, a NUL character and the token.
|
|
280
|
+
*/
|
|
281
|
+
cacheNamespace?: string;
|
|
282
|
+
/**
|
|
283
|
+
* Default: the global fetch.
|
|
284
|
+
*/
|
|
285
|
+
fetch?: typeof fetch;
|
|
286
|
+
/**
|
|
287
|
+
* The Issuer.id that introspects tokens which are not compact JWTs.
|
|
288
|
+
*/
|
|
289
|
+
opaque?: string;
|
|
290
|
+
};
|
|
291
|
+
type VerifyOptions = {
|
|
292
|
+
/**
|
|
293
|
+
* The presented client certificate's SHA-256 thumbprint (base64url), for
|
|
294
|
+
* RFC 8705 bound tokens. A provider is only invoked for a bound token.
|
|
295
|
+
*/
|
|
296
|
+
certificateThumbprint?: string | (() => string | undefined | Promise<string | undefined>);
|
|
297
|
+
};
|
|
298
|
+
type VerifyResult = {
|
|
299
|
+
claims: JWTPayload;
|
|
300
|
+
issuer: Issuer;
|
|
301
|
+
source: 'local' | 'remote';
|
|
302
|
+
};
|
|
303
|
+
interface IVerifier {
|
|
304
|
+
verify(token: string, options?: VerifyOptions): Promise<VerifyResult>;
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/verifier/binding.d.ts
|
|
308
|
+
/**
|
|
309
|
+
* RFC 8705: a token carrying `cnf` is certificate-bound and fails closed
|
|
310
|
+
* unless the presented thumbprint equals `cnf.x5t#S256`. A `cnf` this check
|
|
311
|
+
* cannot satisfy (no `x5t#S256`, e.g. a DPoP `jkt`) fails too.
|
|
312
|
+
*/
|
|
313
|
+
export declare function assertCertificateBinding(claims: JWTPayload, thumbprint: string | undefined): void;
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/verifier/module.d.ts
|
|
316
|
+
type IssuerRecord = {
|
|
317
|
+
/**
|
|
318
|
+
* Issuer.id, or the issuer's index when it has none; the cache entry's issuer.
|
|
319
|
+
*/
|
|
320
|
+
name: string;
|
|
321
|
+
issuer: Issuer;
|
|
322
|
+
keys?: KeySource;
|
|
323
|
+
};
|
|
324
|
+
export declare class Verifier implements IVerifier {
|
|
325
|
+
protected records: IssuerRecord[];
|
|
326
|
+
protected cache: ITokenCache | undefined;
|
|
327
|
+
protected fetch: typeof fetch;
|
|
328
|
+
protected opaque: IssuerRecord | undefined;
|
|
329
|
+
protected cacheNamespace: string;
|
|
330
|
+
constructor(options: VerifierOptions);
|
|
331
|
+
verify(token: string, options?: VerifyOptions): Promise<VerifyResult>;
|
|
332
|
+
/**
|
|
333
|
+
* The claim sets whose `cnf` a result must satisfy: an introspected
|
|
334
|
+
* compact token is held to its own `cnf` as well as the response's.
|
|
335
|
+
*/
|
|
336
|
+
protected bindingClaims(claims: JWTPayload, source: VerifyResult['source'], token: ParsedToken | undefined): JWTPayload[];
|
|
337
|
+
protected selectRecord(token: ParsedToken): IssuerRecord;
|
|
338
|
+
protected verifyLocal(token: ParsedToken, record: IssuerRecord): Promise<JWTPayload>;
|
|
339
|
+
protected assertClaimsPredicate(issuer: Issuer, claims: JWTPayload): Promise<void>;
|
|
340
|
+
/**
|
|
341
|
+
* Verifies a token by RFC 7662 introspection and applies the issuer's
|
|
342
|
+
* audience and claims predicate to the response.
|
|
343
|
+
*/
|
|
344
|
+
protected verifyIntrospection(token: string, record: IssuerRecord): Promise<JWTPayload>;
|
|
345
|
+
protected introspect(token: string, key: string, record: IssuerRecord, options: VerifyOptions, parsed?: ParsedToken): Promise<VerifyResult>;
|
|
346
|
+
/**
|
|
347
|
+
* A cache failure is a miss: the token is verified again instead. An entry
|
|
348
|
+
* is only served under the record an uncached verify would pick, and only
|
|
349
|
+
* after this verifier's own rules that need no signature passed on it:
|
|
350
|
+
* a verifier sharing the cache may have applied other ones.
|
|
351
|
+
*/
|
|
352
|
+
protected readCache(key: string, token: ParsedToken | undefined): Promise<{
|
|
353
|
+
entry: TokenCacheEntry;
|
|
354
|
+
record: IssuerRecord;
|
|
355
|
+
} | undefined>;
|
|
356
|
+
/**
|
|
357
|
+
* Caches until `exp`, measured to the millisecond, or for at most `maxTTL` seconds when given. A token
|
|
358
|
+
* without `exp` is cached only under a `maxTTL`. A cache failure is ignored.
|
|
359
|
+
*/
|
|
360
|
+
protected writeCache(key: string, entry: TokenCacheEntry, exp: unknown, maxTTL?: number): Promise<void>;
|
|
361
|
+
}
|
|
362
|
+
export declare function createVerifier(options: VerifierOptions): Verifier;
|
|
363
|
+
//#endregion
|
|
364
|
+
export type { CachedKeysOptions, DiscoveryOptions, ITokenCache, IVerifier, IntrospectionAuthorizationContext, IntrospectionOptions, IntrospectionResponse, Issuer, JWKSOptions, KeySource, KeySourceContext, MemoryTokenCacheOptions, ParsedToken, ProviderMetadata, TokenCacheEntry, VerifierOptions, VerifyOptions, VerifyResult, VouchlyErrorOptions };
|
|
365
|
+
//# sourceMappingURL=index.d.mts.map
|