@web-ts-toolkit/oidc-vault-dpop-client 0.50.0 → 0.52.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/README.md +483 -73
- package/index.d.mts +182 -81
- package/index.d.ts +182 -81
- package/index.js +943 -226
- package/index.mjs +946 -229
- package/llms.txt +52 -37
- package/package.json +2 -2
package/index.d.mts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser-only DPoP client (CLIENT-02 port of apps/oidc-vault-dpop-example/src/auth/*).
|
|
3
|
-
* No `node:*` or `express` imports. Browser globals (crypto.subtle, indexedDB,
|
|
4
|
-
* sessionStorage, navigator.locks, BroadcastChannel) only behind
|
|
5
|
-
* assertDpopBrowserFeatures / lazy factory calls, never at module top-level.
|
|
6
|
-
*/
|
|
7
|
-
|
|
1
|
+
/** Public-only P-256 JWK carried in proof headers. */
|
|
8
2
|
interface PublicDpopJwk {
|
|
9
3
|
kty: 'EC';
|
|
10
4
|
crv: 'P-256';
|
|
11
5
|
x: string;
|
|
12
6
|
y: string;
|
|
13
7
|
}
|
|
8
|
+
/** Validated persistent signing key and its canonical RFC 7638 thumbprint. */
|
|
14
9
|
interface DpopKey {
|
|
10
|
+
/** Non-extractable ES256 signing key; possession does not itself imply a live session. */
|
|
15
11
|
readonly privateKey: CryptoKey;
|
|
16
12
|
readonly publicJwk: Readonly<PublicDpopJwk>;
|
|
17
13
|
readonly jkt: string;
|
|
18
14
|
readonly scopeId: string;
|
|
19
15
|
}
|
|
20
16
|
|
|
17
|
+
/** Persistent key identity; use the same public origins/mount as the session and backend. */
|
|
21
18
|
interface DpopKeyScope {
|
|
19
|
+
/** Browser location.origin, HTTPS or loopback HTTP. */
|
|
22
20
|
frontendOrigin: string;
|
|
21
|
+
/** Public vault origin, HTTPS or loopback HTTP, with no path/query/fragment. */
|
|
23
22
|
backendOrigin: string;
|
|
23
|
+
/** Absolute public mount; unreserved escapes, dot segments and trailing slashes normalize. */
|
|
24
24
|
basePath: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -31,8 +31,9 @@ interface DpopKeyScope {
|
|
|
31
31
|
* Call with the default `create=true` ONLY at fresh login (`session.login()`
|
|
32
32
|
* does this for you). All credential-bearing operations use `create=false`,
|
|
33
33
|
* so losing IndexedDB cannot silently rebind a login: a missing or changed
|
|
34
|
-
* key throws `DPOP_KEY_LOST` and the caller must start a fresh login.
|
|
35
|
-
*
|
|
34
|
+
* key throws `DPOP_KEY_LOST` and the caller must start a fresh login. An
|
|
35
|
+
* unusable existing record needs explicit `session.resetKey()` before login;
|
|
36
|
+
* this helper never deletes or replaces it. No ephemeral-key or Bearer fallback.
|
|
36
37
|
*
|
|
37
38
|
* Candidate crypto work happens outside the readwrite transaction. IndexedDB
|
|
38
39
|
* serializes the final read/add across tabs, making exactly one candidate win.
|
|
@@ -44,44 +45,39 @@ declare const getOrCreateDpopKey: (input: DpopKeyScope, options?: {
|
|
|
44
45
|
create?: boolean;
|
|
45
46
|
}) => Promise<DpopKey>;
|
|
46
47
|
|
|
48
|
+
/** Inputs for one fresh ES256 request proof; the caller owns the HTTP request and retry policy. */
|
|
47
49
|
interface DpopProofInput {
|
|
50
|
+
/** Exact uppercase HTTP method. */
|
|
48
51
|
method: string;
|
|
52
|
+
/** Absolute HTTP(S) target. Proof normalization omits query/fragment and preserves reserved path escapes. */
|
|
49
53
|
url: string | URL;
|
|
50
54
|
/** API only. Vault login/exchange/refresh/logout present no access token. */
|
|
51
55
|
accessToken?: string;
|
|
56
|
+
/** Optional server-issued value; reuse only within the trusted protection space and signing key. */
|
|
52
57
|
nonce?: string;
|
|
58
|
+
/** Epoch-millisecond clock; default Date.now. Proof iat is integer epoch seconds. */
|
|
53
59
|
now?: () => number;
|
|
60
|
+
/** Reject with signal.reason before/after hashing and while signing; already-started Web Crypto may still finish. */
|
|
61
|
+
signal?: AbortSignal;
|
|
54
62
|
}
|
|
55
63
|
/**
|
|
56
64
|
* Mint a fresh DPoP proof for exactly one request attempt with the scoped key.
|
|
57
65
|
*
|
|
58
66
|
* Every invocation signs a new proof: exact uppercase `htm`, canonical absolute
|
|
59
|
-
* `htu` without query/fragment
|
|
67
|
+
* `htu` without query/fragment, current integer
|
|
60
68
|
* `iat`, fresh 128-bit random `jti`, API-only `ath`, and the optional server
|
|
61
69
|
* `nonce`. The protected header is `{ typ: 'dpop+jwt', alg: 'ES256', jwk:
|
|
62
70
|
* <public P-256 JWK> }`. Call again — with a new `jti`/`iat`/signature — for
|
|
63
71
|
* every nonce or refresh retry; never reuse a proof across attempts.
|
|
72
|
+
* Unreserved path escapes and dot segments normalize; reserved escapes stay
|
|
73
|
+
* encoded. This helper signs only: it does not send HTTP or enforce API scopes.
|
|
64
74
|
*
|
|
65
75
|
* Canonical import: `import { createDpopProof } from
|
|
66
76
|
* '@web-ts-toolkit/oidc-vault-dpop-client'` (named root import).
|
|
67
77
|
*/
|
|
68
78
|
declare const createDpopProof: (key: DpopKey, input: DpopProofInput) => Promise<string>;
|
|
69
79
|
|
|
70
|
-
/**
|
|
71
|
-
type OidcVaultDpopAlgorithm = 'ES256' | 'PS256' | 'RS256';
|
|
72
|
-
/**
|
|
73
|
-
* Immutable persisted sender constraint. `jkt` is the canonical RFC 7638
|
|
74
|
-
* SHA-256 JWK thumbprint (43 base64url characters). Store no JWK, private key,
|
|
75
|
-
* proof algorithm, or historical enforcement mode in this record.
|
|
76
|
-
*/
|
|
77
|
-
interface OidcVaultDpopBinding {
|
|
78
|
-
type: 'dpop';
|
|
79
|
-
jkt: string;
|
|
80
|
-
}
|
|
81
|
-
/** Verified request context; `alg` is checked against the current proof policy. */
|
|
82
|
-
interface OidcVaultVerifiedDpopBinding extends OidcVaultDpopBinding {
|
|
83
|
-
alg: OidcVaultDpopAlgorithm;
|
|
84
|
-
}
|
|
80
|
+
/** Public JSON profile returned by exchange/refresh. */
|
|
85
81
|
interface OidcVaultUserProfile {
|
|
86
82
|
sub: string;
|
|
87
83
|
email?: string;
|
|
@@ -89,46 +85,23 @@ interface OidcVaultUserProfile {
|
|
|
89
85
|
preferredUsername?: string;
|
|
90
86
|
[key: string]: unknown;
|
|
91
87
|
}
|
|
92
|
-
|
|
93
|
-
subject: string;
|
|
94
|
-
sessionId?: string;
|
|
95
|
-
scope?: string;
|
|
96
|
-
claims?: Record<string, unknown>;
|
|
97
|
-
/** Verified sender constraint. Legacy adapters may omit it only for genuinely unbound credentials. */
|
|
98
|
-
confirmation?: OidcVaultAccessTokenConfirmation | null;
|
|
99
|
-
}
|
|
100
|
-
/** Canonical RFC 7638 SHA-256 thumbprint derived from verified token/introspection data, never from a proof alone. */
|
|
101
|
-
interface OidcVaultAccessTokenConfirmation {
|
|
102
|
-
jkt: string;
|
|
103
|
-
}
|
|
104
|
-
interface OidcVaultAuthContext extends OidcVaultAccessTokenValidationResult {
|
|
105
|
-
token: string;
|
|
106
|
-
/** Present only after original-key proof, API ath, nonce and shared replay admission all pass. */
|
|
107
|
-
deviceBinding?: Readonly<OidcVaultVerifiedDpopBinding>;
|
|
108
|
-
}
|
|
109
|
-
declare module 'express-serve-static-core' {
|
|
110
|
-
interface Request {
|
|
111
|
-
auth?: OidcVaultAuthContext;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
88
|
+
/** Vault handle transport; independent of DPoP mode. Default: body. */
|
|
114
89
|
type OidcVaultSessionTransport = 'body' | 'cookie';
|
|
115
90
|
|
|
91
|
+
/** Contract-checked browser credential. Decoding does not authenticate the JWT; the API must verify it. */
|
|
116
92
|
interface DpopAccessToken {
|
|
117
93
|
readonly accessToken: string;
|
|
118
94
|
readonly tokenType: 'DPoP';
|
|
95
|
+
/** Safe epoch milliseconds, capped by JWT exp and response/peer lifetime. May already be expired (e.g. expiresIn: 0); check before reuse. */
|
|
119
96
|
readonly expiresAt: number;
|
|
97
|
+
/** Exact canonical RFC 7638 SHA-256 thumbprint from the one-field cnf object, matching the persistent browser key. */
|
|
120
98
|
readonly jkt: string;
|
|
99
|
+
/** Opaque response generation for cookie-peer coordination, not a server session handle. */
|
|
121
100
|
readonly generation: string;
|
|
122
101
|
readonly user?: OidcVaultUserProfile;
|
|
123
102
|
}
|
|
124
103
|
|
|
125
|
-
/**
|
|
126
|
-
* Browser-only DPoP client (CLIENT-02 port of apps/oidc-vault-dpop-example/src/auth/*).
|
|
127
|
-
* No `node:*` or `express` imports. Browser globals (crypto.subtle, indexedDB,
|
|
128
|
-
* sessionStorage, navigator.locks, BroadcastChannel) only behind
|
|
129
|
-
* assertDpopBrowserFeatures / lazy factory calls, never at module top-level.
|
|
130
|
-
*/
|
|
131
|
-
/** Copyable private example helper. Recognition is NOT proof of possession or an API sender constraint. */
|
|
104
|
+
/** Current opt-in recognition signal source. Recognition is not proof of possession or an API sender constraint. */
|
|
132
105
|
type DeviceFingerprintSignalSource = () => Promise<string | undefined>;
|
|
133
106
|
interface DeviceFingerprintOptions {
|
|
134
107
|
/** Must match the backend's fingerprintRecognition.headerName; default X-Device-Fingerprint. */
|
|
@@ -161,30 +134,88 @@ interface FingerprintJsAgent {
|
|
|
161
134
|
*/
|
|
162
135
|
declare const fingerprintJsSignalSource: (load: () => Promise<FingerprintJsAgent>) => DeviceFingerprintSignalSource;
|
|
163
136
|
|
|
137
|
+
/** Configuration for one browser context and public vault mount. */
|
|
164
138
|
interface OidcVaultDpopSessionOptions {
|
|
139
|
+
/** Pinned public scheme/host/port, HTTPS or loopback HTTP; no path/query/fragment. */
|
|
165
140
|
backendOrigin: string;
|
|
141
|
+
/** Exact public vault mount; default /auth/oidc. Trailing slashes normalize away. */
|
|
166
142
|
basePath?: string;
|
|
143
|
+
/** Default body (tab-local handle). Cookie additionally requires Web Locks and BroadcastChannel. */
|
|
167
144
|
sessionTransport?: OidcVaultSessionTransport;
|
|
168
145
|
/** Current signal on login/exchange/refresh only; optional recognition, never PoP. */
|
|
169
146
|
fingerprint?: DeviceFingerprint;
|
|
147
|
+
/**
|
|
148
|
+
* Per-attempt vault HTTP deadline in milliseconds, including headers, streamed
|
|
149
|
+
* body and JSON parsing. Default 15,000; integer 1..2,147,483,647. Starts at
|
|
150
|
+
* fetch, not queue/key/proof work. Timeout never automatically retries a mutation.
|
|
151
|
+
*/
|
|
152
|
+
requestTimeoutMs?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Maximum actual vault response bytes read from the decoded HTTP body stream,
|
|
155
|
+
* independent of Content-Length. Default 1,048,576 (1 MiB); positive safe integer.
|
|
156
|
+
*/
|
|
157
|
+
maxResponseBytes?: number;
|
|
158
|
+
/** Default global fetch. Injected responses must expose a standard streaming body. */
|
|
170
159
|
fetch?: typeof globalThis.fetch;
|
|
160
|
+
/** Login redirect after authoritative initiation; default location.assign. */
|
|
171
161
|
navigate?: (authorizationUrl: string) => void;
|
|
162
|
+
/** Epoch-millisecond clock; default Date.now. Credential deadline arithmetic must remain safe. */
|
|
172
163
|
now?: () => number;
|
|
173
164
|
}
|
|
165
|
+
/** One tab's session. Both transports serialize lifecycle mutations; access JWTs stay in memory. */
|
|
174
166
|
interface OidcVaultDpopSession {
|
|
167
|
+
/** Opaque persistent-key scope for this frontend origin, backend origin and normalized mount. */
|
|
175
168
|
readonly scopeId: string;
|
|
176
169
|
readonly sessionTransport: OidcVaultSessionTransport;
|
|
170
|
+
/** FIFO with exchange/refresh/logout. Clears frontend auth, creates a missing key and navigates after POST initiation. */
|
|
177
171
|
login(returnTo?: string): Promise<void>;
|
|
172
|
+
/** FIFO. Exchanges the one-time callback code using the initiating key and temporary cookie. */
|
|
178
173
|
exchange(code: string): Promise<DpopAccessToken>;
|
|
179
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Same Promise for concurrent callers until login/exchange/logout/clear/resetKey
|
|
176
|
+
* intervenes; the first caller's rejectedToken owns that batch. FIFO with
|
|
177
|
+
* other lifecycle mutations. No Authorization/ath, even with an expired JWT.
|
|
178
|
+
*/
|
|
180
179
|
refresh(options?: {
|
|
181
180
|
rejectedToken?: string;
|
|
182
181
|
}): Promise<DpopAccessToken>;
|
|
182
|
+
/** FIFO: an earlier refresh publishes its rotated handle before logout uses it. Revokes server refresh state. */
|
|
183
183
|
logout(): Promise<void>;
|
|
184
|
+
/** Memory snapshot only; may already be expired, including a valid expiresIn: 0 response. */
|
|
184
185
|
getAccessToken(): DpopAccessToken | undefined;
|
|
185
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* Existing-key read alongside the lifecycle FIFO. A still-current
|
|
188
|
+
* missing/changed key clears auth; superseded reads cannot clear a newer
|
|
189
|
+
* login. Absence of credentials preserves an in-progress login marker.
|
|
190
|
+
*/
|
|
186
191
|
getKey(): Promise<DpopKey>;
|
|
192
|
+
/**
|
|
193
|
+
* Immediately invalidates local work/auth and pending login. Cookie mode
|
|
194
|
+
* resolves after inactive metadata/peer notification under the shared lock,
|
|
195
|
+
* which can wait for other lock owners. Aborts this context's vault HTTP.
|
|
196
|
+
* Keeps the key; no server logout.
|
|
197
|
+
*/
|
|
187
198
|
clear(): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Explicit local key recovery, including corrupt/mismatched persisted keys.
|
|
201
|
+
* Immediately invalidates local auth/pending login and aborts owned vault HTTP;
|
|
202
|
+
* then atomically deletes only this scope's key AND cookie-session metadata,
|
|
203
|
+
* under the shared lock in cookie mode. Repeating on absent records is harmless;
|
|
204
|
+
* concurrent resets share removal until another lifecycle mutation intervenes.
|
|
205
|
+
* Await completion before login() creates a new non-extractable key through
|
|
206
|
+
* atomic first-key-wins. Never creates a key, calls the backend, removes an
|
|
207
|
+
* HttpOnly cookie, or revokes server sessions/JWTs. Other sessions using the old
|
|
208
|
+
* key need fresh login; old server sessions remain until revocation/expiry.
|
|
209
|
+
* Already-started API/crypto work and retained key references cannot be undone.
|
|
210
|
+
* Storage/other lock owners may delay completion. Disposed contexts reject.
|
|
211
|
+
*/
|
|
212
|
+
resetKey(): Promise<void>;
|
|
213
|
+
/**
|
|
214
|
+
* Closes this context and peer waits immediately, preserving committed
|
|
215
|
+
* handles/key/metadata and pending login for reload. Aborts owned vault HTTP;
|
|
216
|
+
* does not log out, cancel caller-owned API requests, or undo server mutations.
|
|
217
|
+
* Await clear()/resetKey() first if their persistence work must finish. Later calls reject.
|
|
218
|
+
*/
|
|
188
219
|
dispose(): void;
|
|
189
220
|
}
|
|
190
221
|
/**
|
|
@@ -193,72 +224,142 @@ interface OidcVaultDpopSession {
|
|
|
193
224
|
* '@web-ts-toolkit/oidc-vault-dpop-client'` (named root import, no default export).
|
|
194
225
|
* Lifecycle: `login` → `exchange(code)` → `getAccessToken`/`fetchWithDpop` →
|
|
195
226
|
* `refresh` → `logout`. Cookie transport additionally needs Web Locks +
|
|
196
|
-
* `BroadcastChannel` at runtime
|
|
197
|
-
*
|
|
227
|
+
* `BroadcastChannel` at runtime, in addition to secure-context Web Crypto,
|
|
228
|
+
* sessionStorage and IndexedDB CryptoKey persistence.
|
|
229
|
+
* Mutating calls are ordered per instance in both transports; cookie mutations
|
|
230
|
+
* also hold the same-origin/scope Web Lock. clear/resetKey/dispose invalidate entry-time
|
|
231
|
+
* authority immediately. Superseded work rejects with non-terminal
|
|
232
|
+
* DPOP_OPERATION_SUPERSEDED (or DPOP_SESSION_DISPOSED after disposal).
|
|
233
|
+
* Vault HTTP has a per-attempt deadline/byte bound and aborts on invalidation.
|
|
234
|
+
* Valid response nonces are learned for this vault/key, including on success.
|
|
235
|
+
* Vault nonce challenges permit one retry with a fresh proof; a second yields
|
|
236
|
+
* non-terminal DPOP_NONCE_RETRY_EXHAUSTED. Only explicit terminal session,
|
|
237
|
+
* browser/device-binding or recognition errors clear auth, not generic 401s.
|
|
238
|
+
* Peer coordination failures are advisory and cannot clear auth. A new login
|
|
239
|
+
* intent supersedes older peer callbacks even while queued; validated current-key
|
|
240
|
+
* failures and explicit terminal operations retain their own failure cleanup.
|
|
241
|
+
* A lost response leaves server commit/cookie application uncertain: local
|
|
242
|
+
* cancellation cannot roll it back or coordinate across frontend origins.
|
|
198
243
|
*/
|
|
199
244
|
declare const createOidcVaultDpopSession: (options: OidcVaultDpopSessionOptions) => OidcVaultDpopSession;
|
|
200
245
|
|
|
201
246
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
247
|
+
* In-memory opaque nonces, isolated by the caller's trusted protection space and
|
|
248
|
+
* signing-key thumbprint. At most 64 tuples: a successful remember renews write
|
|
249
|
+
* order; insertion at capacity evicts the oldest remembered tuple. Reads do not
|
|
250
|
+
* promote entries. No TTL or server-specific nonce decoding is performed.
|
|
206
251
|
*/
|
|
207
252
|
declare class DpopNonceCache {
|
|
208
253
|
private readonly nonces;
|
|
209
254
|
get(space: string, jkt: string): string | undefined;
|
|
255
|
+
/**
|
|
256
|
+
* Accept one 1..512-character visible-ASCII value, excluding whitespace,
|
|
257
|
+
* double quotes, backslashes and commas (including coalesced duplicate header
|
|
258
|
+
* values). Invalid input returns false without overwriting or changing order.
|
|
259
|
+
*/
|
|
210
260
|
remember(space: string, jkt: string, nonce: string | null): boolean;
|
|
261
|
+
/** Forget every tuple in this cache; does not invalidate credentials or server nonce state. */
|
|
211
262
|
clear(): void;
|
|
212
263
|
}
|
|
213
264
|
|
|
214
265
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
266
|
+
* Credential/nonce policy for a public API subtree, selected by `fetchWithDpop`.
|
|
267
|
+
* @example
|
|
268
|
+
* const apis: DpopApi[] = [
|
|
269
|
+
* { origin: 'https://gateway.example.com', pathPrefix: '/orders', replayNamespace: 'orders-api' },
|
|
270
|
+
* { origin: 'https://gateway.example.com', pathPrefix: '/billing', replayNamespace: 'billing-api' },
|
|
271
|
+
* ];
|
|
219
272
|
*/
|
|
220
|
-
|
|
221
273
|
interface DpopApi {
|
|
222
|
-
/**
|
|
274
|
+
/**
|
|
275
|
+
* Exact public scheme/host/port receiving auth, with an optional root slash.
|
|
276
|
+
* HTTPS or loopback HTTP; no path, query, fragment, wildcard or redirects.
|
|
277
|
+
*/
|
|
223
278
|
origin: string;
|
|
224
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* Absolute path subtree. Omission or `/` permits the entire origin. Matches
|
|
281
|
+
* segment boundaries (`/orders` or `/orders/...`, never `/orders-evil`);
|
|
282
|
+
* the longest normalized matching prefix wins, independent of array order.
|
|
283
|
+
* Trailing slashes are removed; unreserved percent escapes and dot segments
|
|
284
|
+
* normalize as in the proof/outbound path. A prefix normalizing to `/` is
|
|
285
|
+
* origin-wide too. Reserved escapes remain encoded.
|
|
286
|
+
* No URL, `//` authority-like prefix, query, fragment, whitespace, backslash,
|
|
287
|
+
* or malformed escape. Duplicate normalized origin/path scopes are rejected,
|
|
288
|
+
* even if their namespace/credential policies are identical.
|
|
289
|
+
*/
|
|
290
|
+
pathPrefix?: string;
|
|
291
|
+
/**
|
|
292
|
+
* Backend API replayNamespace: 1–128 printable ASCII characters. Nonces use
|
|
293
|
+
* the selected `['api', normalizedOrigin, replayNamespace]` plus signing jkt;
|
|
294
|
+
* paths with the same origin/namespace intentionally share that nonce space.
|
|
295
|
+
*/
|
|
225
296
|
replayNamespace: string;
|
|
226
|
-
/**
|
|
297
|
+
/** Default `omit`. Use `include` only for a cookie-using API. Snapshotted before auth awaits. */
|
|
227
298
|
credentials?: 'omit' | 'include';
|
|
228
299
|
}
|
|
229
300
|
interface DpopFetchContext {
|
|
230
301
|
session: OidcVaultDpopSession;
|
|
302
|
+
/** All scopes are validated on each invocation, including entries that do not match its target. */
|
|
231
303
|
apis: readonly DpopApi[];
|
|
232
304
|
fetch?: typeof globalThis.fetch;
|
|
305
|
+
/** Optional shared cache; each instance holds at most 64 protection-space/key tuples. */
|
|
233
306
|
nonces?: DpopNonceCache;
|
|
234
307
|
now?: () => number;
|
|
235
308
|
}
|
|
236
309
|
interface DpopFetchOptions extends Omit<RequestInit, 'redirect' | 'credentials'> {
|
|
310
|
+
/**
|
|
311
|
+
* Abort this caller's wait before/through auth, proof and HTTP work. Pre-aborted
|
|
312
|
+
* calls do no auth work. Shared refresh/key work continues for other waiters;
|
|
313
|
+
* already-started Web Crypto/server mutations cannot be undone.
|
|
314
|
+
*/
|
|
315
|
+
signal?: AbortSignal | null;
|
|
237
316
|
/**
|
|
238
317
|
* GET/HEAD/OPTIONS may retry. All other methods default to one attempt.
|
|
239
318
|
* Explicitly authorize idempotent retries only for a server operation whose
|
|
240
319
|
* contract supports them (e.g. PUT or POST with a server idempotency key).
|
|
320
|
+
* Nonce learning still occurs on single-attempt and success responses, so a
|
|
321
|
+
* later explicit invocation can use the nonce without resubmitting this call.
|
|
241
322
|
*/
|
|
242
323
|
retry?: 'never' | 'idempotent';
|
|
243
324
|
}
|
|
244
325
|
/**
|
|
245
|
-
* Scoped DPoP `fetch` for
|
|
246
|
-
* attempt
|
|
247
|
-
* challenge and at most once via `session.refresh()`
|
|
326
|
+
* Scoped DPoP `fetch` for a configured API origin/path: mints a fresh proof per
|
|
327
|
+
* attempt and sends `Authorization: DPoP`. When the retry policy permits it,
|
|
328
|
+
* retries once on a DPoP nonce challenge and at most once via `session.refresh()`
|
|
329
|
+
* on `invalid_token`. Learns valid response nonces even on single-attempt,
|
|
330
|
+
* success and final challenge responses, isolated by protection space/key.
|
|
331
|
+
* Only an unambiguous HTTP-401 DPoP challenge authorizes recovery; parameter
|
|
332
|
+
* order is flexible, but malformed/duplicate challenges and fields over 8,192
|
|
333
|
+
* characters do not trigger retries. Final API responses belong to the caller.
|
|
248
334
|
* Canonical import: `import { fetchWithDpop } from
|
|
249
|
-
* '@web-ts-toolkit/oidc-vault-dpop-client'` (named root import).
|
|
250
|
-
*
|
|
251
|
-
*
|
|
335
|
+
* '@web-ts-toolkit/oidc-vault-dpop-client'` (named root import). Selects the
|
|
336
|
+
* longest normalized segment-prefix match in `context.apis`; omission/root is
|
|
337
|
+
* origin-wide. Validates all scopes and snapshots the target and selected
|
|
338
|
+
* origin/path/namespace/credentials before auth awaits. A missing match rejects
|
|
339
|
+
* with DPOP_API_OUT_OF_SCOPE before credential/key/refresh/network work. Bad
|
|
340
|
+
* policies or duplicate normalized scopes reject with INVALID_DPOP_API_CONFIG;
|
|
341
|
+
* malformed paths/origins retain INVALID_DPOP_TARGET / INSECURE_DPOP_ORIGIN.
|
|
342
|
+
* All are non-terminal. Authentication headers are owned by the helper.
|
|
343
|
+
* Outbound path normalization matches the proof: unreserved escapes and dot
|
|
344
|
+
* segments normalize, reserved escapes stay encoded, and query/fragment data
|
|
345
|
+
* is preserved for fetch (HTTP omits fragments). Neither enters the proof.
|
|
346
|
+
* The uppercase method is the same on the proof and every outbound attempt.
|
|
347
|
+
* A nonlive deadline (including an expiresIn: 0 refresh result) stops the
|
|
348
|
+
* attempt with DPOP_ACCESS_TOKEN_EXPIRED, requiresLogin: false. The rotated
|
|
349
|
+
* session handle is retained; no additional refresh loop is started.
|
|
350
|
+
* Caller cancellation rejects with signal.reason, including while waiting on
|
|
351
|
+
* shared refresh/key work, without cancelling that shared work. Discarded retry
|
|
352
|
+
* responses are cancelled best effort; cleanup never delays or replaces a result.
|
|
252
353
|
*/
|
|
253
354
|
declare const fetchWithDpop: (context: DpopFetchContext, input: string | URL, options?: DpopFetchOptions) => Promise<Response>;
|
|
254
355
|
|
|
255
356
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
357
|
+
* Built-in failures use fixed codes/messages, never server text or credentials.
|
|
358
|
+
* requiresLogin identifies terminal session/binding/recognition or local key
|
|
359
|
+
* failures, not every HTTP 401. Non-terminal errors retain auth and do not by
|
|
360
|
+
* themselves authorize an automatic retry. Unknown vault codes are sanitized to
|
|
361
|
+
* OIDC_REQUEST_FAILED with requiresLogin: false; status remains available.
|
|
260
362
|
*/
|
|
261
|
-
/** Fixed local failures; never include keys, credentials, fingerprints or URLs. */
|
|
262
363
|
declare class OidcVaultDpopClientError extends Error {
|
|
263
364
|
readonly code: string;
|
|
264
365
|
readonly requiresLogin: boolean;
|