@cubist-labs/cubesigner-sdk 0.1.50 → 0.2.2

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.
Files changed (47) hide show
  1. package/README.md +66 -13
  2. package/dist/src/client.d.ts +434 -7
  3. package/dist/src/client.js +1022 -18
  4. package/dist/src/ethers/index.d.ts +2 -4
  5. package/dist/src/ethers/index.js +11 -9
  6. package/dist/src/fido.d.ts +76 -0
  7. package/dist/src/fido.js +148 -0
  8. package/dist/src/index.d.ts +102 -30
  9. package/dist/src/index.js +126 -72
  10. package/dist/src/key.d.ts +15 -45
  11. package/dist/src/key.js +31 -93
  12. package/dist/src/mfa.d.ts +85 -14
  13. package/dist/src/mfa.js +158 -40
  14. package/dist/src/org.d.ts +237 -123
  15. package/dist/src/org.js +108 -213
  16. package/dist/src/paginator.d.ts +76 -0
  17. package/dist/src/paginator.js +99 -0
  18. package/dist/src/role.d.ts +76 -74
  19. package/dist/src/role.js +79 -136
  20. package/dist/src/schema.d.ts +1672 -520
  21. package/dist/src/schema.js +1 -1
  22. package/dist/src/schema_types.d.ts +103 -0
  23. package/dist/src/schema_types.js +3 -0
  24. package/dist/src/session/session_manager.js +2 -2
  25. package/dist/src/session/session_storage.js +1 -1
  26. package/dist/src/session/signer_session_manager.d.ts +16 -29
  27. package/dist/src/session/signer_session_manager.js +27 -78
  28. package/dist/src/signer_session.d.ts +232 -125
  29. package/dist/src/signer_session.js +149 -250
  30. package/dist/src/util.d.ts +20 -0
  31. package/dist/src/util.js +31 -2
  32. package/package.json +13 -11
  33. package/src/client.ts +1217 -7
  34. package/src/ethers/index.ts +11 -18
  35. package/src/index.ts +149 -101
  36. package/src/key.ts +28 -121
  37. package/src/mfa.ts +202 -0
  38. package/src/org.ts +126 -275
  39. package/src/paginator.ts +122 -0
  40. package/src/role.ts +108 -181
  41. package/src/schema.ts +1673 -520
  42. package/src/schema_types.ts +103 -0
  43. package/src/session/session_manager.ts +2 -2
  44. package/src/session/session_storage.ts +1 -1
  45. package/src/session/signer_session_manager.ts +38 -108
  46. package/src/signer_session.ts +164 -323
  47. package/src/util.ts +41 -0
package/src/client.ts CHANGED
@@ -1,12 +1,1222 @@
1
1
  import createClient from "openapi-fetch";
2
2
  import { paths } from "./schema";
3
+ import {
4
+ SignerSessionData,
5
+ SignerSessionLifetime,
6
+ SignerSessionManager,
7
+ } from "./session/signer_session_manager";
8
+ import {
9
+ CreateOidcUserOptions,
10
+ IdentityProof,
11
+ KeyInRoleInfo,
12
+ KeyInfoApi,
13
+ ListKeysResponse,
14
+ ListRoleKeysResponse,
15
+ ListRoleUsersResponse,
16
+ ListRolesResponse,
17
+ OidcIdentity,
18
+ SessionsResponse,
19
+ PublicKeyCredential,
20
+ RoleInfo,
21
+ UpdateKeyRequest,
22
+ UpdateOrgRequest,
23
+ UpdateOrgResponse,
24
+ UpdateRoleRequest,
25
+ UserIdInfo,
26
+ UserInRoleInfo,
27
+ UserInfo,
28
+ SessionInfo,
29
+ OrgInfo,
30
+ RatchetConfig,
31
+ OidcAuthResponse,
32
+ EvmSignRequest,
33
+ EvmSignResponse,
34
+ Eth2SignRequest,
35
+ Eth2SignResponse,
36
+ Eth2StakeRequest,
37
+ Eth2StakeResponse,
38
+ Eth2UnstakeRequest,
39
+ Eth2UnstakeResponse,
40
+ BlobSignRequest,
41
+ BlobSignResponse,
42
+ BtcSignResponse,
43
+ BtcSignRequest,
44
+ SolanaSignRequest,
45
+ SolanaSignResponse,
46
+ AvaSignResponse,
47
+ AvaSignRequest,
48
+ AvaTx,
49
+ MfaRequestInfo,
50
+ MemberRole,
51
+ } from "./schema_types";
52
+ import { assertOk } from "./util";
53
+ import { AddFidoChallenge, MfaFidoChallenge, MfaReceipt, TotpChallenge } from "./mfa";
54
+ import { CubeSignerResponse, mapResponse } from "./signer_session";
55
+ import { Key, KeyType } from "./key";
56
+ import { Page, PageOpts, PageQueryArgs, Paginator } from "./paginator";
57
+ import { KeyPolicy } from "./role";
58
+ import { EnvInterface } from "./env";
3
59
 
4
- /** Type of http client.
5
- * @internal
6
- * */
60
+ /** @internal */
7
61
  export type Client = ReturnType<typeof createClient<paths>>;
8
62
 
9
- /** Re-export schema.
10
- * @internal
11
- * */
12
- export * from "./schema";
63
+ export { paths };
64
+
65
+ /**
66
+ * Client to use to send requests to CubeSigner services
67
+ * when authenticating using a CubeSigner session token.
68
+ */
69
+ export class CubeSignerClient {
70
+ readonly #orgId: string;
71
+ readonly #sessionMgr: SignerSessionManager;
72
+
73
+ /** Underlying session manager */
74
+ get sessionMgr(): SignerSessionManager {
75
+ return this.#sessionMgr;
76
+ }
77
+
78
+ /**
79
+ * Constructor.
80
+ * @param {SignerSessionManager} sessionMgr The session manager to use
81
+ * @param {string?} orgId Optional organization ID; if omitted, uses the org ID from the session manager.
82
+ */
83
+ constructor(sessionMgr: SignerSessionManager, orgId?: string) {
84
+ this.#sessionMgr = sessionMgr;
85
+ this.#orgId = orgId ?? sessionMgr.orgId;
86
+ }
87
+
88
+ /**
89
+ * Returns a new instance of this class using the same session manager but targeting a different organization.
90
+ *
91
+ * @param {string} orgId The organization ID.
92
+ * @return {CubeSignerClient} A new instance of this class using the same session manager but targeting different organization.
93
+ */
94
+ withOrg(orgId?: string): CubeSignerClient {
95
+ return orgId ? new CubeSignerClient(this.#sessionMgr, orgId) : this;
96
+ }
97
+
98
+ /** Org id */
99
+ get orgId() {
100
+ return this.#orgId;
101
+ }
102
+
103
+ // #region USERS: userGet, userResetTotp(Init|Complete), userVerifyTotp, userRegisterFido(Init|Complete)
104
+
105
+ /**
106
+ * Obtain information about the current user.
107
+ *
108
+ * @return {Promise<UserInfo>} Retrieves information about the current user.
109
+ */
110
+ async userGet(): Promise<UserInfo> {
111
+ const client = await this.client();
112
+ const resp =
113
+ `${this.orgId}` !== "undefined"
114
+ ? await client.get("/v0/org/{org_id}/user/me", {
115
+ params: { path: { org_id: this.orgId } },
116
+ parseAs: "json",
117
+ })
118
+ : await client.get("/v0/about_me", { parseAs: "json" });
119
+ return assertOk(resp);
120
+ }
121
+
122
+ /**
123
+ * Creates a request to change user's TOTP. This request returns a new TOTP challenge
124
+ * that must be answered by calling `userResetTotpComplete`
125
+ *
126
+ * @param {MfaReceipt} mfaReceipt MFA receipt to include in HTTP headers
127
+ */
128
+ async userResetTotpInit(mfaReceipt?: MfaReceipt): Promise<CubeSignerResponse<TotpChallenge>> {
129
+ const resetTotpFn = async (headers?: HeadersInit) => {
130
+ const client = await this.client();
131
+ const resp = await client.post("/v0/org/{org_id}/user/me/totp", {
132
+ headers,
133
+ params: { path: { org_id: this.orgId } },
134
+ body: null,
135
+ parseAs: "json",
136
+ });
137
+ const data = assertOk(resp);
138
+ return mapResponse(data, (totpInfo) => new TotpChallenge(this, totpInfo));
139
+ };
140
+ return await CubeSignerResponse.create(resetTotpFn, mfaReceipt);
141
+ }
142
+
143
+ /**
144
+ * Answer the TOTP challenge issued by `userResetTotpInit`. If successful, user's
145
+ * TOTP configuration will be updated to that of the TOTP challenge.
146
+ *
147
+ * @param {string} totpId - The ID of the TOTP challenge
148
+ * @param {string} code - The TOTP code that should verify against the TOTP configuration from the challenge.
149
+ */
150
+ async userResetTotpComplete(totpId: string, code: string): Promise<void> {
151
+ const client = await this.client();
152
+ const resp = await client.patch("/v0/org/{org_id}/user/me/totp", {
153
+ parseAs: "json",
154
+ params: { path: { org_id: this.orgId } },
155
+ body: { totp_id: totpId, code },
156
+ });
157
+ assertOk(resp);
158
+ }
159
+
160
+ /**
161
+ * Verifies a given TOTP code against the current user's TOTP configuration.
162
+ * Throws an error if the verification fails.
163
+ *
164
+ * @param {string} code Current TOTP code
165
+ */
166
+ async userVerifyTotp(code: string) {
167
+ const client = await this.client();
168
+ const resp = await client.post("/v0/org/{org_id}/user/me/totp/verify", {
169
+ params: { path: { org_id: this.orgId } },
170
+ body: { code },
171
+ parseAs: "json",
172
+ });
173
+ assertOk(resp);
174
+ }
175
+
176
+ /**
177
+ * Initiate adding a new FIDO device. MFA may be required. This returns a challenge that must
178
+ * be answered with `userRegisterFidoComplete` (after MFA approvals).
179
+ *
180
+ * @param {string} name The name of the new device.
181
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt to include in HTTP headers
182
+ * @return {Promise<CubeSignerResponse<AddFidoChallenge>>} A challenge that must be answered in order to complete FIDO registration.
183
+ */
184
+ async userRegisterFidoInit(
185
+ name: string,
186
+ mfaReceipt?: MfaReceipt,
187
+ ): Promise<CubeSignerResponse<AddFidoChallenge>> {
188
+ const addFidoFn = async (headers?: HeadersInit) => {
189
+ const client = await this.client();
190
+ const resp = await client.post("/v0/org/{org_id}/user/me/fido", {
191
+ headers,
192
+ params: { path: { org_id: this.orgId } },
193
+ body: { name },
194
+ parseAs: "json",
195
+ });
196
+ const data = assertOk(resp);
197
+ return mapResponse(data, (c) => new AddFidoChallenge(this, c));
198
+ };
199
+ return await CubeSignerResponse.create(addFidoFn, mfaReceipt);
200
+ }
201
+
202
+ /**
203
+ * Complete a previously initiated request to add a new FIDO device.
204
+ * @param {string} challengeId The ID of the challenge returned by the remote end.
205
+ * @param {PublicKeyCredential} credential The answer to the challenge.
206
+ */
207
+ async userRegisterFidoComplete(challengeId: string, credential: PublicKeyCredential) {
208
+ const client = await this.client();
209
+ const resp = await client.patch("/v0/org/{org_id}/user/me/fido", {
210
+ params: { path: { org_id: this.orgId } },
211
+ body: {
212
+ challenge_id: challengeId,
213
+ credential,
214
+ },
215
+ parseAs: "json",
216
+ });
217
+ assertOk(resp);
218
+ }
219
+
220
+ // #endregion
221
+
222
+ // #region ORGS: orgGet, orgUpdate
223
+
224
+ /**
225
+ * Obtain information about the current organization.
226
+ * @return {Org} Information about the organization.
227
+ */
228
+ async orgGet(): Promise<OrgInfo> {
229
+ const client = await this.client();
230
+ const resp = await client.get("/v0/org/{org_id}", {
231
+ params: { path: { org_id: this.orgId } },
232
+ parseAs: "json",
233
+ });
234
+ return assertOk(resp);
235
+ }
236
+
237
+ /**
238
+ * Update the org.
239
+ * @param {UpdateOrgRequest} request The JSON request to send to the API server.
240
+ * @return {UpdateOrgResponse} Updated org information.
241
+ */
242
+ async orgUpdate(request: UpdateOrgRequest): Promise<UpdateOrgResponse> {
243
+ const client = await this.client();
244
+ const resp = await client.patch("/v0/org/{org_id}", {
245
+ params: { path: { org_id: this.orgId } },
246
+ body: request,
247
+ parseAs: "json",
248
+ });
249
+ return assertOk(resp);
250
+ }
251
+
252
+ // #endregion
253
+
254
+ // #region ORG USERS: orgUserInvite, orgUsersList, orgUserCreateOidc, orgUserDeleteOidc
255
+
256
+ /**
257
+ * Create a new (first-party) user in the organization and send an email invitation to that user.
258
+ *
259
+ * @param {string} email Email of the user
260
+ * @param {string} name The full name of the user
261
+ * @param {MemberRole} role Optional role. Defaults to "alien.
262
+ */
263
+ async orgUserInvite(email: string, name: string, role?: MemberRole): Promise<void> {
264
+ const client = await this.client();
265
+ const resp = await client.post("/v0/org/{org_id}/invite", {
266
+ params: { path: { org_id: this.orgId } },
267
+ body: {
268
+ email,
269
+ name,
270
+ role,
271
+ skip_email: false,
272
+ },
273
+ parseAs: "json",
274
+ });
275
+ assertOk(resp);
276
+ }
277
+
278
+ /**
279
+ * List users.
280
+ * @return {User[]} Org users.
281
+ */
282
+ async orgUsersList(): Promise<UserIdInfo[]> {
283
+ const client = await this.client();
284
+ const resp = await client.get("/v0/org/{org_id}/users", {
285
+ params: { path: { org_id: this.orgId } },
286
+ parseAs: "json",
287
+ });
288
+ const data = assertOk(resp);
289
+ return data.users;
290
+ }
291
+
292
+ /**
293
+ * Create a new OIDC user. This can be a first-party "Member" or third-party "Alien".
294
+ * @param {OidcIdentity} identity The identity of the OIDC user
295
+ * @param {string} email Email of the OIDC user
296
+ * @param {CreateOidcUserOptions} opts Additional options for new OIDC users
297
+ * @return {string} User id of the new user
298
+ */
299
+ async orgUserCreateOidc(
300
+ identity: OidcIdentity,
301
+ email: string,
302
+ opts: CreateOidcUserOptions = {},
303
+ ): Promise<string> {
304
+ const client = await this.client();
305
+ const resp = await client.post("/v0/org/{org_id}/users", {
306
+ params: { path: { org_id: this.orgId } },
307
+ body: {
308
+ identity,
309
+ role: opts.memberRole ?? "Alien",
310
+ email: email,
311
+ mfa_policy: opts.mfaPolicy ?? null,
312
+ },
313
+ parseAs: "json",
314
+ });
315
+ return assertOk(resp).user_id;
316
+ }
317
+
318
+ /**
319
+ * Delete an existing OIDC user.
320
+ * @param {OidcIdentity} identity The identity of the OIDC user
321
+ */
322
+ async orgUserDeleteOidc(identity: OidcIdentity) {
323
+ const client = await this.client();
324
+ const resp = await client.del("/v0/org/{org_id}/users/oidc", {
325
+ params: { path: { org_id: this.orgId } },
326
+ body: identity,
327
+ parseAs: "json",
328
+ });
329
+ return assertOk(resp);
330
+ }
331
+
332
+ // #endregion
333
+
334
+ // #region KEYS: keyGet, keyUpdate, keyDelete, keysCreate, keysDerive, keysList
335
+
336
+ /**
337
+ * Get a key by its id.
338
+ *
339
+ * @param {string} keyId The id of the key to get.
340
+ * @return {KeyInfoApi} The key information.
341
+ */
342
+ async keyGet(keyId: string): Promise<KeyInfoApi> {
343
+ const client = await this.client();
344
+ const resp = await client.get("/v0/org/{org_id}/keys/{key_id}", {
345
+ params: { path: { org_id: this.orgId, key_id: keyId } },
346
+ parseAs: "json",
347
+ });
348
+ return assertOk(resp);
349
+ }
350
+
351
+ /**
352
+ * Update key.
353
+ * @param {string} keyId The ID of the key to update.
354
+ * @param {UpdateKeyRequest} request The JSON request to send to the API server.
355
+ * @return {KeyInfoApi} The JSON response from the API server.
356
+ */
357
+ async keyUpdate(keyId: string, request: UpdateKeyRequest): Promise<KeyInfoApi> {
358
+ const client = await this.client();
359
+ const resp = await client.patch("/v0/org/{org_id}/keys/{key_id}", {
360
+ params: { path: { org_id: this.orgId, key_id: keyId } },
361
+ body: request,
362
+ parseAs: "json",
363
+ });
364
+ return assertOk(resp);
365
+ }
366
+
367
+ /**
368
+ * Deletes a key.
369
+ *
370
+ * @param {string} keyId - Key id
371
+ */
372
+ async keyDelete(keyId: string) {
373
+ const client = await this.client();
374
+ const resp = await client.del("/v0/org/{org_id}/keys/{key_id}", {
375
+ params: { path: { org_id: this.orgId, key_id: keyId } },
376
+ parseAs: "json",
377
+ });
378
+ assertOk(resp);
379
+ }
380
+
381
+ /**
382
+ * Create new signing keys.
383
+ *
384
+ * @param {KeyType} keyType The type of key to create.
385
+ * @param {number} count The number of keys to create.
386
+ * @param {string?} ownerId The owner of the keys. Defaults to the session's user.
387
+ * @return {KeyInfoApi[]} The new keys.
388
+ */
389
+ async keysCreate(keyType: KeyType, count: number, ownerId?: string): Promise<KeyInfoApi[]> {
390
+ const chain_id = 0; // not used anymore
391
+ const client = await this.client();
392
+ const resp = await client.post("/v0/org/{org_id}/keys", {
393
+ params: { path: { org_id: this.orgId } },
394
+ body: {
395
+ count,
396
+ chain_id,
397
+ key_type: keyType,
398
+ owner: ownerId || null,
399
+ },
400
+ parseAs: "json",
401
+ });
402
+ const data = assertOk(resp);
403
+ return data.keys;
404
+ }
405
+
406
+ /**
407
+ * Derive a set of keys of a specified type using a supplied derivation path and an existing long-lived mnemonic.
408
+ *
409
+ * The owner of the derived key will be the owner of the mnemonic.
410
+ *
411
+ * @param {KeyType} keyType The type of key to create.
412
+ * @param {string[]} derivationPaths Derivation paths from which to derive new keys.
413
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
414
+ *
415
+ * @return {KeyInfoApi[]} The newly derived keys.
416
+ */
417
+ async keysDerive(
418
+ keyType: KeyType,
419
+ derivationPaths: string[],
420
+ mnemonicId: string,
421
+ ): Promise<KeyInfoApi[]> {
422
+ const client = await this.client();
423
+ const resp = await client.put("/v0/org/{org_id}/derive_key", {
424
+ params: { path: { org_id: this.orgId } },
425
+ body: {
426
+ derivation_path: derivationPaths,
427
+ mnemonic_id: mnemonicId,
428
+ key_type: keyType,
429
+ },
430
+ parseAs: "json",
431
+ });
432
+ return assertOk(resp).keys;
433
+ }
434
+
435
+ /**
436
+ * List all keys in the org.
437
+ * @param {KeyType?} type Optional key type to filter list for.
438
+ * @param {PageOpts?} page Pagination options. Defaults to fetching the entire result set.
439
+ * @return {Paginator<ListKeysResponse, KeyInfoApi>} Paginator for iterating over keys.
440
+ */
441
+ keysList(type?: KeyType, page?: PageOpts): Paginator<ListKeysResponse, KeyInfoApi> {
442
+ const listFn = async (query: PageQueryArgs) => {
443
+ const client = await this.client();
444
+ const resp = await client.get("/v0/org/{org_id}/keys", {
445
+ params: {
446
+ path: { org_id: this.orgId },
447
+ query: {
448
+ key_type: type,
449
+ ...query,
450
+ },
451
+ },
452
+ parseAs: "json",
453
+ });
454
+ return assertOk(resp);
455
+ };
456
+ return new Paginator(
457
+ page ?? Page.default(),
458
+ listFn,
459
+ (r) => r.keys,
460
+ (r) => r.last_evaluated_key,
461
+ );
462
+ }
463
+ // #endregion
464
+
465
+ // #region ROLES: roleCreate, roleRead, roleUpdate, roleDelete, rolesList
466
+
467
+ /**
468
+ * Create a new role.
469
+ *
470
+ * @param {string?} name The optional name of the role.
471
+ * @return {string} The ID of the new role.
472
+ */
473
+ async roleCreate(name?: string): Promise<string> {
474
+ const client = await this.client();
475
+ const resp = await client.post("/v0/org/{org_id}/roles", {
476
+ params: { path: { org_id: this.orgId } },
477
+ body: name ? { name } : undefined,
478
+ parseAs: "json",
479
+ });
480
+ return assertOk(resp).role_id;
481
+ }
482
+
483
+ /**
484
+ * Get a role by its id (or name).
485
+ * @param {string} roleId The id of the role to get.
486
+ * @return {RoleInfo} The role.
487
+ */
488
+ async roleGet(roleId: string): Promise<RoleInfo> {
489
+ const client = await this.client();
490
+ const resp = await client.get("/v0/org/{org_id}/roles/{role_id}", {
491
+ params: { path: { org_id: this.orgId, role_id: roleId } },
492
+ parseAs: "json",
493
+ });
494
+ return assertOk(resp);
495
+ }
496
+
497
+ /**
498
+ * Update a role.
499
+ *
500
+ * @param {string} roleId The ID of the role to update.
501
+ * @param {UpdateRoleRequest} request The update request.
502
+ * @return {Promise<RoleInfo>} The updated role information.
503
+ */
504
+ async roleUpdate(roleId: string, request: UpdateRoleRequest): Promise<RoleInfo> {
505
+ const client = await this.client();
506
+ const resp = await client.patch("/v0/org/{org_id}/roles/{role_id}", {
507
+ params: { path: { org_id: this.orgId, role_id: roleId } },
508
+ body: request,
509
+ parseAs: "json",
510
+ });
511
+ return assertOk(resp);
512
+ }
513
+
514
+ /**
515
+ * Delete a role by its ID.
516
+ *
517
+ * @param {string} roleId The ID of the role to delete.
518
+ */
519
+ async roleDelete(roleId: string): Promise<void> {
520
+ const client = await this.client();
521
+ const resp = await client.del("/v0/org/{org_id}/roles/{role_id}", {
522
+ params: { path: { org_id: this.orgId, role_id: roleId } },
523
+ parseAs: "json",
524
+ });
525
+ assertOk(resp);
526
+ }
527
+
528
+ /**
529
+ * List all roles in the org.
530
+ *
531
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
532
+ * @return {RoleInfo} Paginator for iterating over roles.
533
+ */
534
+ rolesList(page?: PageOpts): Paginator<ListRolesResponse, RoleInfo> {
535
+ const listFn = async (query: PageQueryArgs) => {
536
+ const client = await this.client();
537
+ const resp = await client.get("/v0/org/{org_id}/roles", {
538
+ params: {
539
+ path: { org_id: this.orgId },
540
+ query,
541
+ },
542
+ parseAs: "json",
543
+ });
544
+ return assertOk(resp);
545
+ };
546
+ return new Paginator(
547
+ page ?? Page.default(),
548
+ listFn,
549
+ (r) => r.roles,
550
+ (r) => r.last_evaluated_key,
551
+ );
552
+ }
553
+
554
+ // #endregion
555
+
556
+ // #region ROLE KEYS: roleKeysAdd, roleKeysDelete, roleKeysList
557
+
558
+ /**
559
+ * Add existing keys to an existing role.
560
+ *
561
+ * @param {string} roleId The ID of the role
562
+ * @param {string[]} keyIds The IDs of the keys to add to the role.
563
+ * @param {KeyPolicy?} policy The optional policy to apply to each key.
564
+ */
565
+ async roleKeysAdd(roleId: string, keyIds: string[], policy?: KeyPolicy) {
566
+ const client = await this.client();
567
+ const resp = await client.put("/v0/org/{org_id}/roles/{role_id}/add_keys", {
568
+ params: { path: { org_id: this.#orgId, role_id: roleId } },
569
+ body: {
570
+ key_ids: keyIds,
571
+ policy: (policy ?? null) as Record<string, never>[] | null,
572
+ },
573
+ parseAs: "json",
574
+ });
575
+ assertOk(resp, "Failed to add keys to role");
576
+ }
577
+
578
+ /**
579
+ * Remove an existing key from an existing role.
580
+ *
581
+ * @param {string} roleId The ID of the role
582
+ * @param {string} keyId The ID of the key to remove from the role
583
+ */
584
+ async roleKeysRemove(roleId: string, keyId: string) {
585
+ const client = await this.client();
586
+ const resp = await client.del("/v0/org/{org_id}/roles/{role_id}/keys/{key_id}", {
587
+ params: { path: { org_id: this.#orgId, role_id: roleId, key_id: keyId } },
588
+ parseAs: "json",
589
+ });
590
+ assertOk(resp, "Failed to remove key from a role");
591
+ }
592
+
593
+ /**
594
+ * List all keys in a role.
595
+ *
596
+ * @param {string} roleId The ID of the role whose keys to retrieve.
597
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
598
+ * @return {Paginator<ListRoleKeysResponse, KeyInRoleInfo>} Paginator for iterating over the keys in the role.
599
+ */
600
+ roleKeysList(roleId: string, page?: PageOpts): Paginator<ListRoleKeysResponse, KeyInRoleInfo> {
601
+ const listFn = async (query: PageQueryArgs) => {
602
+ const client = await this.client();
603
+ const resp = await client.get("/v0/org/{org_id}/roles/{role_id}/keys", {
604
+ params: {
605
+ path: { org_id: this.orgId, role_id: roleId },
606
+ query,
607
+ },
608
+ parseAs: "json",
609
+ });
610
+ return assertOk(resp);
611
+ };
612
+ return new Paginator(
613
+ page ?? Page.default(),
614
+ listFn,
615
+ (r) => r.keys,
616
+ (r) => r.last_evaluated_key,
617
+ );
618
+ }
619
+
620
+ // #endregion
621
+
622
+ // #region ROLE USERS: roleUserAdd, roleUsersList
623
+
624
+ /**
625
+ * Add an existing user to an existing role.
626
+ *
627
+ * @param {string} roleId The ID of the role.
628
+ * @param {string} userId The ID of the user to add to the role.
629
+ */
630
+ async roleUserAdd(roleId: string, userId: string) {
631
+ const client = await this.client();
632
+ const resp = await client.put("/v0/org/{org_id}/roles/{role_id}/add_user/{user_id}", {
633
+ params: { path: { org_id: this.#orgId, role_id: roleId, user_id: userId } },
634
+ parseAs: "json",
635
+ });
636
+ assertOk(resp, "Failed to add user to role");
637
+ }
638
+
639
+ /**
640
+ * List all users in a role.
641
+ *
642
+ * @param {string} roleId The ID of the role whose users to retrieve.
643
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
644
+ * @return {Paginator<ListRoleUsersResponse, UserInRoleInfo>} Paginator for iterating over the users in the role.
645
+ */
646
+ roleUsersList(roleId: string, page?: PageOpts): Paginator<ListRoleUsersResponse, UserInRoleInfo> {
647
+ const listFn = async (query: PageQueryArgs) => {
648
+ const client = await this.client();
649
+ const resp = await client.get("/v0/org/{org_id}/roles/{role_id}/users", {
650
+ params: {
651
+ path: { org_id: this.orgId, role_id: roleId },
652
+ query,
653
+ },
654
+ parseAs: "json",
655
+ });
656
+ return assertOk(resp);
657
+ };
658
+ return new Paginator(
659
+ page ?? Page.default(),
660
+ listFn,
661
+ (r) => r.users,
662
+ (r) => r.last_evaluated_key,
663
+ );
664
+ }
665
+
666
+ // #endregion
667
+
668
+ // #region SESSIONS: sessionCreateForRole, sessionRefresh, sessionRevoke, sessionsList, sessionKeysList
669
+
670
+ /**
671
+ * Create a new signer session for a given role.
672
+ *
673
+ * @param {string} roleId Role ID
674
+ * @param {string} purpose The purpose of the session
675
+ * @param {string[]} scopes Session scopes. Only `sign:*` scopes are allowed.
676
+ * @param {SignerSessionLifetime} lifetimes Lifetime settings
677
+ * @return {Promise<SignerSessionData>} New signer session info.
678
+ */
679
+ async sessionCreateForRole(
680
+ roleId: string,
681
+ purpose: string,
682
+ scopes?: string[],
683
+ lifetimes?: SignerSessionLifetime,
684
+ ): Promise<SignerSessionData> {
685
+ lifetimes ??= defaultSignerSessionLifetime;
686
+ const invalidScopes = (scopes || []).filter((s) => !s.startsWith("sign:"));
687
+ if (invalidScopes.length > 0) {
688
+ throw new Error(`Role scopes must start with 'sign:'; invalid scopes: ${invalidScopes}`);
689
+ }
690
+
691
+ const client = await this.client();
692
+ const resp = await client.post("/v0/org/{org_id}/roles/{role_id}/tokens", {
693
+ params: { path: { org_id: this.orgId, role_id: roleId } },
694
+ body: {
695
+ purpose,
696
+ scopes,
697
+ auth_lifetime: lifetimes.auth,
698
+ refresh_lifetime: lifetimes.refresh,
699
+ session_lifetime: lifetimes.session,
700
+ grace_lifetime: lifetimes.grace,
701
+ },
702
+ parseAs: "json",
703
+ });
704
+ const data = assertOk(resp);
705
+ return {
706
+ org_id: this.orgId,
707
+ role_id: roleId,
708
+ purpose,
709
+ token: data.token,
710
+ session_info: data.session_info,
711
+ // Keep compatibility with tokens produced by CLI
712
+ env: {
713
+ ["Dev-CubeSignerStack"]: this.#sessionMgr.env,
714
+ },
715
+ };
716
+ }
717
+
718
+ /**
719
+ * Revoke a session.
720
+ *
721
+ * @param {string} sessionId The ID of the session to revoke.
722
+ */
723
+ async sessionRevoke(sessionId: string) {
724
+ const client = await this.client();
725
+ const resp = await client.del("/v0/org/{org_id}/session/{session_id}", {
726
+ params: { path: { org_id: this.orgId, session_id: sessionId } },
727
+ parseAs: "json",
728
+ });
729
+ assertOk(resp);
730
+ }
731
+
732
+ /**
733
+ * Returns a paginator for iterating over all signer sessions optionally filtered by a role.
734
+ *
735
+ * @param {string?} roleId If set, limit to sessions for this role only.
736
+ * @param {PageOpts?} page Pagination options. Defaults to fetching the entire result set.
737
+ * @return {Promise<SignerSessionInfo[]>} Signer sessions for this role.
738
+ */
739
+ sessionsList(roleId?: string, page?: PageOpts): Paginator<SessionsResponse, SessionInfo> {
740
+ const listFn = async (query: PageQueryArgs) => {
741
+ const client = await this.client();
742
+ const resp = await client.get("/v0/org/{org_id}/session", {
743
+ params: {
744
+ path: { org_id: this.#orgId },
745
+ query: { role: roleId, ...query },
746
+ },
747
+ parseAs: "json",
748
+ });
749
+ return assertOk(resp);
750
+ };
751
+ return new Paginator(
752
+ page ?? Page.default(),
753
+ listFn,
754
+ (r) => r.sessions,
755
+ (r) => r.last_evaluated_key,
756
+ );
757
+ }
758
+
759
+ /**
760
+ * Returns the list of keys that this session has access to.
761
+ * @return {Key[]} The list of keys.
762
+ */
763
+ async sessionKeysList(): Promise<KeyInfoApi[]> {
764
+ const client = await this.client();
765
+ const resp = await client.get("/v0/org/{org_id}/token/keys", {
766
+ params: { path: { org_id: this.orgId } },
767
+ parseAs: "json",
768
+ });
769
+ return assertOk(resp).keys;
770
+ }
771
+
772
+ // #endregion
773
+
774
+ // #region IDENTITY: identityProve, identityVerify
775
+
776
+ /**
777
+ * Obtain proof of authentication using the current CubeSigner session.
778
+ *
779
+ * @return {Promise<IdentityProof>} Proof of authentication
780
+ */
781
+ async identityProve(): Promise<IdentityProof> {
782
+ const client = await this.client();
783
+ const resp = await client.post("/v0/org/{org_id}/identity/prove", {
784
+ params: { path: { org_id: this.orgId } },
785
+ parseAs: "json",
786
+ });
787
+ return assertOk(resp);
788
+ }
789
+
790
+ /**
791
+ * Checks if a given identity proof is valid.
792
+ *
793
+ * @param {IdentityProof} proof The proof of authentication.
794
+ */
795
+ async identityVerify(proof: IdentityProof) {
796
+ const client = await this.client();
797
+ const resp = await client.post("/v0/org/{org_id}/identity/verify", {
798
+ params: { path: { org_id: this.orgId } },
799
+ body: proof,
800
+ parseAs: "json",
801
+ });
802
+ assertOk(resp);
803
+ }
804
+
805
+ // #endregion
806
+
807
+ // #region MFA: mfaGet, mfaList, mfaApprove, mfaList, mfaApprove, mfaApproveTotp, mfaApproveFido(Init|Complete)
808
+
809
+ /**
810
+ * Retrieves existing MFA request.
811
+ *
812
+ * @param {string} mfaId MFA request ID
813
+ * @return {Promise<MfaRequestInfo>} MFA request information
814
+ */
815
+ async mfaGet(mfaId: string): Promise<MfaRequestInfo> {
816
+ const client = await this.client();
817
+ const resp = await client.get("/v0/org/{org_id}/mfa/{mfa_id}", {
818
+ params: { path: { org_id: this.orgId, mfa_id: mfaId } },
819
+ });
820
+ return assertOk(resp);
821
+ }
822
+
823
+ /**
824
+ * List pending MFA requests accessible to the current user.
825
+ *
826
+ * @return {Promise<MfaRequestInfo[]>} The MFA requests.
827
+ */
828
+ async mfaList(): Promise<MfaRequestInfo[]> {
829
+ const client = await this.client();
830
+ const resp = await client.get("/v0/org/{org_id}/mfa", {
831
+ params: { path: { org_id: this.orgId } },
832
+ });
833
+ return assertOk(resp).mfa_requests;
834
+ }
835
+
836
+ /**
837
+ * Approve a pending MFA request using the current session.
838
+ *
839
+ * @param {string} mfaId The id of the MFA request
840
+ * @return {Promise<MfaRequestInfo>} The result of the MFA request
841
+ */
842
+ async mfaApprove(mfaId: string): Promise<MfaRequestInfo> {
843
+ const client = await this.client();
844
+ const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}", {
845
+ params: { path: { org_id: this.orgId, mfa_id: mfaId } },
846
+ });
847
+ return assertOk(resp);
848
+ }
849
+
850
+ /**
851
+ * Approve a pending MFA request using TOTP.
852
+ *
853
+ * @param {string} mfaId The MFA request to approve
854
+ * @param {string} code The TOTP code
855
+ * @return {Promise<MfaRequestInfo>} The current status of the MFA request
856
+ */
857
+ async mfaApproveTotp(mfaId: string, code: string): Promise<MfaRequestInfo> {
858
+ const client = await this.client();
859
+ const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/totp", {
860
+ params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
861
+ body: { code },
862
+ parseAs: "json",
863
+ });
864
+ return assertOk(resp);
865
+ }
866
+
867
+ /**
868
+ * Initiate approval of an existing MFA request using FIDO.
869
+ *
870
+ * @param {string} mfaId The MFA request ID.
871
+ * @return {Promise<MfaFidoChallenge>} A challenge that needs to be answered to complete the approval.
872
+ */
873
+ async mfaApproveFidoInit(mfaId: string): Promise<MfaFidoChallenge> {
874
+ const client = await this.client();
875
+ const resp = await client.post("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
876
+ params: { path: { org_id: this.orgId, mfa_id: mfaId } },
877
+ parseAs: "json",
878
+ });
879
+ const challenge = assertOk(resp);
880
+ return new MfaFidoChallenge(this, mfaId, challenge);
881
+ }
882
+
883
+ /**
884
+ * Complete a previously initiated MFA request approval using FIDO.
885
+ *
886
+ * @param {string} mfaId The MFA request ID
887
+ * @param {string} challengeId The challenge ID
888
+ * @param {PublicKeyCredential} credential The answer to the challenge
889
+ * @return {Promise<MfaRequestInfo>} The current status of the MFA request.
890
+ */
891
+ async mfaApproveFidoComplete(
892
+ mfaId: string,
893
+ challengeId: string,
894
+ credential: PublicKeyCredential,
895
+ ): Promise<MfaRequestInfo> {
896
+ const client = await this.client();
897
+ const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
898
+ params: { path: { org_id: this.orgId, mfa_id: mfaId } },
899
+ body: {
900
+ challenge_id: challengeId,
901
+ credential,
902
+ },
903
+ parseAs: "json",
904
+ });
905
+ return assertOk(resp);
906
+ }
907
+
908
+ // #endregion
909
+
910
+ // #region SIGN: signEvm, signEth2, signStake, signUnstake, signAva, signBlob, signBtc, signSolana
911
+
912
+ /**
913
+ * Sign an EVM transaction.
914
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
915
+ * @param {EvmSignRequest} req What to sign.
916
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt.
917
+ * @return {Promise<EvmSignResponse | AcceptedResponse>} Signature (or MFA approval request).
918
+ */
919
+ async signEvm(
920
+ key: Key | string,
921
+ req: EvmSignRequest,
922
+ mfaReceipt?: MfaReceipt,
923
+ ): Promise<CubeSignerResponse<EvmSignResponse>> {
924
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
925
+ const sign = async (headers?: HeadersInit) => {
926
+ const client = await this.client();
927
+ const resp = await client.post("/v1/org/{org_id}/eth1/sign/{pubkey}", {
928
+ params: { path: { org_id: this.orgId, pubkey } },
929
+ body: req,
930
+ headers,
931
+ parseAs: "json",
932
+ });
933
+ return assertOk(resp);
934
+ };
935
+ return await CubeSignerResponse.create(sign, mfaReceipt);
936
+ }
937
+
938
+ /**
939
+ * Sign an Eth2/Beacon-chain validation message.
940
+ *
941
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
942
+ * @param {Eth2SignRequest} req What to sign.
943
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
944
+ * @return {Promise<Eth2SignResponse | AcceptedResponse>} Signature
945
+ */
946
+ async signEth2(
947
+ key: Key | string,
948
+ req: Eth2SignRequest,
949
+ mfaReceipt?: MfaReceipt,
950
+ ): Promise<CubeSignerResponse<Eth2SignResponse>> {
951
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
952
+ const sign = async (headers?: HeadersInit) => {
953
+ const client = await this.client();
954
+ const resp = await client.post("/v1/org/{org_id}/eth2/sign/{pubkey}", {
955
+ params: { path: { org_id: this.orgId, pubkey } },
956
+ body: req,
957
+ headers,
958
+ parseAs: "json",
959
+ });
960
+ return assertOk(resp);
961
+ };
962
+ return await CubeSignerResponse.create(sign, mfaReceipt);
963
+ }
964
+
965
+ /**
966
+ * Sign an Eth2/Beacon-chain deposit (or staking) message.
967
+ *
968
+ * @param {Eth2StakeRequest} req The request to sign.
969
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
970
+ * @return {Promise<Eth2StakeResponse | AcceptedResponse>} The response.
971
+ */
972
+ async signStake(
973
+ req: Eth2StakeRequest,
974
+ mfaReceipt?: MfaReceipt,
975
+ ): Promise<CubeSignerResponse<Eth2StakeResponse>> {
976
+ const sign = async (headers?: HeadersInit) => {
977
+ const client = await this.client();
978
+ const resp = await client.post("/v1/org/{org_id}/eth2/stake", {
979
+ params: { path: { org_id: this.orgId } },
980
+ body: req,
981
+ headers,
982
+ parseAs: "json",
983
+ });
984
+ return assertOk(resp);
985
+ };
986
+ return await CubeSignerResponse.create(sign, mfaReceipt);
987
+ }
988
+
989
+ /**
990
+ * Sign an Eth2/Beacon-chain unstake/exit request.
991
+ *
992
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
993
+ * @param {Eth2UnstakeRequest} req The request to sign.
994
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
995
+ * @return {Promise<Eth2UnstakeResponse | AcceptedResponse>} The response.
996
+ */
997
+ async signUnstake(
998
+ key: Key | string,
999
+ req: Eth2UnstakeRequest,
1000
+ mfaReceipt?: MfaReceipt,
1001
+ ): Promise<CubeSignerResponse<Eth2UnstakeResponse>> {
1002
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1003
+ const sign = async (headers?: HeadersInit) => {
1004
+ const client = await this.client();
1005
+ const resp = await client.post("/v1/org/{org_id}/eth2/unstake/{pubkey}", {
1006
+ params: { path: { org_id: this.orgId, pubkey } },
1007
+ body: req,
1008
+ headers,
1009
+ parseAs: "json",
1010
+ });
1011
+ return assertOk(resp);
1012
+ };
1013
+ return await CubeSignerResponse.create(sign, mfaReceipt);
1014
+ }
1015
+
1016
+ /**
1017
+ * Sign an Avalanche P- or X-chain message.
1018
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
1019
+ * @param {AvaTx} tx Avalanche message (transaction) to sign
1020
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
1021
+ * @return {Promise<AvaSignResponse | AcceptedResponse>} The response.
1022
+ */
1023
+ async signAva(
1024
+ key: Key | string,
1025
+ tx: AvaTx,
1026
+ mfaReceipt?: MfaReceipt,
1027
+ ): Promise<CubeSignerResponse<AvaSignResponse>> {
1028
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1029
+ const sign = async (headers?: HeadersInit) => {
1030
+ const req = <AvaSignRequest>{
1031
+ tx: tx as unknown,
1032
+ };
1033
+ const client = await this.client();
1034
+ const resp = await client.post("/v0/org/{org_id}/ava/sign/{pubkey}", {
1035
+ params: { path: { org_id: this.orgId, pubkey } },
1036
+ body: req,
1037
+ headers,
1038
+ parseAs: "json",
1039
+ });
1040
+ return assertOk(resp);
1041
+ };
1042
+ return await CubeSignerResponse.create(sign, mfaReceipt);
1043
+ }
1044
+
1045
+ /**
1046
+ * Sign a raw blob.
1047
+ *
1048
+ * This requires the key to have a '"AllowRawBlobSigning"' policy. This is because
1049
+ * signing arbitrary messages is, in general, dangerous (and you should instead
1050
+ * prefer typed end-points as used by, for example, `signEvm`). For Secp256k1 keys,
1051
+ * for example, you **must** call this function with a message that is 32 bytes long and
1052
+ * the output of a secure hash function.
1053
+ *
1054
+ * This function returns signatures serialized as;
1055
+ *
1056
+ * - ECDSA signatures are serialized as big-endian r and s plus recovery-id
1057
+ * byte v, which can in general take any of the values 0, 1, 2, or 3.
1058
+ *
1059
+ * - EdDSA signatures are serialized in the standard format.
1060
+ *
1061
+ * - BLS signatures are not supported on the blob-sign endpoint.
1062
+ *
1063
+ * @param {Key | string} key The key to sign with (either {@link Key} or its ID).
1064
+ * @param {BlobSignRequest} req What to sign
1065
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
1066
+ * @return {Promise<BlobSignResponse | AcceptedResponse>} The response.
1067
+ */
1068
+ async signBlob(
1069
+ key: Key | string,
1070
+ req: BlobSignRequest,
1071
+ mfaReceipt?: MfaReceipt,
1072
+ ): Promise<CubeSignerResponse<BlobSignResponse>> {
1073
+ const key_id = typeof key === "string" ? (key as string) : key.id;
1074
+ const sign = async (headers?: HeadersInit) => {
1075
+ const client = await this.client();
1076
+ const resp = await client.post("/v1/org/{org_id}/blob/sign/{key_id}", {
1077
+ params: {
1078
+ path: { org_id: this.orgId, key_id },
1079
+ },
1080
+ body: req,
1081
+ headers,
1082
+ parseAs: "json",
1083
+ });
1084
+ return assertOk(resp);
1085
+ };
1086
+ return await CubeSignerResponse.create(sign, mfaReceipt);
1087
+ }
1088
+
1089
+ /**
1090
+ * Sign a Bitcoin message.
1091
+ *
1092
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
1093
+ * @param {BtcSignRequest} req What to sign
1094
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
1095
+ * @return {Promise<BtcSignResponse | AcceptedResponse>} The response.
1096
+ */
1097
+ async signBtc(
1098
+ key: Key | string,
1099
+ req: BtcSignRequest,
1100
+ mfaReceipt?: MfaReceipt,
1101
+ ): Promise<CubeSignerResponse<BtcSignResponse>> {
1102
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1103
+ const sign = async (headers?: HeadersInit) => {
1104
+ const client = await this.client();
1105
+ const resp = await client.post("/v0/org/{org_id}/btc/sign/{pubkey}", {
1106
+ params: {
1107
+ path: { org_id: this.orgId, pubkey },
1108
+ },
1109
+ body: req,
1110
+ headers: headers,
1111
+ parseAs: "json",
1112
+ });
1113
+ return assertOk(resp);
1114
+ };
1115
+ return await CubeSignerResponse.create(sign, mfaReceipt);
1116
+ }
1117
+
1118
+ /**
1119
+ * Sign a Solana message.
1120
+ *
1121
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
1122
+ * @param {SolanaSignRequest} req What to sign
1123
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
1124
+ * @return {Promise<SolanaSignResponse | AcceptedResponse>} The response.
1125
+ */
1126
+ async signSolana(
1127
+ key: Key | string,
1128
+ req: SolanaSignRequest,
1129
+ mfaReceipt?: MfaReceipt,
1130
+ ): Promise<CubeSignerResponse<SolanaSignResponse>> {
1131
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
1132
+ const sign = async (headers?: HeadersInit) => {
1133
+ const client = await this.client();
1134
+ const resp = await client.post("/v0/org/{org_id}/solana/sign/{pubkey}", {
1135
+ params: { path: { org_id: this.orgId, pubkey } },
1136
+ body: req,
1137
+ headers,
1138
+ parseAs: "json",
1139
+ });
1140
+ return assertOk(resp);
1141
+ };
1142
+ return await CubeSignerResponse.create(sign, mfaReceipt);
1143
+ }
1144
+ // #endregion
1145
+
1146
+ /** HTTPS client */
1147
+ private async client(): Promise<Client> {
1148
+ return await this.#sessionMgr.client();
1149
+ }
1150
+ }
1151
+
1152
+ /**
1153
+ * Client to use to send requests to CubeSigner services
1154
+ * when authenticating using an OIDC token.
1155
+ */
1156
+ export class OidcClient {
1157
+ readonly #orgId: string;
1158
+ readonly #client: Client;
1159
+
1160
+ /**
1161
+ * @param {EnvInterface} env CubeSigner deployment
1162
+ * @param {string} orgId Target organization ID
1163
+ * @param {string} oidcToken User's OIDC token
1164
+ */
1165
+ constructor(env: EnvInterface, orgId: string, oidcToken: string) {
1166
+ this.#orgId = orgId;
1167
+ this.#client = createClient<paths>({
1168
+ baseUrl: env.SignerApiRoot,
1169
+ headers: {
1170
+ Authorization: oidcToken,
1171
+ },
1172
+ });
1173
+ }
1174
+
1175
+ /**
1176
+ * Exchange an OIDC token for a CubeSigner session token.
1177
+ * @param {List<string>} scopes The scopes for the new session
1178
+ * @param {RatchetConfig} lifetimes Lifetimes of the new session.
1179
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt (id + confirmation code)
1180
+ * @return {Promise<CubeSignerResponse<OidcAuthResponse>>} The session data.
1181
+ */
1182
+ async sessionCreate(
1183
+ scopes: Array<string>,
1184
+ lifetimes?: RatchetConfig,
1185
+ mfaReceipt?: MfaReceipt,
1186
+ ): Promise<CubeSignerResponse<OidcAuthResponse>> {
1187
+ const loginFn = async (headers?: HeadersInit) => {
1188
+ const resp = await this.#client.post("/v0/org/{org_id}/oidc", {
1189
+ params: { path: { org_id: this.#orgId } },
1190
+ headers,
1191
+ body: {
1192
+ scopes,
1193
+ tokens: lifetimes,
1194
+ },
1195
+ parseAs: "json",
1196
+ });
1197
+ return assertOk(resp);
1198
+ };
1199
+
1200
+ return await CubeSignerResponse.create(loginFn, mfaReceipt);
1201
+ }
1202
+
1203
+ /**
1204
+ * Exchange an OIDC token for a proof of authentication.
1205
+ *
1206
+ * @return {Promise<IdentityProof>} Proof of authentication
1207
+ */
1208
+ async identityProve(): Promise<IdentityProof> {
1209
+ const resp = await this.#client.post("/v0/org/{org_id}/identity/prove/oidc", {
1210
+ params: { path: { org_id: this.#orgId } },
1211
+ parseAs: "json",
1212
+ });
1213
+ return assertOk(resp);
1214
+ }
1215
+ }
1216
+
1217
+ const defaultSignerSessionLifetime: SignerSessionLifetime = {
1218
+ session: 604800, // 1 week
1219
+ auth: 300, // 5 min
1220
+ refresh: 86400, // 1 day
1221
+ grace: 30, // seconds
1222
+ };