@registrystack/client 0.30.0 → 0.32.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 +8 -0
- package/breg/client.d.ts +98 -0
- package/breg/client.js +77 -1
- package/breg/index.d.ts +34 -0
- package/casework/client.d.ts +38 -0
- package/casework/client.js +14 -0
- package/casework/index.d.ts +7 -0
- package/evidence/client.d.ts +102 -1
- package/evidence/client.js +25 -0
- package/evidence/index.d.ts +20 -0
- package/native.js +1 -1
- package/package.json +4 -4
- package/relay/client.d.ts +52 -0
package/README.md
CHANGED
|
@@ -79,6 +79,14 @@ mutation automatically. See
|
|
|
79
79
|
[Exact JSON in Node](https://docs.registrystack.org/reference/client-api/#exact-json-in-node)
|
|
80
80
|
for the full rules.
|
|
81
81
|
|
|
82
|
+
`breg.verifyWebhookDelivery({ method, path, headers, body, key })` authenticates
|
|
83
|
+
the exact bytes of one Version 1 webhook delivery. The result returns
|
|
84
|
+
`deliveryTime` and `idempotencyKey`; the receiver must bound clock skew and
|
|
85
|
+
deduplicate once-only effects on authenticated `source` plus `id`, or on an
|
|
86
|
+
application business key. See
|
|
87
|
+
[Verify a webhook delivery](https://docs.registrystack.org/reference/client-api/#verify-a-webhook-delivery)
|
|
88
|
+
for a receiver example.
|
|
89
|
+
|
|
82
90
|
## Versioning
|
|
83
91
|
|
|
84
92
|
Install the exact client version that matches the deployment. The unified
|
package/breg/client.d.ts
CHANGED
|
@@ -7,6 +7,41 @@ export type JsonObject = { readonly [key: string]: JsonValue }
|
|
|
7
7
|
/** An integer that satisfies `Number.isSafeInteger` and the option's documented bounds. */
|
|
8
8
|
export type SafeInteger = number
|
|
9
9
|
|
|
10
|
+
export type WebhookVerificationRefusalCode =
|
|
11
|
+
| 'missing_header'
|
|
12
|
+
| 'malformed_signature'
|
|
13
|
+
| 'signature_mismatch'
|
|
14
|
+
| 'unsupported_version'
|
|
15
|
+
|
|
16
|
+
export interface WebhookDeliveryInput {
|
|
17
|
+
method: string
|
|
18
|
+
path: string
|
|
19
|
+
headers: Readonly<Record<string, string>>
|
|
20
|
+
/** Exact bytes in a non-shared backing store. */
|
|
21
|
+
body: Buffer
|
|
22
|
+
/** Secret bytes in a non-shared backing store. */
|
|
23
|
+
key: Buffer
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface VerifiedWebhookDelivery {
|
|
27
|
+
readonly id: string
|
|
28
|
+
readonly source: string
|
|
29
|
+
readonly type: string
|
|
30
|
+
readonly time: string
|
|
31
|
+
readonly dataschema: string
|
|
32
|
+
readonly generation: string
|
|
33
|
+
readonly attempt: string
|
|
34
|
+
readonly deliveryTime: string
|
|
35
|
+
readonly idempotencyKey: string
|
|
36
|
+
readonly body: Buffer
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Authenticate one exact Base Registry Engine Version 1 webhook delivery.
|
|
41
|
+
* Apply delivery-time skew and idempotency policy before acting on the body.
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyWebhookDelivery(input: WebhookDeliveryInput): VerifiedWebhookDelivery
|
|
44
|
+
|
|
10
45
|
export interface PrivateJwk {
|
|
11
46
|
readonly kty: string
|
|
12
47
|
readonly kid: string
|
|
@@ -18,7 +53,23 @@ export interface PrivateKeyJwtConfig {
|
|
|
18
53
|
tokenEndpoint: string
|
|
19
54
|
clientId: string
|
|
20
55
|
clientKey: PrivateJwk
|
|
56
|
+
/**
|
|
57
|
+
* The audience of the client assertion (who checks the client's
|
|
58
|
+
* authentication). Defaults to the token endpoint URL. This is not the
|
|
59
|
+
* `resource` of the token request.
|
|
60
|
+
*/
|
|
21
61
|
audience?: string | null
|
|
62
|
+
/**
|
|
63
|
+
* The RFC 8707 resource indicator the token is requested for: the resource
|
|
64
|
+
* server's registered identifier, not a URL to fetch.
|
|
65
|
+
*/
|
|
66
|
+
resource?: string | null
|
|
67
|
+
/**
|
|
68
|
+
* The scopes requested for the token, sent as one space-delimited `scope`
|
|
69
|
+
* parameter. A requested scope may narrow the client's registered
|
|
70
|
+
* permission set; it can never widen it.
|
|
71
|
+
*/
|
|
72
|
+
scopes?: ReadonlyArray<string> | null
|
|
22
73
|
assertionLifetimeSeconds?: SafeInteger | null
|
|
23
74
|
refreshMarginSeconds?: SafeInteger | null
|
|
24
75
|
requestTimeoutMilliseconds?: SafeInteger | null
|
|
@@ -27,9 +78,56 @@ export interface PrivateKeyJwtConfig {
|
|
|
27
78
|
trustedRootCertificates?: string | null
|
|
28
79
|
}
|
|
29
80
|
|
|
81
|
+
/** OAuth credentials are returned to the caller. Keep them out of logs and persistence. */
|
|
82
|
+
export class PrivateKeyJwt {
|
|
83
|
+
constructor(config: PrivateKeyJwtConfig)
|
|
84
|
+
/** Fresh RFC 8693 exchange. Requires resource and scopes; never shares the service cache. */
|
|
85
|
+
exchange(subjectToken: string): Promise<string>
|
|
86
|
+
/** Client credentials with the shared provider's bounded service-token cache. */
|
|
87
|
+
bearerToken(): Promise<string>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One immutable verified person or grant context. Reconstruct after host source facts change. */
|
|
91
|
+
export interface ExchangeContext {
|
|
92
|
+
issuer: string
|
|
93
|
+
subject: string
|
|
94
|
+
audience: string
|
|
95
|
+
generation: string
|
|
96
|
+
deadlineSeconds: SafeInteger
|
|
97
|
+
grantId?: string | null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type ExchangePrivateKeyJwtConfig = PrivateKeyJwtConfig & {
|
|
101
|
+
resource: string
|
|
102
|
+
scopes: ReadonlyArray<string>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface FirstPartyExchangeSource {
|
|
106
|
+
key: PrivateJwk
|
|
107
|
+
/** Reviewed host attributes; registry_actor_kind must be human and grant claims are refused. */
|
|
108
|
+
attributes: Readonly<Record<string, JsonValue>>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface RemoteExchangeSource {
|
|
112
|
+
/** Exact assertion URL supplied by the authority client, such as Casework.taskAssertionEndpoint. */
|
|
113
|
+
endpoint: string
|
|
114
|
+
bootstrap: ExchangePrivateKeyJwtConfig
|
|
115
|
+
bootstrapResource: string
|
|
116
|
+
bootstrapScope: string
|
|
117
|
+
requestTimeoutMilliseconds?: SafeInteger | null
|
|
118
|
+
connectTimeoutMilliseconds?: SafeInteger | null
|
|
119
|
+
userAgent?: string | null
|
|
120
|
+
trustedRootCertificates?: string | null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type ExchangeAuthorizationConfig =
|
|
124
|
+
| { client: ExchangePrivateKeyJwtConfig; context: ExchangeContext; firstParty: FirstPartyExchangeSource }
|
|
125
|
+
| { client: ExchangePrivateKeyJwtConfig; context: ExchangeContext; remote: RemoteExchangeSource }
|
|
126
|
+
|
|
30
127
|
export type BaseRegistryAuthorization =
|
|
31
128
|
| { static: string }
|
|
32
129
|
| { privateKeyJwt: PrivateKeyJwtConfig }
|
|
130
|
+
| { exchange: ExchangeAuthorizationConfig }
|
|
33
131
|
|
|
34
132
|
export interface BaseRegistryClientConfig {
|
|
35
133
|
baseUrl: string
|
package/breg/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { types: { isProxy } } = require('node:util');
|
|
3
|
+
const { types: { isProxy, isSharedArrayBuffer } } = require('node:util');
|
|
4
4
|
const native = require('./index');
|
|
5
5
|
|
|
6
6
|
const MAX_JSON_DEPTH = 128;
|
|
@@ -50,6 +50,64 @@ function inputError(kind) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function plainDataObject(value, expectedFields) {
|
|
54
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value) || isProxy(value)) {
|
|
55
|
+
throw inputError('invalid_request');
|
|
56
|
+
}
|
|
57
|
+
const prototype = Object.getPrototypeOf(value);
|
|
58
|
+
if (prototype !== Object.prototype && prototype !== null) throw inputError('invalid_request');
|
|
59
|
+
const keys = Reflect.ownKeys(value);
|
|
60
|
+
if (keys.some((key) => typeof key !== 'string')
|
|
61
|
+
|| keys.length !== expectedFields.length
|
|
62
|
+
|| keys.some((key) => !expectedFields.includes(key))) throw inputError('invalid_request');
|
|
63
|
+
const result = Object.create(null);
|
|
64
|
+
for (const field of expectedFields) {
|
|
65
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, field);
|
|
66
|
+
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) {
|
|
67
|
+
throw inputError('invalid_request');
|
|
68
|
+
}
|
|
69
|
+
result[field] = descriptor.value;
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function webhookHeaders(value) {
|
|
75
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value) || isProxy(value)) {
|
|
76
|
+
throw inputError('invalid_request');
|
|
77
|
+
}
|
|
78
|
+
const prototype = Object.getPrototypeOf(value);
|
|
79
|
+
if (prototype !== Object.prototype && prototype !== null) throw inputError('invalid_request');
|
|
80
|
+
const keys = Reflect.ownKeys(value);
|
|
81
|
+
if (keys.some((key) => typeof key !== 'string')) {
|
|
82
|
+
throw inputError('invalid_request');
|
|
83
|
+
}
|
|
84
|
+
const result = Object.create(null);
|
|
85
|
+
for (const name of keys) {
|
|
86
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
87
|
+
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')
|
|
88
|
+
|| typeof descriptor.value !== 'string') throw inputError('invalid_request');
|
|
89
|
+
result[name] = descriptor.value;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function verifyWebhookDelivery(value) {
|
|
95
|
+
try {
|
|
96
|
+
const input = plainDataObject(value, ['method', 'path', 'headers', 'body', 'key']);
|
|
97
|
+
if (typeof input.method !== 'string' || typeof input.path !== 'string'
|
|
98
|
+
|| !Buffer.isBuffer(input.body) || !Buffer.isBuffer(input.key)
|
|
99
|
+
|| isSharedArrayBuffer(input.body.buffer) || isSharedArrayBuffer(input.key.buffer)) {
|
|
100
|
+
throw inputError('invalid_request');
|
|
101
|
+
}
|
|
102
|
+
input.headers = webhookHeaders(input.headers);
|
|
103
|
+
input.body = Buffer.from(input.body);
|
|
104
|
+
input.key = Buffer.from(input.key);
|
|
105
|
+
return native.verifyWebhookDelivery(input);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw normalize(error, 'invalid_request');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
53
111
|
function chargeString(value, budget, kind) {
|
|
54
112
|
budget.stringBytes += Buffer.byteLength(value, 'utf8');
|
|
55
113
|
if (budget.stringBytes > MAX_JSON_STRING_BYTES) throw inputError(kind);
|
|
@@ -288,7 +346,25 @@ class BaseRegistryClient extends native.BaseRegistryClient {
|
|
|
288
346
|
}
|
|
289
347
|
}
|
|
290
348
|
|
|
349
|
+
class PrivateKeyJwt {
|
|
350
|
+
#native;
|
|
351
|
+
constructor(config) {
|
|
352
|
+
try { this.#native = new native.PrivateKeyJwt(cloneJson(config, { nodes: 0, stringBytes: 0, active: new WeakSet() }, 0, 'configuration')); }
|
|
353
|
+
catch (error) { throw normalize(error, 'configuration'); }
|
|
354
|
+
}
|
|
355
|
+
async exchange(subjectToken) {
|
|
356
|
+
try { return await this.#native.exchange(subjectToken); }
|
|
357
|
+
catch (error) { throw normalize(error, 'token'); }
|
|
358
|
+
}
|
|
359
|
+
async bearerToken() {
|
|
360
|
+
try { return await this.#native.bearerToken(); }
|
|
361
|
+
catch (error) { throw normalize(error, 'token'); }
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
291
365
|
module.exports = {
|
|
366
|
+
verifyWebhookDelivery,
|
|
367
|
+
PrivateKeyJwt,
|
|
292
368
|
BaseRegistryClient,
|
|
293
369
|
BaseRegistryClientError,
|
|
294
370
|
BRegMetadata: native.BRegMetadata,
|
package/breg/index.d.ts
CHANGED
|
@@ -244,6 +244,14 @@ export declare class BRegTombstoneBinding {
|
|
|
244
244
|
}
|
|
245
245
|
export type TombstoneBinding = BRegTombstoneBinding
|
|
246
246
|
|
|
247
|
+
/** Shared OAuth client authentication and uncached task-token exchange. */
|
|
248
|
+
export declare class PrivateKeyJwt {
|
|
249
|
+
constructor(config: any)
|
|
250
|
+
exchange(subjectToken: string): Promise<string>
|
|
251
|
+
bearerToken(): Promise<string>
|
|
252
|
+
}
|
|
253
|
+
export type PrivateKeyJwtBinding = PrivateKeyJwt
|
|
254
|
+
|
|
247
255
|
export interface CompleteOutcome {
|
|
248
256
|
kind: string
|
|
249
257
|
value: any
|
|
@@ -281,3 +289,29 @@ export interface RawOutcome {
|
|
|
281
289
|
traceId: string
|
|
282
290
|
etag?: string
|
|
283
291
|
}
|
|
292
|
+
|
|
293
|
+
/** Authenticated BReg webhook metadata and exact body. */
|
|
294
|
+
export interface VerifiedWebhookDelivery {
|
|
295
|
+
id: string
|
|
296
|
+
source: string
|
|
297
|
+
type: string
|
|
298
|
+
time: string
|
|
299
|
+
dataschema: string
|
|
300
|
+
generation: string
|
|
301
|
+
attempt: string
|
|
302
|
+
deliveryTime: string
|
|
303
|
+
idempotencyKey: string
|
|
304
|
+
body: Buffer
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Verify a BReg Version 1 webhook signature without applying receiver policy. */
|
|
308
|
+
export declare function verifyWebhookDelivery(input: WebhookDeliveryInput): VerifiedWebhookDelivery
|
|
309
|
+
|
|
310
|
+
/** Exact BReg webhook request supplied by a receiver. */
|
|
311
|
+
export interface WebhookDeliveryInput {
|
|
312
|
+
method: string
|
|
313
|
+
path: string
|
|
314
|
+
headers: Record<string, string>
|
|
315
|
+
body: Buffer
|
|
316
|
+
key: Buffer
|
|
317
|
+
}
|
package/casework/client.d.ts
CHANGED
|
@@ -21,6 +21,30 @@ export type PageStatus = 'complete' | 'budget_exhausted' | 'source_unavailable'
|
|
|
21
21
|
export type HistoryKind = 'observed' | 'opened' | 'claimed' | 'assigned' | 'delegated' | 'caseload_moved' | 'clock_reminder' | 'clock_step_applied' | 'clock_recomputed' | 'released' | 'draft_saved' | 'attempt_reserved' | 'attempt_uncertain' | 'action_completed' | 'attempt_settled' | 'superseded' | 'completed'
|
|
22
22
|
|
|
23
23
|
export interface IssuerPrincipal { issuer: string; subject: string }
|
|
24
|
+
export type TaskGrantBounds = { type: 'evidence'; requirement: string } | { type: 'breg'; permissions: ReadonlyArray<TaskPermission> }
|
|
25
|
+
export interface TaskPermission { collection: string; operations: ReadonlyArray<string> }
|
|
26
|
+
export interface EvidenceRequesterContext { requesterTags: ReadonlyArray<string>; audience: string }
|
|
27
|
+
export interface TaskApprovalRequest { templateId: string; templateVersion: string }
|
|
28
|
+
export interface TaskTemplatePreview {
|
|
29
|
+
id: string; version: string; label: string; agent: IssuerPrincipal; client: string; resource: string; scopes: ReadonlyArray<string>; purpose: string
|
|
30
|
+
evidenceContext?: EvidenceRequesterContext; bounds: TaskGrantBounds; subjects: { readonly [key: string]: Exclude<JsonScalar, null> }; lifetimeSeconds: SafeInteger
|
|
31
|
+
}
|
|
32
|
+
export interface TaskTemplatePreviews { itemRevision: SafeInteger; templates: ReadonlyArray<TaskTemplatePreview> }
|
|
33
|
+
/** Grant metadata deliberately excludes stored subject values. */
|
|
34
|
+
export interface TaskGrantView {
|
|
35
|
+
id: string; templateId: string; templateVersion: string; agent: IssuerPrincipal; client: string; resource: string; scopes: ReadonlyArray<string>; purpose: string
|
|
36
|
+
evidenceContext?: EvidenceRequesterContext; bounds: TaskGrantBounds; expiresAt: SafeInteger; invalidated: boolean
|
|
37
|
+
}
|
|
38
|
+
export interface TaskGrantList { grants: ReadonlyArray<TaskGrantView> }
|
|
39
|
+
export interface TaskGrantRevocation { id: string; invalidated: boolean }
|
|
40
|
+
/** A short-lived credential. Do not persist or log the assertion. */
|
|
41
|
+
export interface TaskAssertionResponse { assertion: string; expiresAt: SafeInteger; grantExpiresAt: SafeInteger }
|
|
42
|
+
export interface TaskGrantStatusDetails {
|
|
43
|
+
grantId: string; sourceIssuer: string; principal: string; client: string; resource: string; purpose: string
|
|
44
|
+
bounds: TaskGrantBounds; subjects: { readonly [key: string]: Exclude<JsonScalar, null> }; expiresAt: SafeInteger
|
|
45
|
+
}
|
|
46
|
+
export interface TaskGrantStatus { active: boolean; grant?: TaskGrantStatusDetails }
|
|
47
|
+
|
|
24
48
|
export interface DirectoryMember { issuer: string; subject: string; displayName?: string | null }
|
|
25
49
|
export interface SubjectRef { sourceId: string; kind: string; id: string }
|
|
26
50
|
export interface SourceBinding {
|
|
@@ -326,12 +350,18 @@ export type KnownCaseworkProblemCode =
|
|
|
326
350
|
| 'request.invalid'
|
|
327
351
|
| 'request.method-not-allowed'
|
|
328
352
|
| 'request.not-found'
|
|
353
|
+
| 'request.reason-unsupported'
|
|
354
|
+
| 'request.source-rejected'
|
|
329
355
|
| 'request.unprocessable'
|
|
330
356
|
| 'request.unsupported-media-type'
|
|
331
357
|
| 'runtime.failure'
|
|
332
358
|
| 'service.unavailable'
|
|
359
|
+
| 'source-profile.not-applicable'
|
|
360
|
+
| 'source-profile.required'
|
|
333
361
|
| 'source.bad-gateway'
|
|
334
362
|
| 'source.not-found'
|
|
363
|
+
| 'source.record-missing'
|
|
364
|
+
| 'source.reviewer-not-authorized'
|
|
335
365
|
| 'source.signature-invalid'
|
|
336
366
|
| 'work-item.already-claimed'
|
|
337
367
|
| 'work-item.not-holder'
|
|
@@ -405,6 +435,14 @@ export class CaseworkClient {
|
|
|
405
435
|
listWorkItems(token: string, profile: string, sourceProfile: string, query: ListWorkItemsQuery): Promise<CaseworkOutcome<WorkItemPage>>
|
|
406
436
|
nextWorkItem(token: string, profile: string, sourceProfile: string, query?: NextWorkItemQuery | null): Promise<CaseworkOutcome<WorkItemPage>>
|
|
407
437
|
getWorkItem(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome<WorkItem>>
|
|
438
|
+
previewTaskTemplates(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome<TaskTemplatePreviews>>
|
|
439
|
+
listTaskGrants(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome<TaskGrantList>>
|
|
440
|
+
approveTaskGrant(token: string, profile: string, sourceProfile: string, itemId: string, expectedRevision: SafeInteger, idempotencyKey: string, approval: TaskApprovalRequest): Promise<CaseworkOutcome<TaskGrantView>>
|
|
441
|
+
revokeTaskGrant(token: string, profile: string, sourceProfile: string, itemId: string, grantId: string): Promise<CaseworkOutcome<TaskGrantRevocation>>
|
|
442
|
+
taskAssertion(token: string, grantId: string): Promise<CaseworkOutcome<TaskAssertionResponse>>
|
|
443
|
+
/** Exact empty-POST authority route for the shared remote exchange provider. */
|
|
444
|
+
taskAssertionEndpoint(grantId: string): string
|
|
445
|
+
taskGrantStatus(token: string, grantId: string): Promise<CaseworkOutcome<TaskGrantStatus>>
|
|
408
446
|
claimWorkItem(token: string, profile: string, sourceProfile: string, action: CaseworkAction, idempotencyKey: string): Promise<CaseworkOutcome<MutationResponse>>
|
|
409
447
|
releaseWorkItem(token: string, profile: string, sourceProfile: string, action: CaseworkAction, idempotencyKey: string): Promise<CaseworkOutcome<MutationResponse>>
|
|
410
448
|
getDraft(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome<DraftResponse>>
|
package/casework/client.js
CHANGED
|
@@ -92,6 +92,14 @@ class CaseworkClient {
|
|
|
92
92
|
throw normalized(error, 'configuration');
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
taskAssertionEndpoint(grantId) {
|
|
97
|
+
try {
|
|
98
|
+
return this.native.taskAssertionEndpoint(grantId);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw normalized(error, 'invalid_request');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
for (const [method, jsonIndexes] of [
|
|
@@ -112,6 +120,12 @@ for (const [method, jsonIndexes] of [
|
|
|
112
120
|
['listWorkItems', [3]],
|
|
113
121
|
['nextWorkItem', [3]],
|
|
114
122
|
['getWorkItem', []],
|
|
123
|
+
['previewTaskTemplates', []],
|
|
124
|
+
['listTaskGrants', []],
|
|
125
|
+
['approveTaskGrant', [6]],
|
|
126
|
+
['revokeTaskGrant', []],
|
|
127
|
+
['taskAssertion', []],
|
|
128
|
+
['taskGrantStatus', []],
|
|
115
129
|
['claimWorkItem', [3]],
|
|
116
130
|
['releaseWorkItem', [3]],
|
|
117
131
|
['getDraft', []],
|
package/casework/index.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export declare class CaseworkClient {
|
|
|
19
19
|
listWorkItems(token: string, profile: string, sourceProfile: string, query: any): Promise<CaseworkOutcome>
|
|
20
20
|
nextWorkItem(token: string, profile: string, sourceProfile: string, query?: any | undefined | null): Promise<CaseworkOutcome>
|
|
21
21
|
getWorkItem(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome>
|
|
22
|
+
previewTaskTemplates(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome>
|
|
23
|
+
listTaskGrants(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome>
|
|
24
|
+
approveTaskGrant(token: string, profile: string, sourceProfile: string, itemId: string, expectedRevision: number, idempotencyKey: string, approval: any): Promise<CaseworkOutcome>
|
|
25
|
+
revokeTaskGrant(token: string, profile: string, sourceProfile: string, itemId: string, grantId: string): Promise<CaseworkOutcome>
|
|
26
|
+
taskAssertion(token: string, grantId: string): Promise<CaseworkOutcome>
|
|
27
|
+
taskAssertionEndpoint(grantId: string): string
|
|
28
|
+
taskGrantStatus(token: string, grantId: string): Promise<CaseworkOutcome>
|
|
22
29
|
claimWorkItem(token: string, profile: string, sourceProfile: string, action: any, idempotencyKey: string): Promise<CaseworkOutcome>
|
|
23
30
|
releaseWorkItem(token: string, profile: string, sourceProfile: string, action: any, idempotencyKey: string): Promise<CaseworkOutcome>
|
|
24
31
|
getDraft(token: string, profile: string, sourceProfile: string, itemId: string): Promise<CaseworkOutcome>
|
package/evidence/client.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface ProgressiveVerifiedResult {
|
|
|
35
35
|
readonly values: Readonly<Record<string, unknown>>
|
|
36
36
|
readonly value: unknown
|
|
37
37
|
readonly subjectContinuity: ProgressiveSubjectContinuity
|
|
38
|
+
/** Pre-response trust and policy snapshot. Retain with the exact artifact. */
|
|
39
|
+
readonly retainedVerification: Buffer
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/** A signed-JWS result. The credential member of the native result is `null`. */
|
|
@@ -82,7 +84,23 @@ export interface PrivateKeyJwtConfig {
|
|
|
82
84
|
tokenEndpoint: string
|
|
83
85
|
clientId: string
|
|
84
86
|
clientKey: Readonly<Record<string, unknown>>
|
|
87
|
+
/**
|
|
88
|
+
* The audience of the client assertion (who checks the client's
|
|
89
|
+
* authentication). Defaults to the token endpoint URL. This is not the
|
|
90
|
+
* `resource` of the token request.
|
|
91
|
+
*/
|
|
85
92
|
audience?: string
|
|
93
|
+
/**
|
|
94
|
+
* The RFC 8707 resource indicator the token is requested for: the resource
|
|
95
|
+
* server's registered identifier, not a URL to fetch.
|
|
96
|
+
*/
|
|
97
|
+
resource?: string
|
|
98
|
+
/**
|
|
99
|
+
* The scopes requested for the token, sent as one space-delimited `scope`
|
|
100
|
+
* parameter. A requested scope may narrow the client's registered
|
|
101
|
+
* permission set; it can never widen it.
|
|
102
|
+
*/
|
|
103
|
+
scopes?: ReadonlyArray<string>
|
|
86
104
|
assertionLifetimeSeconds?: number
|
|
87
105
|
refreshMarginSeconds?: number
|
|
88
106
|
requestTimeoutMs?: number
|
|
@@ -100,12 +118,91 @@ export type EvidenceTokenConfig =
|
|
|
100
118
|
| { static: string }
|
|
101
119
|
| { privateKeyJwt: PrivateKeyJwtConfig }
|
|
102
120
|
|
|
121
|
+
/** A verified actor/grant generation with a bounded authorization lifetime. */
|
|
122
|
+
export interface EvidenceExchangeContext {
|
|
123
|
+
issuer: string
|
|
124
|
+
subject: string
|
|
125
|
+
audience: string
|
|
126
|
+
generation: string
|
|
127
|
+
deadlineSeconds: number
|
|
128
|
+
grantId?: string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The client half of an exchange authorization.
|
|
133
|
+
*
|
|
134
|
+
* This is not a `PrivateKeyJwtConfig`, even though the two describe the same
|
|
135
|
+
* kind of client: an exchange is pinned to one resource and scope set, so both
|
|
136
|
+
* are required here, and the object is read by the shared exchange
|
|
137
|
+
* configuration parser rather than by the binding's own reader, so its timeout
|
|
138
|
+
* members are the full-word ones that parser accepts. State at most one of
|
|
139
|
+
* `requestTimeoutMilliseconds` and `requestTimeoutSeconds`, and at most one of
|
|
140
|
+
* `connectTimeoutMilliseconds` and `connectTimeoutSeconds`; naming both of a
|
|
141
|
+
* pair is refused.
|
|
142
|
+
*/
|
|
143
|
+
export interface EvidenceExchangeClientConfig {
|
|
144
|
+
tokenEndpoint: string
|
|
145
|
+
clientId: string
|
|
146
|
+
/** The client's private key, as a JWK. */
|
|
147
|
+
clientKey: Readonly<Record<string, unknown>>
|
|
148
|
+
/**
|
|
149
|
+
* The audience of the client assertion, when the authorization server
|
|
150
|
+
* expects something other than its own token endpoint.
|
|
151
|
+
*/
|
|
152
|
+
audience?: string
|
|
153
|
+
/**
|
|
154
|
+
* The RFC 8707 resource indicator the token is requested for: the resource
|
|
155
|
+
* server's registered identifier, not a URL to fetch.
|
|
156
|
+
*/
|
|
157
|
+
resource: string
|
|
158
|
+
/**
|
|
159
|
+
* The scopes requested for the token, sent as one space-delimited `scope`
|
|
160
|
+
* parameter. A requested scope may narrow the client's registered
|
|
161
|
+
* permission set; it can never widen it.
|
|
162
|
+
*/
|
|
163
|
+
scopes: ReadonlyArray<string>
|
|
164
|
+
assertionLifetimeSeconds?: number
|
|
165
|
+
refreshMarginSeconds?: number
|
|
166
|
+
requestTimeoutMilliseconds?: number
|
|
167
|
+
requestTimeoutSeconds?: number
|
|
168
|
+
connectTimeoutMilliseconds?: number
|
|
169
|
+
connectTimeoutSeconds?: number
|
|
170
|
+
userAgent?: string
|
|
171
|
+
/** Additional trust roots for the token endpoint, as a PEM bundle. */
|
|
172
|
+
trustedRootCertificates?: string
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** The owning authority a remote-sourced exchange obtains assertions from. */
|
|
176
|
+
export interface EvidenceExchangeRemoteSourceConfig {
|
|
177
|
+
endpoint: string
|
|
178
|
+
bootstrap: EvidenceExchangeClientConfig
|
|
179
|
+
bootstrapResource: string
|
|
180
|
+
bootstrapScope: string
|
|
181
|
+
requestTimeoutMilliseconds?: number
|
|
182
|
+
requestTimeoutSeconds?: number
|
|
183
|
+
connectTimeoutMilliseconds?: number
|
|
184
|
+
connectTimeoutSeconds?: number
|
|
185
|
+
userAgent?: string
|
|
186
|
+
/** Additional trust roots for the authority, as a PEM bundle. */
|
|
187
|
+
trustedRootCertificates?: string
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export type EvidenceExchangeAuthorizationConfig = {
|
|
191
|
+
client: EvidenceExchangeClientConfig
|
|
192
|
+
context: EvidenceExchangeContext
|
|
193
|
+
} & (
|
|
194
|
+
| { firstParty: { key: Readonly<Record<string, unknown>>, attributes: Readonly<Record<string, unknown>> }, remote?: never }
|
|
195
|
+
| { firstParty?: never, remote: EvidenceExchangeRemoteSourceConfig }
|
|
196
|
+
)
|
|
197
|
+
|
|
103
198
|
/** The configuration `new EvidenceClient(...)` reads. */
|
|
104
199
|
export interface EvidenceClientConfig {
|
|
105
200
|
baseUrl: string
|
|
106
201
|
trustedJwks: TrustedJwks
|
|
107
202
|
revokedKeyIds: ReadonlyArray<string>
|
|
108
|
-
token
|
|
203
|
+
/** Configure exactly one of `token` or `authorization`. */
|
|
204
|
+
token?: EvidenceTokenConfig
|
|
205
|
+
authorization?: { exchange: EvidenceExchangeAuthorizationConfig }
|
|
109
206
|
requestTimeoutMs?: number
|
|
110
207
|
connectTimeoutMs?: number
|
|
111
208
|
userAgent?: string
|
|
@@ -136,6 +233,10 @@ export declare class EvidenceClient extends NativeEvidenceClient {
|
|
|
136
233
|
* the JavaScript wrapper.
|
|
137
234
|
*/
|
|
138
235
|
static fromProfile(path: string, privateKeyJwk?: Readonly<Record<string, unknown>>): EvidenceClient
|
|
236
|
+
static fromProfileWithAuthorization(
|
|
237
|
+
path: string,
|
|
238
|
+
authorization: { exchange: EvidenceExchangeAuthorizationConfig },
|
|
239
|
+
): EvidenceClient
|
|
139
240
|
}
|
|
140
241
|
|
|
141
242
|
/**
|
package/evidence/client.js
CHANGED
|
@@ -166,6 +166,7 @@ for (const name of [
|
|
|
166
166
|
'values',
|
|
167
167
|
'value',
|
|
168
168
|
'subjectContinuity',
|
|
169
|
+
'retainedVerification',
|
|
169
170
|
]) {
|
|
170
171
|
wrapGetter(native.AudienceScopedResult.prototype, name);
|
|
171
172
|
}
|
|
@@ -215,6 +216,16 @@ class EvidenceClient extends native.EvidenceClient {
|
|
|
215
216
|
throw normalize(error);
|
|
216
217
|
}
|
|
217
218
|
}
|
|
219
|
+
|
|
220
|
+
static fromProfileWithAuthorization(path, authorization) {
|
|
221
|
+
try {
|
|
222
|
+
const client = native.EvidenceClient.fromProfileWithAuthorization(path, authorization);
|
|
223
|
+
Object.setPrototypeOf(client, this.prototype);
|
|
224
|
+
return client;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw normalize(error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
218
229
|
}
|
|
219
230
|
|
|
220
231
|
class SdJwtVcBatchResponse extends native.SdJwtVcBatchResponse {
|
|
@@ -236,4 +247,18 @@ module.exports = {
|
|
|
236
247
|
RawEvidenceRequestBatchResponse: native.RawEvidenceRequestBatchResponse,
|
|
237
248
|
AudienceScopedResult: native.AudienceScopedResult,
|
|
238
249
|
SdJwtVcBatchResponse,
|
|
250
|
+
verifyRetained(context, response) {
|
|
251
|
+
try {
|
|
252
|
+
return native.verifyRetained(context, response);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw normalize(error);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
verifyRetainedAsOf(context, response, asOfMillis) {
|
|
258
|
+
try {
|
|
259
|
+
return native.verifyRetainedAsOf(context, response, asOfMillis);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
throw normalize(error);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
239
264
|
};
|
package/evidence/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* FFI boundary until the caller asks for it.
|
|
7
7
|
*/
|
|
8
8
|
export declare class AudienceScopedResult {
|
|
9
|
+
/** Exact serialized pre-response trust snapshot for offline re-verification. */
|
|
10
|
+
get retainedVerification(): Buffer
|
|
9
11
|
get responseFormat(): string
|
|
10
12
|
get evidence(): any
|
|
11
13
|
get traceId(): string | null
|
|
@@ -36,6 +38,12 @@ export declare class EvidenceClient {
|
|
|
36
38
|
* client and are likewise redacted there.
|
|
37
39
|
*/
|
|
38
40
|
static fromProfile(path: string, privateKeyJwk?: any): EvidenceClient
|
|
41
|
+
/**
|
|
42
|
+
* Build a profile-driven client with a context-bound staff exchange.
|
|
43
|
+
* The profile's discovered authority and pinned OAuth request parameters
|
|
44
|
+
* must match the immutable provider before the first token is acquired.
|
|
45
|
+
*/
|
|
46
|
+
static fromProfileWithAuthorization(path: string, authorization: any): EvidenceClient
|
|
39
47
|
/**
|
|
40
48
|
* Close the expectations for one request and generate its nonce. No I/O
|
|
41
49
|
* happens here. The returned request is good for exactly one exchange:
|
|
@@ -300,3 +308,15 @@ export interface VerifiedEvidenceRequestBatch {
|
|
|
300
308
|
export type VerifiedEvidenceRequestBatchItem =
|
|
301
309
|
| { status: 'available', verified: VerifiedEvidence }
|
|
302
310
|
| { status: 'notAvailable' }
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Verify exact retained response bytes against a bounded pre-response trust
|
|
314
|
+
* snapshot without constructing a client or performing network I/O.
|
|
315
|
+
*/
|
|
316
|
+
export declare function verifyRetained(context: Buffer, response: Buffer): VerifiedEvidence
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Replay an earlier decision at its recorded instant. Current decisions use
|
|
320
|
+
* `verify_retained` so expired assertions cannot be accepted as current.
|
|
321
|
+
*/
|
|
322
|
+
export declare function verifyRetainedAsOf(context: Buffer, response: Buffer, asOfMillis: number): VerifiedEvidence
|
package/native.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@registrystack/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Unified Node.js client for Registry Stack products.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://docs.registrystack.org/reference/client-api/",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"typescript": "6.0.3"
|
|
58
58
|
},
|
|
59
59
|
"optionalDependencies": {
|
|
60
|
-
"@registrystack/client-darwin-arm64": "0.
|
|
61
|
-
"@registrystack/client-linux-arm64-gnu": "0.
|
|
62
|
-
"@registrystack/client-linux-x64-gnu": "0.
|
|
60
|
+
"@registrystack/client-darwin-arm64": "0.32.0",
|
|
61
|
+
"@registrystack/client-linux-arm64-gnu": "0.32.0",
|
|
62
|
+
"@registrystack/client-linux-x64-gnu": "0.32.0"
|
|
63
63
|
}
|
|
64
64
|
}
|
package/relay/client.d.ts
CHANGED
|
@@ -14,7 +14,23 @@ export interface PrivateKeyJwtConfig {
|
|
|
14
14
|
tokenEndpoint: string
|
|
15
15
|
clientId: string
|
|
16
16
|
clientKey: PrivateJwk
|
|
17
|
+
/**
|
|
18
|
+
* The audience of the client assertion (who checks the client's
|
|
19
|
+
* authentication). Defaults to the token endpoint URL. This is not the
|
|
20
|
+
* `resource` of the token request.
|
|
21
|
+
*/
|
|
17
22
|
audience?: string | null
|
|
23
|
+
/**
|
|
24
|
+
* The RFC 8707 resource indicator the token is requested for: the resource
|
|
25
|
+
* server's registered identifier, not a URL to fetch.
|
|
26
|
+
*/
|
|
27
|
+
resource?: string | null
|
|
28
|
+
/**
|
|
29
|
+
* The scopes requested for the token, sent as one space-delimited `scope`
|
|
30
|
+
* parameter. A requested scope may narrow the client's registered
|
|
31
|
+
* permission set; it can never widen it.
|
|
32
|
+
*/
|
|
33
|
+
scopes?: ReadonlyArray<string> | null
|
|
18
34
|
assertionLifetimeSeconds?: SafeInteger | null
|
|
19
35
|
refreshMarginSeconds?: SafeInteger | null
|
|
20
36
|
requestTimeoutMilliseconds?: SafeInteger | null
|
|
@@ -23,9 +39,45 @@ export interface PrivateKeyJwtConfig {
|
|
|
23
39
|
trustedRootCertificates?: string | null
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
/** One immutable verified person or grant context. Reconstruct after host source facts change. */
|
|
43
|
+
export interface ExchangeContext {
|
|
44
|
+
issuer: string
|
|
45
|
+
subject: string
|
|
46
|
+
audience: string
|
|
47
|
+
generation: string
|
|
48
|
+
deadlineSeconds: SafeInteger
|
|
49
|
+
grantId?: string | null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ExchangePrivateKeyJwtConfig = PrivateKeyJwtConfig & {
|
|
53
|
+
resource: string
|
|
54
|
+
scopes: ReadonlyArray<string>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface FirstPartyExchangeSource {
|
|
58
|
+
key: PrivateJwk
|
|
59
|
+
attributes: Readonly<Record<string, JsonValue>>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface RemoteExchangeSource {
|
|
63
|
+
endpoint: string
|
|
64
|
+
bootstrap: ExchangePrivateKeyJwtConfig
|
|
65
|
+
bootstrapResource: string
|
|
66
|
+
bootstrapScope: string
|
|
67
|
+
requestTimeoutMilliseconds?: SafeInteger | null
|
|
68
|
+
connectTimeoutMilliseconds?: SafeInteger | null
|
|
69
|
+
userAgent?: string | null
|
|
70
|
+
trustedRootCertificates?: string | null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type ExchangeAuthorizationConfig =
|
|
74
|
+
| { client: ExchangePrivateKeyJwtConfig; context: ExchangeContext; firstParty: FirstPartyExchangeSource }
|
|
75
|
+
| { client: ExchangePrivateKeyJwtConfig; context: ExchangeContext; remote: RemoteExchangeSource }
|
|
76
|
+
|
|
26
77
|
export type RelayAuthorization =
|
|
27
78
|
| { static: string }
|
|
28
79
|
| { privateKeyJwt: PrivateKeyJwtConfig }
|
|
80
|
+
| { exchange: ExchangeAuthorizationConfig }
|
|
29
81
|
|
|
30
82
|
export interface RelayClientConfig {
|
|
31
83
|
baseUrl: string
|