@cubist-labs/cubesigner-sdk 0.2.17 → 0.2.21

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/src/api.ts CHANGED
@@ -1,5 +1,11 @@
1
- import createClient from "openapi-fetch";
2
- import { paths } from "./schema";
1
+ import createClient, {
2
+ FetchOptions,
3
+ FetchResponse,
4
+ FilterKeys,
5
+ HttpMethod,
6
+ PathsWith,
7
+ } from "openapi-fetch";
8
+ import { paths, operations } from "./schema";
3
9
  import {
4
10
  SignerSessionData,
5
11
  SignerSessionLifetime,
@@ -50,8 +56,9 @@ import {
50
56
  UserExportCompleteResponse,
51
57
  UserExportInitResponse,
52
58
  UserExportListResponse,
59
+ Empty,
53
60
  } from "./schema_types";
54
- import { assertOk, encodeToBase64 } from "./util";
61
+ import { encodeToBase64 } from "./util";
55
62
  import { AddFidoChallenge, MfaFidoChallenge, MfaReceipt, TotpChallenge } from "./mfa";
56
63
  import { CubeSignerResponse, mapResponse } from "./response";
57
64
  import { Key, KeyType } from "./key";
@@ -60,11 +67,174 @@ import { KeyPolicy } from "./role";
60
67
  import { EnvInterface } from "./env";
61
68
  import { NAME, VERSION } from ".";
62
69
  import { loadSubtleCrypto } from "./user_export";
70
+ import { EventEmitter } from "./events";
63
71
 
64
72
  /** @internal */
65
73
  export type Client = ReturnType<typeof createClient<paths>>;
66
74
 
67
- export { paths };
75
+ export { paths, operations };
76
+
77
+ /**
78
+ * Omit routes in {@link T} whose methods are all 'never'
79
+ */
80
+ type OmitNeverPaths<T extends paths> = {
81
+ /* eslint-disable-next-line no-unused-vars */ // 'm', but it's needed
82
+ [p in keyof T as T[p] extends { [m in keyof T[p]]: never } ? never : p]: T[p];
83
+ };
84
+
85
+ /**
86
+ * Filter out methods that don't match operation {@link Op}
87
+ */
88
+ type FilterPaths<Op extends keyof operations> = {
89
+ [p in keyof paths]: {
90
+ [m in HttpMethod as m extends keyof paths[p] ? m : never]: m extends keyof paths[p]
91
+ ? operations[Op] extends paths[p][m]
92
+ ? paths[p][m] extends operations[Op]
93
+ ? operations[Op]
94
+ : never
95
+ : never
96
+ : never;
97
+ };
98
+ };
99
+
100
+ type Paths<Op extends keyof operations> = OmitNeverPaths<FilterPaths<Op>>;
101
+
102
+ /**
103
+ * Open-fetch client restricted to the route that corresponds to operation {@link Op}
104
+ */
105
+ export type FetchClient<Op extends keyof operations> = ReturnType<typeof createClient<Paths<Op>>>;
106
+
107
+ /**
108
+ * Type alias for the type of the response body (the "data" field of
109
+ * {@link FetchResponse<T>}) when that response is successful.
110
+ */
111
+ export type FetchResponseSuccessData<T> = Required<FetchResponse<T>>["data"];
112
+
113
+ /**
114
+ * Error response type, thrown on non-successful responses.
115
+ */
116
+ export class ErrResponse extends Error {
117
+ /** Operation that produced this error */
118
+ readonly operation?: keyof operations;
119
+ /** HTTP status code text (derived from `this.status`) */
120
+ readonly statusText?: string;
121
+ /** HTTP status code */
122
+ readonly status?: number;
123
+ /** HTTP response url */
124
+ readonly url?: string;
125
+
126
+ /**
127
+ * @param {Partial<ErrResponse>} init Initializer
128
+ */
129
+ constructor(init: Partial<ErrResponse>) {
130
+ super(init.message);
131
+ Object.assign(this, init);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Wrapper around an open-fetch client restricted to a single operation.
137
+ * The restriction applies only when type checking, the actual
138
+ * client does not restrict anything at runtime.
139
+ * client does not restrict anything at runtime
140
+ */
141
+ export class OpClient<Op extends keyof operations> {
142
+ readonly #op: Op;
143
+ readonly #client: FetchClient<Op>;
144
+ readonly #eventEmitter: EventEmitter;
145
+
146
+ /**
147
+ * @param {Op} op The operation this client should be restricted to
148
+ * @param {FetchClient<Op> | Client} client open-fetch client (either restricted to {@link Op} or not)
149
+ * @param {EventEmitter} eventEmitter The client-local event dispatcher.
150
+ */
151
+ constructor(op: Op, client: FetchClient<Op> | Client, eventEmitter: EventEmitter) {
152
+ this.#op = op;
153
+ this.#client = client as FetchClient<Op>; // either works
154
+ this.#eventEmitter = eventEmitter;
155
+ }
156
+
157
+ /** The operation this client is restricted to */
158
+ get op() {
159
+ return this.#op;
160
+ }
161
+
162
+ /**
163
+ * Inspects the response and returns the response body if the request was successful.
164
+ * Otherwise, dispatches the error to event listeners, then throws {@link ErrResponse}.
165
+ *
166
+ * @param {FetchResponse<T>} resp The response to check
167
+ * @return {FetchResponseSuccessData<T>} The response data corresponding to response type {@link T}.
168
+ */
169
+ private async assertOk<T>(resp: FetchResponse<T>): Promise<FetchResponseSuccessData<T>> {
170
+ if (resp.error) {
171
+ const error = new ErrResponse({
172
+ operation: this.op,
173
+ message: (resp.error as any).message, // eslint-disable-line @typescript-eslint/no-explicit-any
174
+ statusText: resp.response?.statusText,
175
+ status: resp.response?.status,
176
+ url: resp.response?.url,
177
+ });
178
+ this.#eventEmitter.classifyAndEmitError(error);
179
+ throw error;
180
+ }
181
+ if (resp.data === undefined) {
182
+ throw new Error("Response data is undefined");
183
+ }
184
+ return resp.data;
185
+ }
186
+
187
+ /* eslint-disable valid-jsdoc */
188
+
189
+ /**
190
+ * Invoke HTTP GET
191
+ */
192
+ async get(
193
+ url: PathsWith<Paths<Op>, "get">,
194
+ init: FetchOptions<FilterKeys<Paths<Op>[PathsWith<Paths<Op>, "get">], "get">>,
195
+ ) {
196
+ const resp = await this.#client.get(url, init);
197
+ return await this.assertOk(resp);
198
+ }
199
+
200
+ /** Invoke HTTP POST */
201
+ async post(
202
+ url: PathsWith<Paths<Op>, "post">,
203
+ init: FetchOptions<FilterKeys<Paths<Op>[PathsWith<Paths<Op>, "post">], "post">>,
204
+ ) {
205
+ const resp = await this.#client.post(url, init);
206
+ return await this.assertOk(resp);
207
+ }
208
+
209
+ /** Invoke HTTP PATCH */
210
+ async patch(
211
+ url: PathsWith<Paths<Op>, "patch">,
212
+ init: FetchOptions<FilterKeys<Paths<Op>[PathsWith<Paths<Op>, "patch">], "patch">>,
213
+ ) {
214
+ const resp = await this.#client.patch(url, init);
215
+ return await this.assertOk(resp);
216
+ }
217
+
218
+ /** Invoke HTTP DELETE */
219
+ async del(
220
+ url: PathsWith<Paths<Op>, "delete">,
221
+ init: FetchOptions<FilterKeys<Paths<Op>[PathsWith<Paths<Op>, "delete">], "delete">>,
222
+ ) {
223
+ const resp = await this.#client.del(url, init);
224
+ return await this.assertOk(resp);
225
+ }
226
+
227
+ /** Invoke HTTP PUT */
228
+ async put(
229
+ url: PathsWith<Paths<Op>, "put">,
230
+ init: FetchOptions<FilterKeys<Paths<Op>[PathsWith<Paths<Op>, "put">], "put">>,
231
+ ) {
232
+ const resp = await this.#client.put(url, init);
233
+ return await this.assertOk(resp);
234
+ }
235
+
236
+ /* eslint-enable valid-jsdoc */
237
+ }
68
238
 
69
239
  /**
70
240
  * Creates a new HTTP client, setting the "User-Agent" header to this package's {name}@{version}.
@@ -90,6 +260,7 @@ export function createHttpClient(baseUrl: string, authToken: string): Client {
90
260
  export class CubeSignerApi {
91
261
  readonly #orgId: string;
92
262
  readonly #sessionMgr: SignerSessionManager;
263
+ readonly #eventEmitter: EventEmitter;
93
264
 
94
265
  /** Underlying session manager */
95
266
  get sessionMgr(): SignerSessionManager {
@@ -108,6 +279,7 @@ export class CubeSignerApi {
108
279
  */
109
280
  constructor(sessionMgr: SignerSessionManager, orgId?: string) {
110
281
  this.#sessionMgr = sessionMgr;
282
+ this.#eventEmitter = new EventEmitter([sessionMgr.events]);
111
283
  this.#orgId = orgId ?? sessionMgr.orgId;
112
284
  }
113
285
 
@@ -126,7 +298,19 @@ export class CubeSignerApi {
126
298
  return this.#orgId;
127
299
  }
128
300
 
129
- // #region USERS: userGet, userResetTotp(Init|Complete), userVerifyTotp, userRegisterFido(Init|Complete)
301
+ /**
302
+ * HTTP client restricted to a single operation. The restriction applies only
303
+ * when type checking, the actual client does not restrict anything at runtime.
304
+ *
305
+ * @param {Op} op The operation to restrict the client to
306
+ * @return {Promise<OpClient<Op>>} The client restricted to {@link op}
307
+ */
308
+ private async client<Op extends keyof operations>(op: Op): Promise<OpClient<Op>> {
309
+ const fetchClient = await this.#sessionMgr.client();
310
+ return new OpClient(op, fetchClient, this.#eventEmitter);
311
+ }
312
+
313
+ // #region USERS: userGet, userTotp(ResetInit|ResetComplete|Verify|Delete), userFido(RegisterInit|RegisterComplete|Delete)
130
314
 
131
315
  /**
132
316
  * Obtain information about the current user.
@@ -134,32 +318,32 @@ export class CubeSignerApi {
134
318
  * @return {Promise<UserInfo>} Retrieves information about the current user.
135
319
  */
136
320
  async userGet(): Promise<UserInfo> {
137
- const client = await this.client();
138
- const resp =
139
- `${this.orgId}` !== "undefined"
140
- ? await client.get("/v0/org/{org_id}/user/me", {
141
- params: { path: { org_id: this.orgId } },
142
- parseAs: "json",
143
- })
144
- : await client.get("/v0/about_me", { parseAs: "json" });
145
- return assertOk(resp);
321
+ if (`${this.orgId}` === "undefined") {
322
+ const client = await this.client("aboutMeLegacy");
323
+ return await client.get("/v0/about_me", {});
324
+ } else {
325
+ const client = await this.client("aboutMe");
326
+ return await client.get("/v0/org/{org_id}/user/me", {
327
+ params: { path: { org_id: this.orgId } },
328
+ });
329
+ }
146
330
  }
147
331
 
148
332
  /**
149
333
  * Creates a request to change user's TOTP. Returns a {@link TotpChallenge}
150
334
  * that must be answered either by calling {@link TotpChallenge.answer} (or
151
- * {@link CubeSignerApi.userResetTotpComplete}).
335
+ * {@link CubeSignerApi.userTotpResetComplete}).
152
336
  *
153
337
  * @param {string} issuer Optional issuer; defaults to "Cubist"
154
338
  * @param {MfaReceipt} mfaReceipt MFA receipt to include in HTTP headers
155
339
  */
156
- async userResetTotpInit(
340
+ async userTotpResetInit(
157
341
  issuer?: string,
158
342
  mfaReceipt?: MfaReceipt,
159
343
  ): Promise<CubeSignerResponse<TotpChallenge>> {
160
344
  const resetTotpFn = async (headers?: HeadersInit) => {
161
- const client = await this.client();
162
- const resp = await client.post("/v0/org/{org_id}/user/me/totp", {
345
+ const client = await this.client("userResetTotpInit");
346
+ const data = await client.post("/v0/org/{org_id}/user/me/totp", {
163
347
  headers,
164
348
  params: { path: { org_id: this.orgId } },
165
349
  body: issuer
@@ -167,16 +351,14 @@ export class CubeSignerApi {
167
351
  issuer,
168
352
  }
169
353
  : null,
170
- parseAs: "json",
171
354
  });
172
- const data = assertOk(resp);
173
355
  return mapResponse(data, (totpInfo) => new TotpChallenge(this, totpInfo));
174
356
  };
175
357
  return await CubeSignerResponse.create(resetTotpFn, mfaReceipt);
176
358
  }
177
359
 
178
360
  /**
179
- * Answer the TOTP challenge issued by {@link userResetTotpInit}. If successful, user's
361
+ * Answer the TOTP challenge issued by {@link userTotpResetInit}. If successful, user's
180
362
  * TOTP configuration will be updated to that of the TOTP challenge.
181
363
  *
182
364
  * Instead of calling this method directly, prefer {@link TotpChallenge.answer}.
@@ -184,14 +366,12 @@ export class CubeSignerApi {
184
366
  * @param {string} totpId - The ID of the TOTP challenge
185
367
  * @param {string} code - The TOTP code that should verify against the TOTP configuration from the challenge.
186
368
  */
187
- async userResetTotpComplete(totpId: string, code: string): Promise<void> {
188
- const client = await this.client();
189
- const resp = await client.patch("/v0/org/{org_id}/user/me/totp", {
190
- parseAs: "json",
369
+ async userTotpResetComplete(totpId: string, code: string): Promise<void> {
370
+ const client = await this.client("userResetTotpComplete");
371
+ await client.patch("/v0/org/{org_id}/user/me/totp", {
191
372
  params: { path: { org_id: this.orgId } },
192
373
  body: { totp_id: totpId, code },
193
374
  });
194
- assertOk(resp);
195
375
  }
196
376
 
197
377
  /**
@@ -200,45 +380,60 @@ export class CubeSignerApi {
200
380
  *
201
381
  * @param {string} code Current TOTP code
202
382
  */
203
- async userVerifyTotp(code: string) {
204
- const client = await this.client();
205
- const resp = await client.post("/v0/org/{org_id}/user/me/totp/verify", {
383
+ async userTotpVerify(code: string) {
384
+ const client = await this.client("userVerifyTotp");
385
+ await client.post("/v0/org/{org_id}/user/me/totp/verify", {
206
386
  params: { path: { org_id: this.orgId } },
207
387
  body: { code },
208
- parseAs: "json",
209
388
  });
210
- assertOk(resp);
389
+ }
390
+
391
+ /**
392
+ * Delete TOTP from the user's account.
393
+ * Allowed only if at least one FIDO key is registered with the user's account.
394
+ * MFA via FIDO is always required.
395
+ *
396
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt to include in HTTP headers
397
+ */
398
+ async userTotpDelete(mfaReceipt?: MfaReceipt): Promise<CubeSignerResponse<Empty>> {
399
+ const deleteTotpFn = async (headers?: HeadersInit) => {
400
+ const client = await this.client("userDeleteTotp");
401
+ return await client.del("/v0/org/{org_id}/user/me/totp", {
402
+ headers,
403
+ params: { path: { org_id: this.orgId } },
404
+ body: null,
405
+ });
406
+ };
407
+ return await CubeSignerResponse.create(deleteTotpFn, mfaReceipt);
211
408
  }
212
409
 
213
410
  /**
214
411
  * Initiate adding a new FIDO device. MFA may be required. This returns a {@link AddFidoChallenge}
215
- * that must be answered with {@link AddFidoChallenge.answer} or {@link userRegisterFidoComplete}
412
+ * that must be answered with {@link AddFidoChallenge.answer} or {@link userFidoRegisterComplete}
216
413
  * (after MFA approvals).
217
414
  *
218
415
  * @param {string} name The name of the new device.
219
416
  * @param {MfaReceipt} mfaReceipt Optional MFA receipt to include in HTTP headers
220
417
  * @return {Promise<CubeSignerResponse<AddFidoChallenge>>} A challenge that must be answered in order to complete FIDO registration.
221
418
  */
222
- async userRegisterFidoInit(
419
+ async userFidoRegisterInit(
223
420
  name: string,
224
421
  mfaReceipt?: MfaReceipt,
225
422
  ): Promise<CubeSignerResponse<AddFidoChallenge>> {
226
423
  const addFidoFn = async (headers?: HeadersInit) => {
227
- const client = await this.client();
228
- const resp = await client.post("/v0/org/{org_id}/user/me/fido", {
424
+ const client = await this.client("userRegisterFidoInit");
425
+ const data = await client.post("/v0/org/{org_id}/user/me/fido", {
229
426
  headers,
230
427
  params: { path: { org_id: this.orgId } },
231
428
  body: { name },
232
- parseAs: "json",
233
429
  });
234
- const data = assertOk(resp);
235
430
  return mapResponse(data, (c) => new AddFidoChallenge(this, c));
236
431
  };
237
432
  return await CubeSignerResponse.create(addFidoFn, mfaReceipt);
238
433
  }
239
434
 
240
435
  /**
241
- * Complete a previously initiated (via {@link userRegisterFidoInit}) request to add a new FIDO device.
436
+ * Complete a previously initiated (via {@link userFidoRegisterInit}) request to add a new FIDO device.
242
437
  *
243
438
  * Instead of calling this method directly, prefer {@link AddFidoChallenge.answer} or
244
439
  * {@link AddFidoChallenge.createCredentialAndAnswer}.
@@ -246,17 +441,38 @@ export class CubeSignerApi {
246
441
  * @param {string} challengeId The ID of the challenge returned by the remote end.
247
442
  * @param {PublicKeyCredential} credential The answer to the challenge.
248
443
  */
249
- async userRegisterFidoComplete(challengeId: string, credential: PublicKeyCredential) {
250
- const client = await this.client();
251
- const resp = await client.patch("/v0/org/{org_id}/user/me/fido", {
444
+ async userFidoRegisterComplete(challengeId: string, credential: PublicKeyCredential) {
445
+ const client = await this.client("userRegisterFidoComplete");
446
+ await client.patch("/v0/org/{org_id}/user/me/fido", {
252
447
  params: { path: { org_id: this.orgId } },
253
448
  body: {
254
449
  challenge_id: challengeId,
255
450
  credential,
256
451
  },
257
- parseAs: "json",
258
452
  });
259
- assertOk(resp);
453
+ }
454
+
455
+ /**
456
+ * Delete a FIDO key from the user's account.
457
+ * Allowed only if TOTP is also defined.
458
+ * MFA via TOTP is always required.
459
+ *
460
+ * @param {string} fidoId The ID of the desired FIDO key
461
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt to include in HTTP headers
462
+ */
463
+ async userFidoDelete(
464
+ fidoId: string,
465
+ mfaReceipt?: MfaReceipt,
466
+ ): Promise<CubeSignerResponse<Empty>> {
467
+ const deleteFidoFn = async (headers?: HeadersInit) => {
468
+ const client = await this.client("userDeleteFido");
469
+ return await client.del("/v0/org/{org_id}/user/me/fido/{fido_id}", {
470
+ headers,
471
+ params: { path: { org_id: this.orgId, fido_id: fidoId } },
472
+ body: null,
473
+ });
474
+ };
475
+ return await CubeSignerResponse.create(deleteFidoFn, mfaReceipt);
260
476
  }
261
477
 
262
478
  // #endregion
@@ -268,12 +484,10 @@ export class CubeSignerApi {
268
484
  * @return {OrgInfo} Information about the organization.
269
485
  */
270
486
  async orgGet(): Promise<OrgInfo> {
271
- const client = await this.client();
272
- const resp = await client.get("/v0/org/{org_id}", {
487
+ const client = await this.client("getOrg");
488
+ return await client.get("/v0/org/{org_id}", {
273
489
  params: { path: { org_id: this.orgId } },
274
- parseAs: "json",
275
490
  });
276
- return assertOk(resp);
277
491
  }
278
492
 
279
493
  /**
@@ -282,13 +496,11 @@ export class CubeSignerApi {
282
496
  * @return {UpdateOrgResponse} Updated org information.
283
497
  */
284
498
  async orgUpdate(request: UpdateOrgRequest): Promise<UpdateOrgResponse> {
285
- const client = await this.client();
286
- const resp = await client.patch("/v0/org/{org_id}", {
499
+ const client = await this.client("updateOrg");
500
+ return await client.patch("/v0/org/{org_id}", {
287
501
  params: { path: { org_id: this.orgId } },
288
502
  body: request,
289
- parseAs: "json",
290
503
  });
291
- return assertOk(resp);
292
504
  }
293
505
 
294
506
  // #endregion
@@ -303,8 +515,8 @@ export class CubeSignerApi {
303
515
  * @param {MemberRole} role Optional role. Defaults to "alien".
304
516
  */
305
517
  async orgUserInvite(email: string, name: string, role?: MemberRole): Promise<void> {
306
- const client = await this.client();
307
- const resp = await client.post("/v0/org/{org_id}/invite", {
518
+ const client = await this.client("invite");
519
+ await client.post("/v0/org/{org_id}/invite", {
308
520
  params: { path: { org_id: this.orgId } },
309
521
  body: {
310
522
  email,
@@ -312,9 +524,7 @@ export class CubeSignerApi {
312
524
  role,
313
525
  skip_email: false,
314
526
  },
315
- parseAs: "json",
316
527
  });
317
- assertOk(resp);
318
528
  }
319
529
 
320
530
  /**
@@ -322,13 +532,11 @@ export class CubeSignerApi {
322
532
  * @return {User[]} Org users.
323
533
  */
324
534
  async orgUsersList(): Promise<UserIdInfo[]> {
325
- const client = await this.client();
535
+ const client = await this.client("listUsersInOrg");
326
536
  const resp = await client.get("/v0/org/{org_id}/users", {
327
537
  params: { path: { org_id: this.orgId } },
328
- parseAs: "json",
329
538
  });
330
- const data = assertOk(resp);
331
- return data.users;
539
+ return resp.users;
332
540
  }
333
541
 
334
542
  /**
@@ -343,8 +551,8 @@ export class CubeSignerApi {
343
551
  email: string,
344
552
  opts: CreateOidcUserOptions = {},
345
553
  ): Promise<string> {
346
- const client = await this.client();
347
- const resp = await client.post("/v0/org/{org_id}/users", {
554
+ const client = await this.client("createOidcUser");
555
+ const data = await client.post("/v0/org/{org_id}/users", {
348
556
  params: { path: { org_id: this.orgId } },
349
557
  body: {
350
558
  identity,
@@ -352,9 +560,8 @@ export class CubeSignerApi {
352
560
  email: email,
353
561
  mfa_policy: opts.mfaPolicy ?? null,
354
562
  },
355
- parseAs: "json",
356
563
  });
357
- return assertOk(resp).user_id;
564
+ return data.user_id;
358
565
  }
359
566
 
360
567
  /**
@@ -362,13 +569,11 @@ export class CubeSignerApi {
362
569
  * @param {OidcIdentity} identity The identity of the OIDC user
363
570
  */
364
571
  async orgUserDeleteOidc(identity: OidcIdentity) {
365
- const client = await this.client();
366
- const resp = await client.del("/v0/org/{org_id}/users/oidc", {
572
+ const client = await this.client("deleteOidcUser");
573
+ return await client.del("/v0/org/{org_id}/users/oidc", {
367
574
  params: { path: { org_id: this.orgId } },
368
575
  body: identity,
369
- parseAs: "json",
370
576
  });
371
- return assertOk(resp);
372
577
  }
373
578
 
374
579
  // #endregion
@@ -382,12 +587,10 @@ export class CubeSignerApi {
382
587
  * @return {KeyInfoApi} The key information.
383
588
  */
384
589
  async keyGet(keyId: string): Promise<KeyInfoApi> {
385
- const client = await this.client();
386
- const resp = await client.get("/v0/org/{org_id}/keys/{key_id}", {
590
+ const client = await this.client("getKeyInOrg");
591
+ return await client.get("/v0/org/{org_id}/keys/{key_id}", {
387
592
  params: { path: { org_id: this.orgId, key_id: keyId } },
388
- parseAs: "json",
389
593
  });
390
- return assertOk(resp);
391
594
  }
392
595
 
393
596
  /**
@@ -397,13 +600,11 @@ export class CubeSignerApi {
397
600
  * @return {KeyInfoApi} The JSON response from the API server.
398
601
  */
399
602
  async keyUpdate(keyId: string, request: UpdateKeyRequest): Promise<KeyInfoApi> {
400
- const client = await this.client();
401
- const resp = await client.patch("/v0/org/{org_id}/keys/{key_id}", {
603
+ const client = await this.client("updateKey");
604
+ return await client.patch("/v0/org/{org_id}/keys/{key_id}", {
402
605
  params: { path: { org_id: this.orgId, key_id: keyId } },
403
606
  body: request,
404
- parseAs: "json",
405
607
  });
406
- return assertOk(resp);
407
608
  }
408
609
 
409
610
  /**
@@ -412,12 +613,10 @@ export class CubeSignerApi {
412
613
  * @param {string} keyId - Key id
413
614
  */
414
615
  async keyDelete(keyId: string) {
415
- const client = await this.client();
416
- const resp = await client.del("/v0/org/{org_id}/keys/{key_id}", {
616
+ const client = await this.client("deleteKey");
617
+ await client.del("/v0/org/{org_id}/keys/{key_id}", {
417
618
  params: { path: { org_id: this.orgId, key_id: keyId } },
418
- parseAs: "json",
419
619
  });
420
- assertOk(resp);
421
620
  }
422
621
 
423
622
  /**
@@ -430,8 +629,8 @@ export class CubeSignerApi {
430
629
  */
431
630
  async keysCreate(keyType: KeyType, count: number, ownerId?: string): Promise<KeyInfoApi[]> {
432
631
  const chain_id = 0; // not used anymore
433
- const client = await this.client();
434
- const resp = await client.post("/v0/org/{org_id}/keys", {
632
+ const client = await this.client("createKey");
633
+ const data = await client.post("/v0/org/{org_id}/keys", {
435
634
  params: { path: { org_id: this.orgId } },
436
635
  body: {
437
636
  count,
@@ -439,9 +638,7 @@ export class CubeSignerApi {
439
638
  key_type: keyType,
440
639
  owner: ownerId || null,
441
640
  },
442
- parseAs: "json",
443
641
  });
444
- const data = assertOk(resp);
445
642
  return data.keys;
446
643
  }
447
644
 
@@ -461,17 +658,16 @@ export class CubeSignerApi {
461
658
  derivationPaths: string[],
462
659
  mnemonicId: string,
463
660
  ): Promise<KeyInfoApi[]> {
464
- const client = await this.client();
465
- const resp = await client.put("/v0/org/{org_id}/derive_key", {
661
+ const client = await this.client("deriveKey");
662
+ const data = await client.put("/v0/org/{org_id}/derive_key", {
466
663
  params: { path: { org_id: this.orgId } },
467
664
  body: {
468
665
  derivation_path: derivationPaths,
469
666
  mnemonic_id: mnemonicId,
470
667
  key_type: keyType,
471
668
  },
472
- parseAs: "json",
473
669
  });
474
- return assertOk(resp).keys;
670
+ return data.keys;
475
671
  }
476
672
 
477
673
  /**
@@ -482,8 +678,8 @@ export class CubeSignerApi {
482
678
  */
483
679
  keysList(type?: KeyType, page?: PageOpts): Paginator<ListKeysResponse, KeyInfoApi> {
484
680
  const listFn = async (query: PageQueryArgs) => {
485
- const client = await this.client();
486
- const resp = await client.get("/v0/org/{org_id}/keys", {
681
+ const client = await this.client("listKeysInOrg");
682
+ return await client.get("/v0/org/{org_id}/keys", {
487
683
  params: {
488
684
  path: { org_id: this.orgId },
489
685
  query: {
@@ -491,9 +687,7 @@ export class CubeSignerApi {
491
687
  ...query,
492
688
  },
493
689
  },
494
- parseAs: "json",
495
690
  });
496
- return assertOk(resp);
497
691
  };
498
692
  return new Paginator(
499
693
  page ?? Page.default(),
@@ -513,13 +707,12 @@ export class CubeSignerApi {
513
707
  * @return {string} The ID of the new role.
514
708
  */
515
709
  async roleCreate(name?: string): Promise<string> {
516
- const client = await this.client();
517
- const resp = await client.post("/v0/org/{org_id}/roles", {
710
+ const client = await this.client("createRole");
711
+ const data = await client.post("/v0/org/{org_id}/roles", {
518
712
  params: { path: { org_id: this.orgId } },
519
713
  body: name ? { name } : undefined,
520
- parseAs: "json",
521
714
  });
522
- return assertOk(resp).role_id;
715
+ return data.role_id;
523
716
  }
524
717
 
525
718
  /**
@@ -528,12 +721,10 @@ export class CubeSignerApi {
528
721
  * @return {RoleInfo} The role.
529
722
  */
530
723
  async roleGet(roleId: string): Promise<RoleInfo> {
531
- const client = await this.client();
532
- const resp = await client.get("/v0/org/{org_id}/roles/{role_id}", {
724
+ const client = await this.client("getRole");
725
+ return await client.get("/v0/org/{org_id}/roles/{role_id}", {
533
726
  params: { path: { org_id: this.orgId, role_id: roleId } },
534
- parseAs: "json",
535
727
  });
536
- return assertOk(resp);
537
728
  }
538
729
 
539
730
  /**
@@ -544,13 +735,11 @@ export class CubeSignerApi {
544
735
  * @return {Promise<RoleInfo>} The updated role information.
545
736
  */
546
737
  async roleUpdate(roleId: string, request: UpdateRoleRequest): Promise<RoleInfo> {
547
- const client = await this.client();
548
- const resp = await client.patch("/v0/org/{org_id}/roles/{role_id}", {
738
+ const client = await this.client("updateRole");
739
+ return await client.patch("/v0/org/{org_id}/roles/{role_id}", {
549
740
  params: { path: { org_id: this.orgId, role_id: roleId } },
550
741
  body: request,
551
- parseAs: "json",
552
742
  });
553
- return assertOk(resp);
554
743
  }
555
744
 
556
745
  /**
@@ -559,12 +748,10 @@ export class CubeSignerApi {
559
748
  * @param {string} roleId The ID of the role to delete.
560
749
  */
561
750
  async roleDelete(roleId: string): Promise<void> {
562
- const client = await this.client();
563
- const resp = await client.del("/v0/org/{org_id}/roles/{role_id}", {
751
+ const client = await this.client("deleteRole");
752
+ await client.del("/v0/org/{org_id}/roles/{role_id}", {
564
753
  params: { path: { org_id: this.orgId, role_id: roleId } },
565
- parseAs: "json",
566
754
  });
567
- assertOk(resp);
568
755
  }
569
756
 
570
757
  /**
@@ -575,15 +762,13 @@ export class CubeSignerApi {
575
762
  */
576
763
  rolesList(page?: PageOpts): Paginator<ListRolesResponse, RoleInfo> {
577
764
  const listFn = async (query: PageQueryArgs) => {
578
- const client = await this.client();
579
- const resp = await client.get("/v0/org/{org_id}/roles", {
765
+ const client = await this.client("listRoles");
766
+ return await client.get("/v0/org/{org_id}/roles", {
580
767
  params: {
581
768
  path: { org_id: this.orgId },
582
769
  query,
583
770
  },
584
- parseAs: "json",
585
771
  });
586
- return assertOk(resp);
587
772
  };
588
773
  return new Paginator(
589
774
  page ?? Page.default(),
@@ -605,16 +790,14 @@ export class CubeSignerApi {
605
790
  * @param {KeyPolicy?} policy The optional policy to apply to each key.
606
791
  */
607
792
  async roleKeysAdd(roleId: string, keyIds: string[], policy?: KeyPolicy) {
608
- const client = await this.client();
609
- const resp = await client.put("/v0/org/{org_id}/roles/{role_id}/add_keys", {
793
+ const client = await this.client("addKeysToRole");
794
+ await client.put("/v0/org/{org_id}/roles/{role_id}/add_keys", {
610
795
  params: { path: { org_id: this.#orgId, role_id: roleId } },
611
796
  body: {
612
797
  key_ids: keyIds,
613
798
  policy: (policy ?? null) as Record<string, never>[] | null,
614
799
  },
615
- parseAs: "json",
616
800
  });
617
- assertOk(resp, "Failed to add keys to role");
618
801
  }
619
802
 
620
803
  /**
@@ -624,12 +807,10 @@ export class CubeSignerApi {
624
807
  * @param {string} keyId The ID of the key to remove from the role
625
808
  */
626
809
  async roleKeysRemove(roleId: string, keyId: string) {
627
- const client = await this.client();
628
- const resp = await client.del("/v0/org/{org_id}/roles/{role_id}/keys/{key_id}", {
810
+ const client = await this.client("removeKeyFromRole");
811
+ await client.del("/v0/org/{org_id}/roles/{role_id}/keys/{key_id}", {
629
812
  params: { path: { org_id: this.#orgId, role_id: roleId, key_id: keyId } },
630
- parseAs: "json",
631
813
  });
632
- assertOk(resp, "Failed to remove key from a role");
633
814
  }
634
815
 
635
816
  /**
@@ -641,15 +822,13 @@ export class CubeSignerApi {
641
822
  */
642
823
  roleKeysList(roleId: string, page?: PageOpts): Paginator<ListRoleKeysResponse, KeyInRoleInfo> {
643
824
  const listFn = async (query: PageQueryArgs) => {
644
- const client = await this.client();
645
- const resp = await client.get("/v0/org/{org_id}/roles/{role_id}/keys", {
825
+ const client = await this.client("listRoleKeys");
826
+ return await client.get("/v0/org/{org_id}/roles/{role_id}/keys", {
646
827
  params: {
647
828
  path: { org_id: this.orgId, role_id: roleId },
648
829
  query,
649
830
  },
650
- parseAs: "json",
651
831
  });
652
- return assertOk(resp);
653
832
  };
654
833
  return new Paginator(
655
834
  page ?? Page.default(),
@@ -670,12 +849,10 @@ export class CubeSignerApi {
670
849
  * @param {string} userId The ID of the user to add to the role.
671
850
  */
672
851
  async roleUserAdd(roleId: string, userId: string) {
673
- const client = await this.client();
674
- const resp = await client.put("/v0/org/{org_id}/roles/{role_id}/add_user/{user_id}", {
852
+ const client = await this.client("addUserToRole");
853
+ await client.put("/v0/org/{org_id}/roles/{role_id}/add_user/{user_id}", {
675
854
  params: { path: { org_id: this.#orgId, role_id: roleId, user_id: userId } },
676
- parseAs: "json",
677
855
  });
678
- assertOk(resp, "Failed to add user to role");
679
856
  }
680
857
 
681
858
  /**
@@ -687,15 +864,13 @@ export class CubeSignerApi {
687
864
  */
688
865
  roleUsersList(roleId: string, page?: PageOpts): Paginator<ListRoleUsersResponse, UserInRoleInfo> {
689
866
  const listFn = async (query: PageQueryArgs) => {
690
- const client = await this.client();
691
- const resp = await client.get("/v0/org/{org_id}/roles/{role_id}/users", {
867
+ const client = await this.client("listRoleUsers");
868
+ return await client.get("/v0/org/{org_id}/roles/{role_id}/users", {
692
869
  params: {
693
870
  path: { org_id: this.orgId, role_id: roleId },
694
871
  query,
695
872
  },
696
- parseAs: "json",
697
873
  });
698
- return assertOk(resp);
699
874
  };
700
875
  return new Paginator(
701
876
  page ?? Page.default(),
@@ -707,7 +882,46 @@ export class CubeSignerApi {
707
882
 
708
883
  // #endregion
709
884
 
710
- // #region SESSIONS: sessionCreateForRole, sessionRefresh, sessionRevoke, sessionsList, sessionKeysList
885
+ // #region SESSIONS: session(Create|CreateForRole|Refresh|Revoke|List|KeysList)
886
+
887
+ /**
888
+ * Create new user session (management and/or signing)
889
+ *
890
+ * @param {string} purpose The purpose of the session
891
+ * @param {string[]} scopes Session scopes.
892
+ * @param {SignerSessionLifetime} lifetimes Lifetime settings
893
+ * @return {Promise<SignerSessionData>} New signer session info.
894
+ */
895
+ async sessionCreate(
896
+ purpose: string,
897
+ scopes: string[],
898
+ lifetimes?: SignerSessionLifetime,
899
+ ): Promise<SignerSessionData> {
900
+ lifetimes ??= defaultSignerSessionLifetime;
901
+ const client = await this.client("createSession");
902
+ const data = await client.post("/v0/org/{org_id}/session", {
903
+ params: { path: { org_id: this.orgId } },
904
+ body: {
905
+ purpose,
906
+ scopes,
907
+ auth_lifetime: lifetimes.auth,
908
+ refresh_lifetime: lifetimes.refresh,
909
+ session_lifetime: lifetimes.session,
910
+ grace_lifetime: lifetimes.grace,
911
+ },
912
+ });
913
+ return {
914
+ org_id: this.orgId,
915
+ role_id: undefined,
916
+ purpose,
917
+ token: data.token,
918
+ session_info: data.session_info,
919
+ // Keep compatibility with tokens produced by CLI
920
+ env: {
921
+ ["Dev-CubeSignerStack"]: this.#sessionMgr.env,
922
+ },
923
+ };
924
+ }
711
925
 
712
926
  /**
713
927
  * Create a new signer session for a given role.
@@ -730,8 +944,8 @@ export class CubeSignerApi {
730
944
  throw new Error(`Role scopes must start with 'sign:'; invalid scopes: ${invalidScopes}`);
731
945
  }
732
946
 
733
- const client = await this.client();
734
- const resp = await client.post("/v0/org/{org_id}/roles/{role_id}/tokens", {
947
+ const client = await this.client("createRoleToken");
948
+ const data = await client.post("/v0/org/{org_id}/roles/{role_id}/tokens", {
735
949
  params: { path: { org_id: this.orgId, role_id: roleId } },
736
950
  body: {
737
951
  purpose,
@@ -741,9 +955,7 @@ export class CubeSignerApi {
741
955
  session_lifetime: lifetimes.session,
742
956
  grace_lifetime: lifetimes.grace,
743
957
  },
744
- parseAs: "json",
745
958
  });
746
- const data = assertOk(resp);
747
959
  return {
748
960
  org_id: this.orgId,
749
961
  role_id: roleId,
@@ -763,12 +975,10 @@ export class CubeSignerApi {
763
975
  * @param {string} sessionId The ID of the session to revoke.
764
976
  */
765
977
  async sessionRevoke(sessionId: string) {
766
- const client = await this.client();
767
- const resp = await client.del("/v0/org/{org_id}/session/{session_id}", {
978
+ const client = await this.client("revokeSession");
979
+ await client.del("/v0/org/{org_id}/session/{session_id}", {
768
980
  params: { path: { org_id: this.orgId, session_id: sessionId } },
769
- parseAs: "json",
770
981
  });
771
- assertOk(resp);
772
982
  }
773
983
 
774
984
  /**
@@ -780,15 +990,13 @@ export class CubeSignerApi {
780
990
  */
781
991
  sessionsList(roleId?: string, page?: PageOpts): Paginator<SessionsResponse, SessionInfo> {
782
992
  const listFn = async (query: PageQueryArgs) => {
783
- const client = await this.client();
784
- const resp = await client.get("/v0/org/{org_id}/session", {
993
+ const client = await this.client("listSessions");
994
+ return await client.get("/v0/org/{org_id}/session", {
785
995
  params: {
786
996
  path: { org_id: this.#orgId },
787
997
  query: { role: roleId, ...query },
788
998
  },
789
- parseAs: "json",
790
999
  });
791
- return assertOk(resp);
792
1000
  };
793
1001
  return new Paginator(
794
1002
  page ?? Page.default(),
@@ -803,12 +1011,11 @@ export class CubeSignerApi {
803
1011
  * @return {Key[]} The list of keys.
804
1012
  */
805
1013
  async sessionKeysList(): Promise<KeyInfoApi[]> {
806
- const client = await this.client();
1014
+ const client = await this.client("listTokenKeys");
807
1015
  const resp = await client.get("/v0/org/{org_id}/token/keys", {
808
1016
  params: { path: { org_id: this.orgId } },
809
- parseAs: "json",
810
1017
  });
811
- return assertOk(resp).keys;
1018
+ return resp.keys;
812
1019
  }
813
1020
 
814
1021
  // #endregion
@@ -821,12 +1028,10 @@ export class CubeSignerApi {
821
1028
  * @return {Promise<IdentityProof>} Proof of authentication
822
1029
  */
823
1030
  async identityProve(): Promise<IdentityProof> {
824
- const client = await this.client();
825
- const resp = await client.post("/v0/org/{org_id}/identity/prove", {
1031
+ const client = await this.client("createProofCubeSigner");
1032
+ return await client.post("/v0/org/{org_id}/identity/prove", {
826
1033
  params: { path: { org_id: this.orgId } },
827
- parseAs: "json",
828
1034
  });
829
- return assertOk(resp);
830
1035
  }
831
1036
 
832
1037
  /**
@@ -835,13 +1040,11 @@ export class CubeSignerApi {
835
1040
  * @param {IdentityProof} proof The proof of authentication.
836
1041
  */
837
1042
  async identityVerify(proof: IdentityProof) {
838
- const client = await this.client();
839
- const resp = await client.post("/v0/org/{org_id}/identity/verify", {
1043
+ const client = await this.client("verifyProof");
1044
+ await client.post("/v0/org/{org_id}/identity/verify", {
840
1045
  params: { path: { org_id: this.orgId } },
841
1046
  body: proof,
842
- parseAs: "json",
843
1047
  });
844
- assertOk(resp);
845
1048
  }
846
1049
 
847
1050
  // #endregion
@@ -855,11 +1058,10 @@ export class CubeSignerApi {
855
1058
  * @return {Promise<MfaRequestInfo>} MFA request information
856
1059
  */
857
1060
  async mfaGet(mfaId: string): Promise<MfaRequestInfo> {
858
- const client = await this.client();
859
- const resp = await client.get("/v0/org/{org_id}/mfa/{mfa_id}", {
1061
+ const client = await this.client("mfaGet");
1062
+ return await client.get("/v0/org/{org_id}/mfa/{mfa_id}", {
860
1063
  params: { path: { org_id: this.orgId, mfa_id: mfaId } },
861
1064
  });
862
- return assertOk(resp);
863
1065
  }
864
1066
 
865
1067
  /**
@@ -868,11 +1070,11 @@ export class CubeSignerApi {
868
1070
  * @return {Promise<MfaRequestInfo[]>} The MFA requests.
869
1071
  */
870
1072
  async mfaList(): Promise<MfaRequestInfo[]> {
871
- const client = await this.client();
1073
+ const client = await this.client("mfaList");
872
1074
  const resp = await client.get("/v0/org/{org_id}/mfa", {
873
1075
  params: { path: { org_id: this.orgId } },
874
1076
  });
875
- return assertOk(resp).mfa_requests;
1077
+ return resp.mfa_requests;
876
1078
  }
877
1079
 
878
1080
  /**
@@ -882,11 +1084,10 @@ export class CubeSignerApi {
882
1084
  * @return {Promise<MfaRequestInfo>} The result of the MFA request
883
1085
  */
884
1086
  async mfaApprove(mfaId: string): Promise<MfaRequestInfo> {
885
- const client = await this.client();
886
- const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}", {
1087
+ const client = await this.client("mfaApproveCs");
1088
+ return await client.patch("/v0/org/{org_id}/mfa/{mfa_id}", {
887
1089
  params: { path: { org_id: this.orgId, mfa_id: mfaId } },
888
1090
  });
889
- return assertOk(resp);
890
1091
  }
891
1092
 
892
1093
  /**
@@ -897,13 +1098,11 @@ export class CubeSignerApi {
897
1098
  * @return {Promise<MfaRequestInfo>} The current status of the MFA request
898
1099
  */
899
1100
  async mfaApproveTotp(mfaId: string, code: string): Promise<MfaRequestInfo> {
900
- const client = await this.client();
901
- const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/totp", {
1101
+ const client = await this.client("mfaApproveTotp");
1102
+ return await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/totp", {
902
1103
  params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
903
1104
  body: { code },
904
- parseAs: "json",
905
1105
  });
906
- return assertOk(resp);
907
1106
  }
908
1107
 
909
1108
  /**
@@ -914,12 +1113,10 @@ export class CubeSignerApi {
914
1113
  * @return {Promise<MfaFidoChallenge>} A challenge that needs to be answered to complete the approval.
915
1114
  */
916
1115
  async mfaApproveFidoInit(mfaId: string): Promise<MfaFidoChallenge> {
917
- const client = await this.client();
918
- const resp = await client.post("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
1116
+ const client = await this.client("mfaApproveFido");
1117
+ const challenge = await client.post("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
919
1118
  params: { path: { org_id: this.orgId, mfa_id: mfaId } },
920
- parseAs: "json",
921
1119
  });
922
- const challenge = assertOk(resp);
923
1120
  return new MfaFidoChallenge(this, mfaId, challenge);
924
1121
  }
925
1122
 
@@ -939,16 +1136,14 @@ export class CubeSignerApi {
939
1136
  challengeId: string,
940
1137
  credential: PublicKeyCredential,
941
1138
  ): Promise<MfaRequestInfo> {
942
- const client = await this.client();
943
- const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
1139
+ const client = await this.client("mfaApproveFidoComplete");
1140
+ return await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
944
1141
  params: { path: { org_id: this.orgId, mfa_id: mfaId } },
945
1142
  body: {
946
1143
  challenge_id: challengeId,
947
1144
  credential,
948
1145
  },
949
- parseAs: "json",
950
1146
  });
951
- return assertOk(resp);
952
1147
  }
953
1148
 
954
1149
  // #endregion
@@ -968,17 +1163,15 @@ export class CubeSignerApi {
968
1163
  mfaReceipt?: MfaReceipt,
969
1164
  ): Promise<CubeSignerResponse<EvmSignResponse>> {
970
1165
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
971
- const sign = async (headers?: HeadersInit) => {
972
- const client = await this.client();
973
- const resp = await client.post("/v1/org/{org_id}/eth1/sign/{pubkey}", {
1166
+ const signFn = async (headers?: HeadersInit) => {
1167
+ const client = await this.client("eth1Sign");
1168
+ return await client.post("/v1/org/{org_id}/eth1/sign/{pubkey}", {
974
1169
  params: { path: { org_id: this.orgId, pubkey } },
975
1170
  body: req,
976
1171
  headers,
977
- parseAs: "json",
978
1172
  });
979
- return assertOk(resp);
980
1173
  };
981
- return await CubeSignerResponse.create(sign, mfaReceipt);
1174
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
982
1175
  }
983
1176
 
984
1177
  /**
@@ -996,14 +1189,12 @@ export class CubeSignerApi {
996
1189
  ): Promise<CubeSignerResponse<Eth2SignResponse>> {
997
1190
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
998
1191
  const sign = async (headers?: HeadersInit) => {
999
- const client = await this.client();
1000
- const resp = await client.post("/v1/org/{org_id}/eth2/sign/{pubkey}", {
1192
+ const client = await this.client("eth2Sign");
1193
+ return await client.post("/v1/org/{org_id}/eth2/sign/{pubkey}", {
1001
1194
  params: { path: { org_id: this.orgId, pubkey } },
1002
1195
  body: req,
1003
1196
  headers,
1004
- parseAs: "json",
1005
1197
  });
1006
- return assertOk(resp);
1007
1198
  };
1008
1199
  return await CubeSignerResponse.create(sign, mfaReceipt);
1009
1200
  }
@@ -1020,14 +1211,12 @@ export class CubeSignerApi {
1020
1211
  mfaReceipt?: MfaReceipt,
1021
1212
  ): Promise<CubeSignerResponse<Eth2StakeResponse>> {
1022
1213
  const sign = async (headers?: HeadersInit) => {
1023
- const client = await this.client();
1024
- const resp = await client.post("/v1/org/{org_id}/eth2/stake", {
1214
+ const client = await this.client("stake");
1215
+ return await client.post("/v1/org/{org_id}/eth2/stake", {
1025
1216
  params: { path: { org_id: this.orgId } },
1026
1217
  body: req,
1027
1218
  headers,
1028
- parseAs: "json",
1029
1219
  });
1030
- return assertOk(resp);
1031
1220
  };
1032
1221
  return await CubeSignerResponse.create(sign, mfaReceipt);
1033
1222
  }
@@ -1046,17 +1235,15 @@ export class CubeSignerApi {
1046
1235
  mfaReceipt?: MfaReceipt,
1047
1236
  ): Promise<CubeSignerResponse<Eth2UnstakeResponse>> {
1048
1237
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1049
- const sign = async (headers?: HeadersInit) => {
1050
- const client = await this.client();
1051
- const resp = await client.post("/v1/org/{org_id}/eth2/unstake/{pubkey}", {
1238
+ const signFn = async (headers?: HeadersInit) => {
1239
+ const client = await this.client("unstake");
1240
+ return await client.post("/v1/org/{org_id}/eth2/unstake/{pubkey}", {
1052
1241
  params: { path: { org_id: this.orgId, pubkey } },
1053
1242
  body: req,
1054
1243
  headers,
1055
- parseAs: "json",
1056
1244
  });
1057
- return assertOk(resp);
1058
1245
  };
1059
- return await CubeSignerResponse.create(sign, mfaReceipt);
1246
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
1060
1247
  }
1061
1248
 
1062
1249
  /**
@@ -1072,20 +1259,18 @@ export class CubeSignerApi {
1072
1259
  mfaReceipt?: MfaReceipt,
1073
1260
  ): Promise<CubeSignerResponse<AvaSignResponse>> {
1074
1261
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1075
- const sign = async (headers?: HeadersInit) => {
1262
+ const signFn = async (headers?: HeadersInit) => {
1076
1263
  const req = <AvaSignRequest>{
1077
1264
  tx: tx as unknown,
1078
1265
  };
1079
- const client = await this.client();
1080
- const resp = await client.post("/v0/org/{org_id}/ava/sign/{pubkey}", {
1266
+ const client = await this.client("avaSign");
1267
+ return await client.post("/v0/org/{org_id}/ava/sign/{pubkey}", {
1081
1268
  params: { path: { org_id: this.orgId, pubkey } },
1082
1269
  body: req,
1083
1270
  headers,
1084
- parseAs: "json",
1085
1271
  });
1086
- return assertOk(resp);
1087
1272
  };
1088
- return await CubeSignerResponse.create(sign, mfaReceipt);
1273
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
1089
1274
  }
1090
1275
 
1091
1276
  /**
@@ -1117,19 +1302,17 @@ export class CubeSignerApi {
1117
1302
  mfaReceipt?: MfaReceipt,
1118
1303
  ): Promise<CubeSignerResponse<BlobSignResponse>> {
1119
1304
  const key_id = typeof key === "string" ? (key as string) : key.id;
1120
- const sign = async (headers?: HeadersInit) => {
1121
- const client = await this.client();
1122
- const resp = await client.post("/v1/org/{org_id}/blob/sign/{key_id}", {
1305
+ const signFn = async (headers?: HeadersInit) => {
1306
+ const client = await this.client("blobSign");
1307
+ return await client.post("/v1/org/{org_id}/blob/sign/{key_id}", {
1123
1308
  params: {
1124
1309
  path: { org_id: this.orgId, key_id },
1125
1310
  },
1126
1311
  body: req,
1127
1312
  headers,
1128
- parseAs: "json",
1129
1313
  });
1130
- return assertOk(resp);
1131
1314
  };
1132
- return await CubeSignerResponse.create(sign, mfaReceipt);
1315
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
1133
1316
  }
1134
1317
 
1135
1318
  /**
@@ -1146,19 +1329,17 @@ export class CubeSignerApi {
1146
1329
  mfaReceipt?: MfaReceipt,
1147
1330
  ): Promise<CubeSignerResponse<BtcSignResponse>> {
1148
1331
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1149
- const sign = async (headers?: HeadersInit) => {
1150
- const client = await this.client();
1151
- const resp = await client.post("/v0/org/{org_id}/btc/sign/{pubkey}", {
1332
+ const signFn = async (headers?: HeadersInit) => {
1333
+ const client = await this.client("btcSign");
1334
+ return await client.post("/v0/org/{org_id}/btc/sign/{pubkey}", {
1152
1335
  params: {
1153
1336
  path: { org_id: this.orgId, pubkey },
1154
1337
  },
1155
1338
  body: req,
1156
1339
  headers: headers,
1157
- parseAs: "json",
1158
1340
  });
1159
- return assertOk(resp);
1160
1341
  };
1161
- return await CubeSignerResponse.create(sign, mfaReceipt);
1342
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
1162
1343
  }
1163
1344
 
1164
1345
  /**
@@ -1175,25 +1356,18 @@ export class CubeSignerApi {
1175
1356
  mfaReceipt?: MfaReceipt,
1176
1357
  ): Promise<CubeSignerResponse<SolanaSignResponse>> {
1177
1358
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1178
- const sign = async (headers?: HeadersInit) => {
1179
- const client = await this.client();
1180
- const resp = await client.post("/v0/org/{org_id}/solana/sign/{pubkey}", {
1359
+ const signFn = async (headers?: HeadersInit) => {
1360
+ const client = await this.client("solanaSign");
1361
+ return await client.post("/v0/org/{org_id}/solana/sign/{pubkey}", {
1181
1362
  params: { path: { org_id: this.orgId, pubkey } },
1182
1363
  body: req,
1183
1364
  headers,
1184
- parseAs: "json",
1185
1365
  });
1186
- return assertOk(resp);
1187
1366
  };
1188
- return await CubeSignerResponse.create(sign, mfaReceipt);
1367
+ return await CubeSignerResponse.create(signFn, mfaReceipt);
1189
1368
  }
1190
1369
  // #endregion
1191
1370
 
1192
- /** HTTPS client */
1193
- private async client(): Promise<Client> {
1194
- return await this.#sessionMgr.client();
1195
- }
1196
-
1197
1371
  // #region USER EXPORT: userExport(Init,Complete,List,Delete)
1198
1372
  /**
1199
1373
  * List outstanding user-export requests.
@@ -1209,8 +1383,8 @@ export class CubeSignerApi {
1209
1383
  page?: PageOpts,
1210
1384
  ): Paginator<UserExportListResponse, UserExportInitResponse> {
1211
1385
  const listFn = async (query: PageQueryArgs) => {
1212
- const client = await this.client();
1213
- const resp = await client.get("/v0/org/{org_id}/user/me/export", {
1386
+ const client = await this.client("userExportList");
1387
+ return await client.get("/v0/org/{org_id}/user/me/export", {
1214
1388
  params: {
1215
1389
  path: { org_id: this.orgId },
1216
1390
  query: {
@@ -1219,9 +1393,7 @@ export class CubeSignerApi {
1219
1393
  ...query,
1220
1394
  },
1221
1395
  },
1222
- parseAs: "json",
1223
1396
  });
1224
- return assertOk(resp);
1225
1397
  };
1226
1398
  return new Paginator(
1227
1399
  page ?? Page.default(),
@@ -1238,8 +1410,8 @@ export class CubeSignerApi {
1238
1410
  * @param {string?} userId Optional user ID. If omitted, uses the current user's ID. Only org owners can delete user-export requests for users other than themselves.
1239
1411
  */
1240
1412
  async userExportDelete(keyId: string, userId?: string): Promise<void> {
1241
- const client = await this.client();
1242
- const resp = await client.del("/v0/org/{org_id}/user/me/export", {
1413
+ const client = await this.client("userExportDelete");
1414
+ await client.del("/v0/org/{org_id}/user/me/export", {
1243
1415
  params: {
1244
1416
  path: { org_id: this.orgId },
1245
1417
  query: {
@@ -1247,9 +1419,7 @@ export class CubeSignerApi {
1247
1419
  user_id: userId,
1248
1420
  },
1249
1421
  },
1250
- parseAs: "json",
1251
1422
  });
1252
- assertOk(resp);
1253
1423
  }
1254
1424
 
1255
1425
  /**
@@ -1263,17 +1433,15 @@ export class CubeSignerApi {
1263
1433
  keyId: string,
1264
1434
  mfaReceipt?: MfaReceipt,
1265
1435
  ): Promise<CubeSignerResponse<UserExportInitResponse>> {
1266
- const init = async (headers?: HeadersInit) => {
1267
- const client = await this.client();
1268
- const resp = await client.post("/v0/org/{org_id}/user/me/export", {
1436
+ const initFn = async (headers?: HeadersInit) => {
1437
+ const client = await this.client("userExportInit");
1438
+ return await client.post("/v0/org/{org_id}/user/me/export", {
1269
1439
  params: { path: { org_id: this.orgId } },
1270
1440
  body: { key_id: keyId },
1271
1441
  headers,
1272
- parseAs: "json",
1273
1442
  });
1274
- return assertOk(resp);
1275
1443
  };
1276
- return await CubeSignerResponse.create(init, mfaReceipt);
1444
+ return await CubeSignerResponse.create(initFn, mfaReceipt);
1277
1445
  }
1278
1446
 
1279
1447
  /**
@@ -1294,20 +1462,18 @@ export class CubeSignerApi {
1294
1462
  const publicKeyB64 = encodeToBase64(Buffer.from(await subtle.exportKey("raw", publicKey)));
1295
1463
 
1296
1464
  // make the request
1297
- const complete = async (headers?: HeadersInit) => {
1298
- const client = await this.client();
1299
- const resp = await client.patch("/v0/org/{org_id}/user/me/export", {
1465
+ const completeFn = async (headers?: HeadersInit) => {
1466
+ const client = await this.client("userExportComplete");
1467
+ return await client.patch("/v0/org/{org_id}/user/me/export", {
1300
1468
  params: { path: { org_id: this.orgId } },
1301
1469
  body: {
1302
1470
  key_id: keyId,
1303
1471
  public_key: publicKeyB64,
1304
1472
  },
1305
1473
  headers,
1306
- parseAs: "json",
1307
1474
  });
1308
- return assertOk(resp);
1309
1475
  };
1310
- return await CubeSignerResponse.create(complete, mfaReceipt);
1476
+ return await CubeSignerResponse.create(completeFn, mfaReceipt);
1311
1477
  }
1312
1478
  // #endregion
1313
1479
  }
@@ -1332,6 +1498,16 @@ export class OidcClient {
1332
1498
  this.#client = createHttpClient(env.SignerApiRoot, oidcToken);
1333
1499
  }
1334
1500
 
1501
+ /**
1502
+ * HTTP client restricted to a single operation.
1503
+ *
1504
+ * @param {Op} op The operation to restrict the client to
1505
+ * @return {OpClient<Op>} The client restricted to {@link op}
1506
+ */
1507
+ private client<Op extends keyof operations>(op: Op): OpClient<Op> {
1508
+ return new OpClient(op, this.#client, new EventEmitter([]));
1509
+ }
1510
+
1335
1511
  /**
1336
1512
  * Exchange an OIDC token for a CubeSigner session token.
1337
1513
  * @param {List<string>} scopes The scopes for the new session
@@ -1345,16 +1521,15 @@ export class OidcClient {
1345
1521
  mfaReceipt?: MfaReceipt,
1346
1522
  ): Promise<CubeSignerResponse<SignerSessionData>> {
1347
1523
  const loginFn = async (headers?: HeadersInit) => {
1348
- const resp = await this.#client.post("/v0/org/{org_id}/oidc", {
1524
+ const client = this.client("oidcAuth");
1525
+ const data = await client.post("/v0/org/{org_id}/oidc", {
1349
1526
  params: { path: { org_id: this.#orgId } },
1350
1527
  headers,
1351
1528
  body: {
1352
1529
  scopes,
1353
1530
  tokens: lifetimes,
1354
1531
  },
1355
- parseAs: "json",
1356
1532
  });
1357
- const data = assertOk(resp);
1358
1533
  return mapResponse(
1359
1534
  data,
1360
1535
  (sessionInfo) =>
@@ -1379,11 +1554,10 @@ export class OidcClient {
1379
1554
  * @return {Promise<IdentityProof>} Proof of authentication
1380
1555
  */
1381
1556
  async identityProve(): Promise<IdentityProof> {
1382
- const resp = await this.#client.post("/v0/org/{org_id}/identity/prove/oidc", {
1557
+ const client = this.client("createProofOidc");
1558
+ return await client.post("/v0/org/{org_id}/identity/prove/oidc", {
1383
1559
  params: { path: { org_id: this.#orgId } },
1384
- parseAs: "json",
1385
1560
  });
1386
- return assertOk(resp);
1387
1561
  }
1388
1562
  }
1389
1563