@proof-holdings/delegation-verifier 0.1.0 → 0.2.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 +90 -28
- package/dist/delegate.d.ts +0 -7
- package/dist/delegate.d.ts.map +1 -1
- package/dist/delegate.js +3 -30
- package/dist/delegate.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/jwks.d.ts +115 -14
- package/dist/jwks.d.ts.map +1 -1
- package/dist/jwks.js +325 -102
- package/dist/jwks.js.map +1 -1
- package/dist/keyset.d.ts +155 -0
- package/dist/keyset.d.ts.map +1 -0
- package/dist/keyset.js +505 -0
- package/dist/keyset.js.map +1 -0
- package/dist/status.d.ts +3 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +33 -26
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +79 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +68 -50
- package/dist/verify.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# @proof-holdings/delegation-verifier
|
|
2
2
|
|
|
3
3
|
Verify a **Proof of Delegation**: a signed statement that the controller of a domain authorized
|
|
4
|
-
a specific agent artifact — an npm package, a container, an endpoint
|
|
5
|
-
scopes.
|
|
4
|
+
a specific agent artifact — an npm package, a container, an endpoint.
|
|
6
5
|
|
|
7
6
|
You do not need an account with anyone to use this. It runs against public surfaces only, and
|
|
8
7
|
it has **no built-in trust list**: you name the issuers you accept.
|
|
@@ -18,8 +17,8 @@ Zero runtime dependencies. Node ≥ 18 (or any runtime with WebCrypto, `fetch` a
|
|
|
18
17
|
|
|
19
18
|
A verified delegation says exactly one thing:
|
|
20
19
|
|
|
21
|
-
> The controller of `principal` authorized `delegate
|
|
22
|
-
>
|
|
20
|
+
> The controller of `principal` authorized `delegate`, and the issuer checked that control when
|
|
21
|
+
> it signed.
|
|
23
22
|
|
|
24
23
|
It is **not** a statement that the artifact is safe, audited, maintained, or endorsed, and it is
|
|
25
24
|
not a review of the business behind the domain. Do not render it as a generic "verified" badge.
|
|
@@ -41,7 +40,6 @@ const result = await verifyPublishedDelegation(serverCard, {
|
|
|
41
40
|
// The artifact YOU resolved — the package you are installing, the endpoint you are calling.
|
|
42
41
|
delegate: { type: 'purl', value: 'pkg:npm/postmark-mcp' },
|
|
43
42
|
expectedPrincipal: 'postmarkapp.com',
|
|
44
|
-
requiredScopes: ['send-email'],
|
|
45
43
|
});
|
|
46
44
|
|
|
47
45
|
if (result.valid) {
|
|
@@ -85,21 +83,29 @@ presented as *"X says it authorized this"*, never as *verified*.
|
|
|
85
83
|
1. **Trust policy** — the token's `iss` must be one you named. No trust list, no verification.
|
|
86
84
|
2. **Signature** — RS256/ES256 against the issuer's JWKS. The algorithm is chosen by the
|
|
87
85
|
verifier, never read from the token, so `alg: none` and HMAC forgeries die before any key is
|
|
88
|
-
fetched. Key sets are cached; an unknown `kid` triggers at most one refetch per 30s.
|
|
86
|
+
fetched. Key sets are cached; an unknown `kid` triggers at most one refetch per 30s. The
|
|
87
|
+
signing key's purpose, window and tombstone are checked here too — see
|
|
88
|
+
[Key rotation](#key-rotation-and-the-key-set-cache).
|
|
89
89
|
3. **Claims** — `token_type`, schema version, `exp`/`iat` (5s tolerance by default), and the
|
|
90
90
|
required claim set.
|
|
91
91
|
4. **Principal** — pinned to the domain you expect, when you supply one. See above: without it
|
|
92
92
|
the answer says who *claims* the artifact, not that the right party did.
|
|
93
93
|
5. **Delegate** — canonicalized and compared byte-for-byte with the identity you resolved.
|
|
94
|
-
6. **Status** — two channels, both authenticated:
|
|
94
|
+
6. **Status** — two channels, both authenticated, asked in order:
|
|
95
95
|
- the IETF Token Status List the token points at (`status.status_list`), whose uri is
|
|
96
96
|
refused unless it belongs to your trusted issuer, and whose signature is checked against
|
|
97
|
-
the same key set
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
the same key set. A readable `valid`, `revoked` or `suspended` slot is the answer, and the
|
|
98
|
+
check stops there. An issuer whose validity cascades folds a revoked or suspended domain
|
|
99
|
+
control proof beneath the delegation into this same slot (proof.holdings does);
|
|
100
|
+
- the issuer's status endpoint, asked only when the list gives no answer: the token carries
|
|
101
|
+
no status-list claim, or the list cannot be read, is not the trusted issuer's, or holds no
|
|
102
|
+
valid/revoked/suspended value for the token's slot. One thing
|
|
103
|
+
only the endpoint can report is an **expiry** inherited from the domain control proof, which
|
|
104
|
+
a list slot never encodes. A delegation whose own slot and own `exp` both read fine is
|
|
105
|
+
therefore not re-checked for it — an accepted residual, explained in
|
|
106
|
+
[Why the status endpoint is not always called](#why-the-status-endpoint-is-not-always-called-and-what-that-leaves-uncovered).
|
|
107
|
+
|
|
108
|
+
Status checking is **fail-closed**: if neither channel gives an answer, the result is
|
|
103
109
|
`status_unavailable`, not "valid". Pass `statusCheck: 'skip'` to check signature and claims
|
|
104
110
|
only — the result then carries `statusChecked: false`, which means *revocation was not
|
|
105
111
|
checked*, never *not revoked*.
|
|
@@ -121,6 +127,56 @@ than before.
|
|
|
121
127
|
`POST <statusEndpoint>` with `{"proof_token": "<jwt>"}` answering JSON with at least
|
|
122
128
|
`{"valid": boolean}` and optionally `{"reason": "revoked" | "suspended" | ...}`.
|
|
123
129
|
|
|
130
|
+
## Key rotation and the key-set cache
|
|
131
|
+
|
|
132
|
+
An issuer's JWKS may carry several keys at once. Beside the standard members, a key may declare
|
|
133
|
+
`proof_purpose` (`proof`, `delegation` or `status-list`), a window (`not_before`, `not_after`,
|
|
134
|
+
`max_token_lifetime` — integer seconds) or `proof_legacy: true`; revoked keys are listed in a
|
|
135
|
+
top-level `proof_tombstones: [{ "kid", "revoked_at" }]`. A key set without these members verifies
|
|
136
|
+
exactly as before. Once any key carries `proof_purpose`:
|
|
137
|
+
|
|
138
|
+
- a delegation must be signed by a `delegation` key or a legacy one (`wrong_key_purpose`);
|
|
139
|
+
- its `iat` must lie in the key's window, ± 5 minutes (`outside_key_window`), and its `exp` no later
|
|
140
|
+
than `iat + max_token_lifetime` (`token_lifetime_exceeded`) — legacy keys skip both;
|
|
141
|
+
- a token without `kid` is tried against legacy keys only.
|
|
142
|
+
|
|
143
|
+
The status list's own signing key is held to the same rules as a `status-list` key; a list signed by
|
|
144
|
+
a key that fails them counts as unreadable, exactly like a list that could not be fetched.
|
|
145
|
+
|
|
146
|
+
**One response is never trusted for long.** A new key verifies from its first sighting, so an
|
|
147
|
+
emergency key works at once. But the verifier only *remembers* a key carrying `proof_purpose` or
|
|
148
|
+
`proof_legacy` — keeps it after it disappears from the JWKS, refuses new key material under its
|
|
149
|
+
`kid`, refuses whole a document that widens its window — once two fetches at least 5 minutes apart
|
|
150
|
+
served it unchanged; until then the next document replaces or drops it. A narrower window and a
|
|
151
|
+
tombstone wait for the same confirmation: a tombstone applies only when a fetch at least 5 minutes
|
|
152
|
+
after its first sighting still carries it (a fetch in between that drops it starts over;
|
|
153
|
+
`refreshJwks` forces the second fetch), and once applied it stays until `revoked_at` + 365 days or
|
|
154
|
+
`clearTombstones()`, whatever later documents say.
|
|
155
|
+
|
|
156
|
+
The key set is refetched every 10 minutes. When a refetch fails, the cache's confirmed keys keep
|
|
157
|
+
answering until it is `maxJwksAgeMs` old, then the answer is `jwks_stale`; an applied tombstone
|
|
158
|
+
answers `key_revoked` at any age. A failed refetch of a cached set is retried at most once per 30s.
|
|
159
|
+
The cache lives in the process, keyed by `jwksUri`.
|
|
160
|
+
|
|
161
|
+
| Option | Default | Meaning |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `maxJwksAgeMs` | 24 h | How old the cache may be when a refetch fails. Above 25 days, non-integer or ≤ 0 is `invalid_options`. |
|
|
164
|
+
| `keySetStore` | none | `{ load(jwksUri), save(jwksUri, state) }`, sync or async, opaque string state. Persists the key set — tombstones included — across restarts and cold starts. A failing store never fails a verification. |
|
|
165
|
+
| `onJwksEvent` | ignored | Receives `{ type, jwksUri, kid?, message }`: `tombstone_pending`, `tombstone_applied`, `tombstone_for_live_key`, `window_narrowing_pending`, `window_narrowing_applied`, `window_widening_rejected`, `key_material_changed`, `kid_vanished`, `store_error`. |
|
|
166
|
+
| `timeoutMs` | 5000 | Bounds each network request and each `keySetStore` call. |
|
|
167
|
+
|
|
168
|
+
Exported alongside `verifyDelegation`:
|
|
169
|
+
|
|
170
|
+
- `refreshJwks({ jwksUri, fetch?, timeoutMs?, keySetStore?, onJwksEvent? })` — refetch now,
|
|
171
|
+
ignoring the TTL and cooldowns; resolves `{ ok: true }` or `{ ok: false, message }`. The method a
|
|
172
|
+
`jwks_stale` result's `refreshHint` names.
|
|
173
|
+
- `clearTombstones({ jwksUri?, keySetStore? })` — the manual reset: forgets applied and pending
|
|
174
|
+
tombstones for one key set (and its persisted copy), or for every set in memory.
|
|
175
|
+
- `getJwksDiagnostics(jwksUri)` — cache age, whether the registry is live, vanished keys, pending and
|
|
176
|
+
applied tombstones; `null` for a key set this process has never used.
|
|
177
|
+
- `DEFAULT_MAX_JWKS_AGE_MS`, `MAX_JWKS_AGE_CAP_MS` — to validate your own configuration against the
|
|
178
|
+
same bounds.
|
|
179
|
+
|
|
124
180
|
## Failure reasons
|
|
125
181
|
|
|
126
182
|
Every failure carries an `outcome: 'invalid' | 'unconfirmed'` beside `reason` —
|
|
@@ -132,18 +188,22 @@ because they occur in practice).
|
|
|
132
188
|
|
|
133
189
|
| `reason` | `outcome` | Meaning |
|
|
134
190
|
| --- | --- | --- |
|
|
135
|
-
| `invalid_options` | `invalid` | The call is wrong — usually a missing or uncanonicalizable `delegate
|
|
191
|
+
| `invalid_options` | `invalid` | The call is wrong — usually a missing or uncanonicalizable `delegate`, or the `requiredScopes` option, removed in 0.2.0 with the `scope` claim (refused rather than ignored, so no caller reads a valid answer as "my scopes were granted"). |
|
|
136
192
|
| `no_trust_policy` | `invalid` | `trustedIssuers` was empty. There is no default. |
|
|
137
193
|
| `malformed_token` | `invalid` | Not a decodable JWT. |
|
|
138
194
|
| `untrusted_issuer` | `invalid` | Signed by someone you did not name. |
|
|
139
195
|
| `unsupported_algorithm` | `invalid` | The token asked for an algorithm this verifier does not accept. |
|
|
140
196
|
| `unknown_key` / `bad_signature` | `invalid` | No matching key, or the signature does not hold. |
|
|
141
|
-
| `jwks_unavailable` | `unconfirmed` | Could not read the key set — "I could not check", not "it is bad". |
|
|
142
|
-
| `
|
|
197
|
+
| `jwks_unavailable` | `unconfirmed` | Could not read the key set, or could not refetch it for an unknown `kid` — "I could not check", not "it is bad". |
|
|
198
|
+
| `jwks_stale` | `unconfirmed` | A refetch failed and the cached key set is older than `maxJwksAgeMs`. Carries `cacheAgeSeconds` (null when unknown) and `refreshHint: { jwksUri, method: 'refreshJwks' }`. |
|
|
199
|
+
| `key_revoked` | `invalid` | The issuer revoked the signing key (an applied JWKS tombstone). Carries `pullHint: { issuerOrigin, handle }` — where to ask for a re-signed token. The handle is read from a token whose signature was not checked: an address to ask, not a verified identity. |
|
|
200
|
+
| `wrong_key_purpose` | `invalid` | The signing key belongs to another key family than a delegation. |
|
|
201
|
+
| `outside_key_window` | `invalid` | The token's `iat` lies outside the signing key's window (5 minutes of skew allowed). |
|
|
202
|
+
| `token_lifetime_exceeded` | `invalid` | `exp` lies beyond `iat` plus the signing key's `max_token_lifetime`. |
|
|
203
|
+
| `not_a_delegation` / `unsupported_version` | `invalid` | Wrong token type, or a `delegation_schema_version` major other than 2 (a 1.x token, which still carries `scope`, is refused before any network call). |
|
|
143
204
|
| `expired` / `not_yet_valid` / `malformed_claims` | `invalid` | Claim-level rejections. |
|
|
144
205
|
| `principal_mismatch` | `invalid` | Issued by a different domain than you expected. |
|
|
145
206
|
| `delegate_mismatch` | `invalid` | **Attests a different artifact than the one you resolved.** |
|
|
146
|
-
| `scope_not_granted` | `invalid` | Does not grant a scope you required. |
|
|
147
207
|
| `status_uri_untrusted` | `unconfirmed` | The status-list uri in the token is not the issuer's. Not fetched. |
|
|
148
208
|
| `status_unavailable` | `unconfirmed` | Revocation could not be checked. Fail closed. |
|
|
149
209
|
| `revoked` / `suspended` | `invalid` | Withdrawn, or reversibly paused. |
|
|
@@ -160,17 +220,19 @@ that token's validity in it. An issuer that keeps a side-channel revocation reas
|
|
|
160
220
|
published list is not honoring the channel it chose to publish — a fact about that issuer, not
|
|
161
221
|
something a generic verifier can special-case.
|
|
162
222
|
|
|
163
|
-
The endpoint
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
223
|
+
The endpoint is asked only when the list gives no answer: the token carries no status-list claim,
|
|
224
|
+
or the list cannot be read, is not the trusted issuer's, or holds no valid/revoked/suspended value
|
|
225
|
+
for the token's slot. It is the only channel that can report two things the bit structurally
|
|
226
|
+
cannot — `unknown_delegation` ("no record of this handle" is distinguishable from "a record that
|
|
227
|
+
is dead" only by a live call) and, for issuers whose validity cascades through something else, like
|
|
228
|
+
a domain control proof underneath a delegation, an EXPIRY driven by that something else, which a
|
|
229
|
+
status-list bit never encodes (only revoked/suspended are, by the same draft's own vocabulary) —
|
|
230
|
+
so when the list does answer, neither is checked. For proof.holdings specifically, that
|
|
231
|
+
cascade-expiry gap is bounded, not eliminated: a delegation can no longer be minted longer than its
|
|
232
|
+
underlying control proof, and a control cannot be re-issued shorter than an outstanding delegation
|
|
233
|
+
without capping the reissue to cover it — but a control shortened before that guarantee existed, or a
|
|
234
|
+
reissue that hit its own rare fail-open path, can still leave a delegation whose list bit and own
|
|
235
|
+
`exp` claim both read fine while its issuer would answer `expired`. This is a known, accepted trade-off — not
|
|
174
236
|
silently assumed away.
|
|
175
237
|
|
|
176
238
|
### One thing a failure reason is not
|
package/dist/delegate.d.ts
CHANGED
|
@@ -15,8 +15,6 @@ import type { DelegateInput, DelegateType } from './types.js';
|
|
|
15
15
|
export declare const DELEGATE_TYPES: readonly DelegateType[];
|
|
16
16
|
/** Cap on the CANONICAL delegate string. */
|
|
17
17
|
export declare const DELEGATE_MAX_LENGTH = 512;
|
|
18
|
-
export declare const DELEGATION_SCOPE_MAX_ENTRIES = 32;
|
|
19
|
-
export declare const DELEGATION_SCOPE_TOKEN_MAX_LENGTH = 64;
|
|
20
18
|
export declare class DelegateIdentifierError extends Error {
|
|
21
19
|
constructor(message: string);
|
|
22
20
|
}
|
|
@@ -26,9 +24,4 @@ export declare class DelegateIdentifierError extends Error {
|
|
|
26
24
|
* cannot be canonicalized is a bug in the CALL, not evidence about the token.
|
|
27
25
|
*/
|
|
28
26
|
export declare function canonicalizeDelegate(input: DelegateInput): string;
|
|
29
|
-
/**
|
|
30
|
-
* Validate and canonicalize a scope list: every entry a lowercase kebab-case token,
|
|
31
|
-
* deduplicated, sorted. Throws `DelegateIdentifierError` on any rejection.
|
|
32
|
-
*/
|
|
33
|
-
export declare function canonicalizeScopes(scopes: string[]): string[];
|
|
34
27
|
//# sourceMappingURL=delegate.d.ts.map
|
package/dist/delegate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA6B,CAAC;AAEhF,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA6B,CAAC;AAEhF,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAKvC,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAIjE"}
|
package/dist/delegate.js
CHANGED
|
@@ -14,10 +14,6 @@
|
|
|
14
14
|
export const DELEGATE_TYPES = ['url', 'purl'];
|
|
15
15
|
/** Cap on the CANONICAL delegate string. */
|
|
16
16
|
export const DELEGATE_MAX_LENGTH = 512;
|
|
17
|
-
export const DELEGATION_SCOPE_MAX_ENTRIES = 32;
|
|
18
|
-
export const DELEGATION_SCOPE_TOKEN_MAX_LENGTH = 64;
|
|
19
|
-
/** Lowercase kebab-case: `send-email`, `read-profile-v2`. */
|
|
20
|
-
const SCOPE_TOKEN_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
21
17
|
/** `pkg:type/[namespace/]name` — structure only; per-type case rules applied after. */
|
|
22
18
|
const PURL_RE = /^pkg:([a-z0-9.+-]+)\/(.+)$/i;
|
|
23
19
|
export class DelegateIdentifierError extends Error {
|
|
@@ -116,10 +112,11 @@ function canonicalizeUrl(value) {
|
|
|
116
112
|
throw new DelegateIdentifierError('delegate URL must not carry a fragment');
|
|
117
113
|
}
|
|
118
114
|
// URL() already lowercases the host and strips an explicit default port; pathname keeps its
|
|
119
|
-
// case. Trailing slash is canonicalized away except at the root, where it is always '/'
|
|
115
|
+
// case. Trailing slash is canonicalized away except at the root, where it is always '/' — a
|
|
116
|
+
// path of slashes only collapses to that root, so the result is idempotent.
|
|
120
117
|
let pathname = parsed.pathname;
|
|
121
118
|
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
122
|
-
pathname = pathname.replace(/\/+$/, '');
|
|
119
|
+
pathname = pathname.replace(/\/+$/, '') || '/';
|
|
123
120
|
}
|
|
124
121
|
return enforceLength(`https://${parsed.host}${pathname}`);
|
|
125
122
|
}
|
|
@@ -129,28 +126,4 @@ function enforceLength(canonical) {
|
|
|
129
126
|
}
|
|
130
127
|
return canonical;
|
|
131
128
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Validate and canonicalize a scope list: every entry a lowercase kebab-case token,
|
|
134
|
-
* deduplicated, sorted. Throws `DelegateIdentifierError` on any rejection.
|
|
135
|
-
*/
|
|
136
|
-
export function canonicalizeScopes(scopes) {
|
|
137
|
-
if (!Array.isArray(scopes) || scopes.length === 0) {
|
|
138
|
-
throw new DelegateIdentifierError('scope must contain at least one entry');
|
|
139
|
-
}
|
|
140
|
-
if (scopes.length > DELEGATION_SCOPE_MAX_ENTRIES) {
|
|
141
|
-
throw new DelegateIdentifierError(`too many scope entries (max ${DELEGATION_SCOPE_MAX_ENTRIES})`);
|
|
142
|
-
}
|
|
143
|
-
for (const token of scopes) {
|
|
144
|
-
if (typeof token !== 'string' || token.length === 0) {
|
|
145
|
-
throw new DelegateIdentifierError('scope entries must be non-empty strings');
|
|
146
|
-
}
|
|
147
|
-
if (token.length > DELEGATION_SCOPE_TOKEN_MAX_LENGTH) {
|
|
148
|
-
throw new DelegateIdentifierError(`scope entry is too long (max ${DELEGATION_SCOPE_TOKEN_MAX_LENGTH} characters)`);
|
|
149
|
-
}
|
|
150
|
-
if (!SCOPE_TOKEN_RE.test(token)) {
|
|
151
|
-
throw new DelegateIdentifierError(`invalid scope entry "${token}" — lowercase kebab-case tokens only (e.g. send-email)`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return [...new Set(scopes)].sort();
|
|
155
|
-
}
|
|
156
129
|
//# sourceMappingURL=delegate.js.map
|
package/dist/delegate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.js","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,KAAK,EAAE,MAAM,CAAU,CAAC;AAEhF,4CAA4C;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,
|
|
1
|
+
{"version":3,"file":"delegate.js","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @proof-holdings/delegation-verifier — issuer-agnostic verification of Proof of Delegation.
|
|
3
3
|
*
|
|
4
|
-
* A delegation says: the domain in `principal` authorized the artifact in `delegate
|
|
5
|
-
*
|
|
4
|
+
* A delegation says: the domain in `principal` authorized the artifact in `delegate`, and the
|
|
5
|
+
* issuer checked that domain's control at issuance. It says nothing
|
|
6
6
|
* about whether the artifact is safe, audited, or any good.
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
@@ -28,10 +28,10 @@ export type { FoundPublication } from './publication.js';
|
|
|
28
28
|
*/
|
|
29
29
|
export { parseDelegationPointer, pointerRecordName, POINTER_VERSION, POINTER_RECORD_PREFIX, } from './publication.js';
|
|
30
30
|
export type { PointerParseResult, PointerRejection } from './publication.js';
|
|
31
|
-
export { canonicalizeDelegate,
|
|
32
|
-
export { resetJwksCache, SUPPORTED_ALGORITHMS } from './jwks.js';
|
|
33
|
-
export type { SupportedAlgorithm } from './jwks.js';
|
|
31
|
+
export { canonicalizeDelegate, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
|
|
32
|
+
export { clearTombstones, DEFAULT_MAX_JWKS_AGE_MS, getJwksDiagnostics, MAX_JWKS_AGE_CAP_MS, refreshJwks, resetJwksCache, SUPPORTED_ALGORITHMS, } from './jwks.js';
|
|
33
|
+
export type { JwksDiagnostics, SupportedAlgorithm } from './jwks.js';
|
|
34
34
|
export { readStatusSlot } from './status.js';
|
|
35
35
|
export { classifyFailureReason } from './types.js';
|
|
36
|
-
export type { DelegateInput, DelegateType, FailureOutcome, FailureReason, FetchLike, TrustedIssuer, VerificationFailure, VerificationResult, VerificationSuccess, VerifiedDelegation, VerifyOptions, } from './types.js';
|
|
36
|
+
export type { DelegateInput, DelegateType, FailureOutcome, FailureReason, FetchLike, JwksEvent, JwksEventType, KeySetStore, TrustedIssuer, VerificationFailure, VerificationResult, VerificationSuccess, VerifiedDelegation, VerifyOptions, } from './types.js';
|
|
37
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @proof-holdings/delegation-verifier — issuer-agnostic verification of Proof of Delegation.
|
|
3
3
|
*
|
|
4
|
-
* A delegation says: the domain in `principal` authorized the artifact in `delegate
|
|
5
|
-
*
|
|
4
|
+
* A delegation says: the domain in `principal` authorized the artifact in `delegate`, and the
|
|
5
|
+
* issuer checked that domain's control at issuance. It says nothing
|
|
6
6
|
* about whether the artifact is safe, audited, or any good.
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
@@ -26,8 +26,8 @@ export { verifyPublishedDelegation, extractDelegationPublications, DELEGATION_ME
|
|
|
26
26
|
* and hands the TXT values in.
|
|
27
27
|
*/
|
|
28
28
|
export { parseDelegationPointer, pointerRecordName, POINTER_VERSION, POINTER_RECORD_PREFIX, } from './publication.js';
|
|
29
|
-
export { canonicalizeDelegate,
|
|
30
|
-
export { resetJwksCache, SUPPORTED_ALGORITHMS } from './jwks.js';
|
|
29
|
+
export { canonicalizeDelegate, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
|
|
30
|
+
export { clearTombstones, DEFAULT_MAX_JWKS_AGE_MS, getJwksDiagnostics, MAX_JWKS_AGE_CAP_MS, refreshJwks, resetJwksCache, SUPPORTED_ALGORITHMS, } from './jwks.js';
|
|
31
31
|
export { readStatusSlot } from './status.js';
|
|
32
32
|
export { classifyFailureReason } from './types.js';
|
|
33
33
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/jwks.d.ts
CHANGED
|
@@ -1,47 +1,148 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JWKS retrieval and key cache.
|
|
2
|
+
* JWKS retrieval and key cache — the I/O half. The rules a fetched key set is held to (merge,
|
|
3
|
+
* tombstones, purpose, window) live in `keyset.ts`; this file decides WHEN to fetch and what to
|
|
4
|
+
* answer when a fetch fails.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
* verifier that shipped before this package:
|
|
6
|
+
* Behaviours that are load-bearing rather than tidy, the first three learned from the hand-rolled
|
|
7
|
+
* Go verifier that shipped before this package:
|
|
6
8
|
*
|
|
7
9
|
* - The algorithm is chosen from THIS list, never from the token's own header. A token that
|
|
8
10
|
* asks for `none` or for an HMAC is refused before any key is looked up.
|
|
9
|
-
* - An unknown `kid` is indistinguishable from a rotated key, so a miss triggers
|
|
10
|
-
* refetch per cooldown window. Without it every junk-kid token costs one outbound
|
|
11
|
-
* and a busy verifier trips the issuer's rate limit — failing its GOOD tokens too.
|
|
11
|
+
* - An unknown `kid` is indistinguishable from a rotated (or emergency) key, so a miss triggers
|
|
12
|
+
* at most one refetch per cooldown window. Without it every junk-kid token costs one outbound
|
|
13
|
+
* request and a busy verifier trips the issuer's rate limit — failing its GOOD tokens too.
|
|
12
14
|
* - Concurrent misses share ONE in-flight fetch per key set, with the cache re-checked after
|
|
13
15
|
* it resolves. Serialising is what makes the cooldown hold under concurrency.
|
|
16
|
+
* - A failed refetch does not discard a cached key set. Under `maxJwksAgeMs` its CONFIRMED keys
|
|
17
|
+
* keep answering (a kid they do not cover is `jwks_unavailable`: it may be new); over it the answer
|
|
18
|
+
* is `jwks_stale` — except for an applied tombstone, which is `key_revoked` at any age. A set
|
|
19
|
+
* that was never fetched has no cache to fall back on, so its failure stays `jwks_unavailable`
|
|
20
|
+
* and is retried on every call.
|
|
21
|
+
* - Age is measured on a monotonic clock in-process. Only what is persisted uses wall clock.
|
|
14
22
|
*/
|
|
15
|
-
import type {
|
|
23
|
+
import type { KeyFamily, KeyPolicyViolation, KeyRecord } from './keyset.js';
|
|
24
|
+
import type { FetchLike, JwksEvent, KeySetStore } from './types.js';
|
|
16
25
|
export declare const SUPPORTED_ALGORITHMS: readonly ["RS256", "ES256"];
|
|
17
26
|
export type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];
|
|
18
|
-
/** How long a `kid` miss
|
|
27
|
+
/** How long a `kid` miss — or a failed refetch of a cached set — suppresses further fetches. */
|
|
19
28
|
export declare const KEY_MISS_COOLDOWN_MS = 30000;
|
|
20
29
|
/** How long a successfully fetched key set is reused without refetching. */
|
|
21
30
|
export declare const KEY_SET_TTL_MS: number;
|
|
31
|
+
/** Default `maxJwksAgeMs`. */
|
|
32
|
+
export declare const DEFAULT_MAX_JWKS_AGE_MS: number;
|
|
33
|
+
/** Largest accepted `maxJwksAgeMs` — below the issuer's ~30-day lead on its next key. */
|
|
34
|
+
export declare const MAX_JWKS_AGE_CAP_MS: number;
|
|
22
35
|
/**
|
|
23
36
|
* A key set is kilobytes. Anything larger is not a JWKS, and the request is driven by input the
|
|
24
37
|
* verifier does not control, so it is capped rather than streamed to completion.
|
|
25
38
|
*/
|
|
26
39
|
export declare const MAX_REMOTE_BODY_BYTES: number;
|
|
27
|
-
|
|
40
|
+
export interface JwksClock {
|
|
41
|
+
monotonicMs(): number;
|
|
42
|
+
wallMs(): number;
|
|
43
|
+
}
|
|
44
|
+
/** Test seam — replaces both clocks; `null` restores the system clocks. Not exported publicly. */
|
|
45
|
+
export declare function setJwksClockForTests(replacement: JwksClock | null): void;
|
|
46
|
+
/** The per-call knobs that shape key-set handling. */
|
|
47
|
+
export interface KeySetOptions {
|
|
48
|
+
maxJwksAgeMs: number;
|
|
49
|
+
keySetStore?: KeySetStore;
|
|
50
|
+
onJwksEvent?: (event: JwksEvent) => void;
|
|
51
|
+
}
|
|
52
|
+
/** Test seam — drops every cached key set, tombstones included. */
|
|
28
53
|
export declare function resetJwksCache(): void;
|
|
29
54
|
export declare class JwksUnavailableError extends Error {
|
|
30
55
|
constructor(message: string);
|
|
31
56
|
}
|
|
32
57
|
export declare function isSupportedAlgorithm(alg: unknown): alg is SupportedAlgorithm;
|
|
58
|
+
export interface KeyCandidate {
|
|
59
|
+
key: CryptoKey;
|
|
60
|
+
record: KeyRecord;
|
|
61
|
+
}
|
|
62
|
+
export type KeyResolution = {
|
|
63
|
+
kind: 'keys';
|
|
64
|
+
candidates: KeyCandidate[];
|
|
65
|
+
registryLive: boolean;
|
|
66
|
+
} | {
|
|
67
|
+
kind: 'revoked';
|
|
68
|
+
kid: string;
|
|
69
|
+
} | {
|
|
70
|
+
kind: 'wrong_purpose';
|
|
71
|
+
kid: string;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'unknown';
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'stale';
|
|
76
|
+
cacheAgeSeconds: number | null;
|
|
77
|
+
} | {
|
|
78
|
+
kind: 'unavailable';
|
|
79
|
+
message: string;
|
|
80
|
+
};
|
|
33
81
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* when the key set could not be read at all ("I could not check" is not "the token is bad").
|
|
82
|
+
* The keys that may verify a token of `family` carrying `kid` — or why none may. Fetches the key
|
|
83
|
+
* set when it is older than the TTL, or once per cooldown when `kid` is unknown.
|
|
37
84
|
*/
|
|
38
|
-
export declare function
|
|
85
|
+
export declare function resolveVerificationKeys(params: {
|
|
39
86
|
jwksUri: string;
|
|
40
87
|
kid?: string;
|
|
41
88
|
alg: SupportedAlgorithm;
|
|
89
|
+
family: KeyFamily;
|
|
42
90
|
fetch: FetchLike;
|
|
43
91
|
timeoutMs: number;
|
|
44
|
-
}): Promise<
|
|
92
|
+
} & KeySetOptions): Promise<KeyResolution>;
|
|
45
93
|
/** Verify a JWS signature against one candidate key. */
|
|
46
94
|
export declare function verifySignature(key: CryptoKey, alg: SupportedAlgorithm, signingInput: string, signature: Uint8Array): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Signature first, then the window and lifetime of the key that verified it — so a forged token
|
|
97
|
+
* is reported as `bad_signature`, not as a policy violation of a key it never came from.
|
|
98
|
+
*/
|
|
99
|
+
export declare function verifyWithCandidates(resolution: Extract<KeyResolution, {
|
|
100
|
+
kind: 'keys';
|
|
101
|
+
}>, alg: SupportedAlgorithm, signingInput: string, signature: Uint8Array, token: {
|
|
102
|
+
iat: unknown;
|
|
103
|
+
exp: unknown;
|
|
104
|
+
}): Promise<'ok' | 'bad_signature' | KeyPolicyViolation>;
|
|
105
|
+
/**
|
|
106
|
+
* Refetch a key set now, ignoring the TTL and cooldowns — the method a `jwks_stale` answer names.
|
|
107
|
+
* Resolves with the outcome rather than throwing.
|
|
108
|
+
*/
|
|
109
|
+
export declare function refreshJwks(params: {
|
|
110
|
+
jwksUri: string;
|
|
111
|
+
fetch?: FetchLike;
|
|
112
|
+
timeoutMs?: number;
|
|
113
|
+
keySetStore?: KeySetStore;
|
|
114
|
+
onJwksEvent?: (event: JwksEvent) => void;
|
|
115
|
+
}): Promise<{
|
|
116
|
+
ok: true;
|
|
117
|
+
} | {
|
|
118
|
+
ok: false;
|
|
119
|
+
message: string;
|
|
120
|
+
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Forget every applied and pending tombstone — the documented manual reset. With `jwksUri`, only
|
|
123
|
+
* that key set (and, given a `keySetStore`, its persisted copy); without, every set in memory.
|
|
124
|
+
*/
|
|
125
|
+
export declare function clearTombstones(params?: {
|
|
126
|
+
jwksUri?: string;
|
|
127
|
+
keySetStore?: KeySetStore;
|
|
128
|
+
}): Promise<void>;
|
|
129
|
+
export interface JwksDiagnostics {
|
|
130
|
+
jwksUri: string;
|
|
131
|
+
/** Age of the cached set in seconds; null when never fetched or the persisted age is unknown. */
|
|
132
|
+
cacheAgeSeconds: number | null;
|
|
133
|
+
registryLive: boolean;
|
|
134
|
+
/** Confirmed registry kids no longer served before their retention ended — kept with last-known state. */
|
|
135
|
+
vanishedKids: string[];
|
|
136
|
+
pendingTombstones: Array<{
|
|
137
|
+
kid: string;
|
|
138
|
+
revokedAt: number;
|
|
139
|
+
pendingForSeconds: number;
|
|
140
|
+
}>;
|
|
141
|
+
appliedTombstones: Array<{
|
|
142
|
+
kid: string;
|
|
143
|
+
revokedAt: number;
|
|
144
|
+
}>;
|
|
145
|
+
}
|
|
146
|
+
/** What the verifier currently holds for one key set, or null if it has never been used. */
|
|
147
|
+
export declare function getJwksDiagnostics(jwksUri: string): JwksDiagnostics | null;
|
|
47
148
|
//# sourceMappingURL=jwks.d.ts.map
|
package/dist/jwks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../src/jwks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../src/jwks.ts"],"names":[],"mappings":"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"}
|