@cubist-labs/cubesigner-sdk 0.1.26 → 0.1.77

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 (44) hide show
  1. package/README.md +94 -33
  2. package/dist/src/ethers/index.d.ts +25 -5
  3. package/dist/src/ethers/index.js +58 -16
  4. package/dist/src/fido.d.ts +76 -0
  5. package/dist/src/fido.js +148 -0
  6. package/dist/src/index.d.ts +148 -35
  7. package/dist/src/index.js +320 -53
  8. package/dist/src/key.d.ts +64 -8
  9. package/dist/src/key.js +91 -19
  10. package/dist/src/org.d.ts +98 -9
  11. package/dist/src/org.js +144 -29
  12. package/dist/src/paginator.d.ts +76 -0
  13. package/dist/src/paginator.js +99 -0
  14. package/dist/src/role.d.ts +20 -8
  15. package/dist/src/role.js +7 -5
  16. package/dist/src/schema.d.ts +2395 -393
  17. package/dist/src/schema.js +1 -1
  18. package/dist/src/session/cognito_manager.d.ts +59 -0
  19. package/dist/src/session/cognito_manager.js +111 -0
  20. package/dist/src/session/session_manager.d.ts +15 -0
  21. package/dist/src/session/session_manager.js +21 -2
  22. package/dist/src/session/session_storage.js +1 -1
  23. package/dist/src/session/signer_session_manager.d.ts +24 -12
  24. package/dist/src/session/signer_session_manager.js +45 -20
  25. package/dist/src/signer_session.d.ts +136 -38
  26. package/dist/src/signer_session.js +187 -80
  27. package/dist/src/util.d.ts +20 -0
  28. package/dist/src/util.js +31 -2
  29. package/package.json +12 -7
  30. package/src/ethers/index.ts +88 -16
  31. package/src/fido.ts +166 -0
  32. package/src/index.ts +366 -77
  33. package/src/key.ts +112 -16
  34. package/src/org.ts +200 -37
  35. package/src/paginator.ts +122 -0
  36. package/src/role.ts +24 -11
  37. package/src/schema.ts +2458 -449
  38. package/src/session/{management_session_manager.ts → cognito_manager.ts} +13 -15
  39. package/src/session/session_manager.ts +25 -1
  40. package/src/session/session_storage.ts +1 -1
  41. package/src/session/signer_session_manager.ts +57 -27
  42. package/src/signer_session.ts +266 -89
  43. package/src/util.ts +41 -0
  44. package/src/session/oidc_session_manager.ts +0 -193
@@ -1,10 +1,22 @@
1
1
  import assert from "assert";
2
- import { CubeSigner, Key, OidcSessionManager, OidcSessionStorage, Org } from ".";
2
+ import {
3
+ CubeSigner,
4
+ Key,
5
+ toKeyInfo,
6
+ Org,
7
+ KeyInfo,
8
+ MfaReceipt,
9
+ IdentityProof,
10
+ MfaFidoChallenge,
11
+ } from ".";
3
12
  import { components, paths } from "./client";
4
- import { assertOk } from "./util";
5
- import { SignerSessionManager, SignerSessionStorage } from "./session/signer_session_manager";
6
-
7
- export type KeyInfo = components["schemas"]["KeyInfo"];
13
+ import { JsonMap, assertOk } from "./util";
14
+ import { PublicKeyCredential } from "./fido";
15
+ import {
16
+ NewSessionResponse,
17
+ SignerSessionManager,
18
+ SignerSessionStorage,
19
+ } from "./session/signer_session_manager";
8
20
 
9
21
  /* eslint-disable */
10
22
  export type EvmSignRequest =
@@ -20,7 +32,9 @@ export type BlobSignRequest =
20
32
  export type BtcSignRequest =
21
33
  paths["/v0/org/{org_id}/btc/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
22
34
  export type SolanaSignRequest =
23
- paths["/v1/org/{org_id}/solana/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
35
+ paths["/v0/org/{org_id}/solana/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
36
+ export type AvaSignRequest =
37
+ paths["/v0/org/{org_id}/ava/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
24
38
 
25
39
  export type EvmSignResponse =
26
40
  components["responses"]["Eth1SignResponse"]["content"]["application/json"];
@@ -38,6 +52,8 @@ export type SolanaSignResponse =
38
52
  components["responses"]["SolanaSignResponse"]["content"]["application/json"];
39
53
  export type MfaRequestInfo =
40
54
  components["responses"]["MfaRequestInfo"]["content"]["application/json"];
55
+ export type AvaSignResponse =
56
+ components["responses"]["AvaSignResponse"]["content"]["application/json"];
41
57
 
42
58
  export type AcceptedResponse = components["schemas"]["AcceptedResponse"];
43
59
  export type ErrorResponse = components["schemas"]["ErrorResponse"];
@@ -47,20 +63,61 @@ export type BtcSignatureKind = components["schemas"]["BtcSignatureKind"];
47
63
  /** MFA request kind */
48
64
  export type MfaType = components["schemas"]["MfaType"];
49
65
 
66
+ /** Ava P- or X-chain transaction */
67
+ export type AvaTx = { P: AvaPChainTx } | { X: AvaXChainTx };
68
+
69
+ /** Ava P-chain transaction */
70
+ export type AvaPChainTx =
71
+ | { AddPermissionlessValidator: JsonMap }
72
+ | { AddSubnetValidator: JsonMap }
73
+ | { AddValidator: JsonMap }
74
+ | { CreateChain: JsonMap }
75
+ | { CreateSubnet: JsonMap }
76
+ | { Export: JsonMap }
77
+ | { Import: JsonMap };
78
+
79
+ /** Ava X-chain transaction */
80
+ export type AvaXChainTx = { Base: JsonMap } | { Export: JsonMap } | { Import: JsonMap };
81
+
50
82
  type SignFn<U> = (headers?: HeadersInit) => Promise<U | AcceptedResponse>;
51
83
 
84
+ export interface MfaRequired {
85
+ /** Org id */
86
+ org_id: string;
87
+ /** MFA request id */
88
+ id: string;
89
+ /** Optional MFA session */
90
+ session?: NewSessionResponse | null;
91
+ }
92
+
52
93
  /**
53
- * A response of a signing request.
94
+ * A response of a CubeSigner request.
54
95
  */
55
96
  export class SignResponse<U> {
56
- readonly #cs: CubeSigner;
57
- readonly #orgId: string;
58
97
  readonly #signFn: SignFn<U>;
59
98
  readonly #resp: U | AcceptedResponse;
99
+ /**
100
+ * Optional MFA id. Only set if there is an MFA request associated with the
101
+ * signing request
102
+ */
103
+ readonly #mfaRequired?: MfaRequired;
104
+
105
+ /** @return {string} The MFA id associated with this request */
106
+ mfaId(): string {
107
+ return this.#mfaRequired!.id;
108
+ }
60
109
 
61
- /** @return {boolean} True if this signing request requires an MFA approval */
110
+ /** @return {boolean} True if this request requires an MFA approval */
62
111
  requiresMfa(): boolean {
63
- return (this.#resp as AcceptedResponse).accepted?.MfaRequired !== undefined;
112
+ return this.#mfaRequired !== undefined;
113
+ }
114
+
115
+ /**
116
+ * Returns session information to use for any MFA approval requests (if any was included in the response).
117
+ * @return {ClientSessionInfo | undefined}
118
+ */
119
+ mfaSessionInfo(): NewSessionResponse | undefined {
120
+ return (this.#resp as AcceptedResponse).accepted?.MfaRequired?.session ?? undefined;
64
121
  }
65
122
 
66
123
  /** @return {U} The signed data */
@@ -69,47 +126,56 @@ export class SignResponse<U> {
69
126
  }
70
127
 
71
128
  /**
72
- * Approves the MFA request using a given signer session and a TOTP code.
73
- *
74
- * Note: This only works for MFA requests that require a single approval.
129
+ * Approves the MFA request using a given session and a TOTP code.
75
130
  *
76
131
  * @param {SignerSession} session Signer session to use
77
132
  * @param {string} code 6-digit TOTP code
78
133
  * @return {SignResponse<U>} The result of signing with the approval
79
134
  */
80
135
  async approveTotp(session: SignerSession, code: string): Promise<SignResponse<U>> {
81
- const mfaId = this.#mfaId();
82
-
136
+ assert(this.requiresMfa());
137
+ const mfaId = this.mfaId();
138
+ const mfaOrgId = this.#mfaRequired!.org_id;
83
139
  const mfaApproval = await session.totpApprove(mfaId, code);
84
140
  assert(mfaApproval.id === mfaId);
85
141
  const mfaConf = mfaApproval.receipt?.confirmation;
86
142
 
87
143
  if (!mfaConf) {
88
- throw new Error("MfaRequest has not been approved yet");
144
+ return this;
89
145
  }
90
146
 
91
- return await this.#signWithMfaApproval(mfaConf!);
147
+ return await this.signWithMfaApproval({ mfaId, mfaOrgId, mfaConf });
92
148
  }
93
149
 
94
150
  /**
95
- * Approves the MFA request using CubeSigner's management session.
96
- *
97
- * Note: This only works for MFA requests that require a single approval.
151
+ * Approves the MFA request using a given `CubeSigner` instance (i.e., its management session).
98
152
  *
153
+ * @param {CubeSigner} cs CubeSigner whose session to use
99
154
  * @return {SignResponse<U>} The result of signing with the approval
100
155
  */
101
- async approve(): Promise<SignResponse<U>> {
102
- const mfaId = this.#mfaId();
156
+ async approve(cs: CubeSigner): Promise<SignResponse<U>> {
157
+ assert(this.requiresMfa());
158
+ const mfaId = this.#mfaRequired!.id;
159
+ const mfaOrgId = this.#mfaRequired!.org_id;
103
160
 
104
- const mfaApproval = await Org.mfaApprove(this.#cs, this.#orgId, mfaId);
161
+ const mfaApproval = await Org.mfaApprove(cs, mfaOrgId, mfaId);
105
162
  assert(mfaApproval.id === mfaId);
106
163
  const mfaConf = mfaApproval.receipt?.confirmation;
107
164
 
108
165
  if (!mfaConf) {
109
- throw new Error("MfaRequest has not been approved yet");
166
+ return this;
110
167
  }
111
168
 
112
- return await this.#signWithMfaApproval(mfaConf);
169
+ return await this.signWithMfaApproval({ mfaId, mfaOrgId, mfaConf });
170
+ }
171
+
172
+ /**
173
+ * @param {MfaReceipt} mfaReceipt The MFA receipt
174
+ * @return {Promise<SignResponse<U>>} The result of signing after MFA approval
175
+ */
176
+ async signWithMfaApproval(mfaReceipt: MfaReceipt): Promise<SignResponse<U>> {
177
+ const headers = SignResponse.getMfaHeaders(mfaReceipt);
178
+ return new SignResponse(this.#signFn, await this.#signFn(headers));
113
179
  }
114
180
 
115
181
  // --------------------------------------------------------------------------
@@ -119,44 +185,45 @@ export class SignResponse<U> {
119
185
  /**
120
186
  * Constructor.
121
187
  *
122
- * @param {CubeSigner} cs The CubeSigner instance to use for requests
123
- * @param {string} orgId The org id of the corresponding signing request
124
188
  * @param {SignFn} signFn The signing function that this response is from.
125
189
  * This argument is used to resend requests with
126
190
  * different headers if needed.
127
191
  * @param {U | AcceptedResponse} resp The response as returned by the OpenAPI
128
192
  * client.
129
193
  */
130
- constructor(cs: CubeSigner, orgId: string, signFn: SignFn<U>, resp: U | AcceptedResponse) {
131
- this.#cs = cs;
132
- this.#orgId = orgId;
194
+ constructor(signFn: SignFn<U>, resp: U | AcceptedResponse) {
133
195
  this.#signFn = signFn;
134
196
  this.#resp = resp;
197
+ this.#mfaRequired = (this.#resp as AcceptedResponse).accepted?.MfaRequired;
135
198
  }
136
199
 
137
200
  /**
138
- * @param {string} mfaConf MFA request approval confirmation code
139
- * @return {Promise<SignResponse<U>>} The result of signing after MFA approval
201
+ * Static constructor.
202
+ * @param {SignFn} signFn The signing function that this response is from.
203
+ * This argument is used to resend requests with
204
+ * different headers if needed.
205
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
206
+ * @return {Promise<SignResponse<U>>} New instance of this class.
140
207
  */
141
- async #signWithMfaApproval(mfaConf: string): Promise<SignResponse<U>> {
142
- const mfaId = this.#mfaId();
143
-
144
- const headers = {
145
- "x-cubist-mfa-id": mfaId,
146
- "x-cubist-mfa-confirmation": mfaConf,
147
- };
148
- return new SignResponse(this.#cs, this.#orgId, this.#signFn, await this.#signFn(headers));
208
+ static async create<U>(signFn: SignFn<U>, mfaReceipt?: MfaReceipt): Promise<SignResponse<U>> {
209
+ const seed = await signFn(this.getMfaHeaders(mfaReceipt));
210
+ return new SignResponse(signFn, seed);
149
211
  }
150
212
 
151
213
  /**
152
- * @return {string} MFA id if MFA is required for this response; throws otherwise.
214
+ * Returns HTTP headers containing a given MFA receipt.
215
+ *
216
+ * @param {MfaReceipt} mfaReceipt MFA receipt
217
+ * @return {HeadersInit} Headers including that receipt
153
218
  */
154
- #mfaId(): string {
155
- const mfaRequired = (this.#resp as AcceptedResponse).accepted?.MfaRequired;
156
- if (!mfaRequired) {
157
- throw new Error("Request does not require MFA approval");
158
- }
159
- return mfaRequired.id;
219
+ static getMfaHeaders(mfaReceipt?: MfaReceipt): HeadersInit | undefined {
220
+ return mfaReceipt
221
+ ? {
222
+ "x-cubist-mfa-id": mfaReceipt.mfaId,
223
+ "x-cubist-mfa-org-id": mfaReceipt.mfaOrgId,
224
+ "x-cubist-mfa-confirmation": mfaReceipt.mfaConf,
225
+ }
226
+ : undefined;
160
227
  }
161
228
  }
162
229
 
@@ -197,15 +264,19 @@ export class SignerSessionInfo {
197
264
 
198
265
  /** Signer session. */
199
266
  export class SignerSession {
200
- readonly cs: CubeSigner;
201
- sessionMgr: OidcSessionManager | SignerSessionManager;
267
+ sessionMgr: SignerSessionManager;
202
268
  readonly #orgId: string;
203
269
 
270
+ /** Org id */
271
+ get orgId() {
272
+ return this.#orgId;
273
+ }
274
+
204
275
  /**
205
276
  * Returns the list of keys that this token grants access to.
206
277
  * @return {Key[]} The list of keys.
207
278
  */
208
- async keys(): Promise<Key[]> {
279
+ async keys(): Promise<KeyInfo[]> {
209
280
  const resp = await (
210
281
  await this.sessionMgr.client()
211
282
  ).get("/v0/org/{org_id}/token/keys", {
@@ -213,7 +284,7 @@ export class SignerSession {
213
284
  parseAs: "json",
214
285
  });
215
286
  const data = assertOk(resp);
216
- return data.keys.map((k: KeyInfo) => new Key(this.cs, this.#orgId, k));
287
+ return data.keys.map((k) => toKeyInfo(k));
217
288
  }
218
289
 
219
290
  /**
@@ -234,13 +305,72 @@ export class SignerSession {
234
305
  return assertOk(resp);
235
306
  }
236
307
 
308
+ /**
309
+ * Initiate approval of an existing MFA request using FIDO.
310
+ * @param {string} mfaId The MFA request ID.
311
+ * @return {Promise<MfaFidoChallenge>} A challenge that needs to be answered to complete the approval.
312
+ */
313
+ async fidoApproveStart(mfaId: string): Promise<MfaFidoChallenge> {
314
+ const client = await this.sessionMgr.client();
315
+ const resp = await client.post("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
316
+ params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
317
+ parseAs: "json",
318
+ });
319
+ const challenge = assertOk(resp);
320
+ return new MfaFidoChallenge(this, mfaId, challenge);
321
+ }
322
+
323
+ /**
324
+ * Complete a previously initiated MFA request approval using FIDO.
325
+ * @param {string} mfaId The MFA request ID
326
+ * @param {string} challengeId The challenge ID
327
+ * @param {PublicKeyCredential} credential The answer to the challenge
328
+ * @return {Promise<MfaRequestInfo>} The current status of the MFA request.
329
+ */
330
+ async fidoApproveComplete(
331
+ mfaId: string,
332
+ challengeId: string,
333
+ credential: PublicKeyCredential,
334
+ ): Promise<MfaRequestInfo> {
335
+ const client = await this.sessionMgr.client();
336
+ const resp = await client.patch("/v0/org/{org_id}/mfa/{mfa_id}/fido", {
337
+ params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
338
+ body: {
339
+ challenge_id: challengeId,
340
+ credential,
341
+ },
342
+ parseAs: "json",
343
+ });
344
+ return assertOk(resp);
345
+ }
346
+
347
+ /**
348
+ * Get a pending MFA request by its id.
349
+ * @param {CubeSigner} cs Management session to use (this argument will be removed in future versions)
350
+ * @param {string} mfaId The id of the MFA request.
351
+ * @return {Promise<MfaRequestInfo>} The MFA request.
352
+ */
353
+ async getMfaInfo(cs: CubeSigner, mfaId: string): Promise<MfaRequestInfo> {
354
+ const resp = await (
355
+ await cs.management()
356
+ ).get("/v0/org/{org_id}/mfa/{mfa_id}", {
357
+ params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
358
+ });
359
+ return assertOk(resp);
360
+ }
361
+
237
362
  /**
238
363
  * Submit an EVM sign request.
239
364
  * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
240
365
  * @param {EvmSignRequest} req What to sign.
366
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt.
241
367
  * @return {Promise<EvmSignResponse | AcceptedResponse>} Signature
242
368
  */
243
- async signEvm(key: Key | string, req: EvmSignRequest): Promise<SignResponse<EvmSignResponse>> {
369
+ async signEvm(
370
+ key: Key | string,
371
+ req: EvmSignRequest,
372
+ mfaReceipt?: MfaReceipt,
373
+ ): Promise<SignResponse<EvmSignResponse>> {
244
374
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
245
375
  const sign = async (headers?: HeadersInit) => {
246
376
  const resp = await (
@@ -253,16 +383,21 @@ export class SignerSession {
253
383
  });
254
384
  return assertOk(resp);
255
385
  };
256
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
386
+ return await SignResponse.create(sign, mfaReceipt);
257
387
  }
258
388
 
259
389
  /**
260
390
  * Submit an 'eth2' sign request.
261
391
  * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
262
392
  * @param {Eth2SignRequest} req What to sign.
393
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
263
394
  * @return {Promise<Eth2SignResponse | AcceptedResponse>} Signature
264
395
  */
265
- async signEth2(key: Key | string, req: Eth2SignRequest): Promise<SignResponse<Eth2SignResponse>> {
396
+ async signEth2(
397
+ key: Key | string,
398
+ req: Eth2SignRequest,
399
+ mfaReceipt?: MfaReceipt,
400
+ ): Promise<SignResponse<Eth2SignResponse>> {
266
401
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
267
402
  const sign = async (headers?: HeadersInit) => {
268
403
  const resp = await (
@@ -275,15 +410,19 @@ export class SignerSession {
275
410
  });
276
411
  return assertOk(resp);
277
412
  };
278
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
413
+ return await SignResponse.create(sign, mfaReceipt);
279
414
  }
280
415
 
281
416
  /**
282
417
  * Sign a stake request.
283
418
  * @param {Eth2StakeRequest} req The request to sign.
419
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
284
420
  * @return {Promise<Eth2StakeResponse | AcceptedResponse>} The response.
285
421
  */
286
- async stake(req: Eth2StakeRequest): Promise<SignResponse<Eth2StakeResponse>> {
422
+ async stake(
423
+ req: Eth2StakeRequest,
424
+ mfaReceipt?: MfaReceipt,
425
+ ): Promise<SignResponse<Eth2StakeResponse>> {
287
426
  const sign = async (headers?: HeadersInit) => {
288
427
  const resp = await (
289
428
  await this.sessionMgr.client()
@@ -295,18 +434,20 @@ export class SignerSession {
295
434
  });
296
435
  return assertOk(resp);
297
436
  };
298
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
437
+ return await SignResponse.create(sign, mfaReceipt);
299
438
  }
300
439
 
301
440
  /**
302
441
  * Sign an unstake request.
303
442
  * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
304
443
  * @param {Eth2UnstakeRequest} req The request to sign.
444
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
305
445
  * @return {Promise<Eth2UnstakeResponse | AcceptedResponse>} The response.
306
446
  */
307
447
  async unstake(
308
448
  key: Key | string,
309
449
  req: Eth2UnstakeRequest,
450
+ mfaReceipt?: MfaReceipt,
310
451
  ): Promise<SignResponse<Eth2UnstakeResponse>> {
311
452
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
312
453
  const sign = async (headers?: HeadersInit) => {
@@ -320,16 +461,21 @@ export class SignerSession {
320
461
  });
321
462
  return assertOk(resp);
322
463
  };
323
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
464
+ return await SignResponse.create(sign, mfaReceipt);
324
465
  }
325
466
 
326
467
  /**
327
468
  * Sign a raw blob.
328
469
  * @param {Key | string} key The key to sign with (either {@link Key} or its ID).
329
470
  * @param {BlobSignRequest} req What to sign
471
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
330
472
  * @return {Promise<BlobSignResponse | AcceptedResponse>} The response.
331
473
  */
332
- async signBlob(key: Key | string, req: BlobSignRequest): Promise<SignResponse<BlobSignResponse>> {
474
+ async signBlob(
475
+ key: Key | string,
476
+ req: BlobSignRequest,
477
+ mfaReceipt?: MfaReceipt,
478
+ ): Promise<SignResponse<BlobSignResponse>> {
333
479
  const key_id = typeof key === "string" ? (key as string) : key.id;
334
480
  const sign = async (headers?: HeadersInit) => {
335
481
  const resp = await (
@@ -344,16 +490,21 @@ export class SignerSession {
344
490
  });
345
491
  return assertOk(resp);
346
492
  };
347
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
493
+ return await SignResponse.create(sign, mfaReceipt);
348
494
  }
349
495
 
350
496
  /**
351
497
  * Sign a bitcoin message.
352
498
  * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
353
499
  * @param {BtcSignRequest} req What to sign
500
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
354
501
  * @return {Promise<BtcSignResponse | AcceptedResponse>} The response.
355
502
  */
356
- async signBtc(key: Key | string, req: BtcSignRequest): Promise<SignResponse<BtcSignResponse>> {
503
+ async signBtc(
504
+ key: Key | string,
505
+ req: BtcSignRequest,
506
+ mfaReceipt?: MfaReceipt,
507
+ ): Promise<SignResponse<BtcSignResponse>> {
357
508
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
358
509
  const sign = async (headers?: HeadersInit) => {
359
510
  const resp = await (
@@ -368,24 +519,26 @@ export class SignerSession {
368
519
  });
369
520
  return assertOk(resp);
370
521
  };
371
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
522
+ return await SignResponse.create(sign, mfaReceipt);
372
523
  }
373
524
 
374
525
  /**
375
526
  * Sign a solana message.
376
527
  * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
377
528
  * @param {SolanaSignRequest} req What to sign
529
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
378
530
  * @return {Promise<SolanaSignResponse | AcceptedResponse>} The response.
379
531
  */
380
532
  async signSolana(
381
533
  key: Key | string,
382
534
  req: SolanaSignRequest,
535
+ mfaReceipt?: MfaReceipt,
383
536
  ): Promise<SignResponse<SolanaSignResponse>> {
384
537
  const pubkey = typeof key === "string" ? (key as string) : key.materialId;
385
538
  const sign = async (headers?: HeadersInit) => {
386
539
  const resp = await (
387
540
  await this.sessionMgr.client()
388
- ).post("/v1/org/{org_id}/solana/sign/{pubkey}", {
541
+ ).post("/v0/org/{org_id}/solana/sign/{pubkey}", {
389
542
  params: { path: { org_id: this.#orgId, pubkey } },
390
543
  body: req,
391
544
  headers,
@@ -393,45 +546,69 @@ export class SignerSession {
393
546
  });
394
547
  return assertOk(resp);
395
548
  };
396
- return new SignResponse(this.cs, this.#orgId, sign, await sign());
549
+ return await SignResponse.create(sign, mfaReceipt);
397
550
  }
398
551
 
399
552
  /**
400
- * Loads an existing signer session from storage.
401
- * @param {CubeSigner} cs The CubeSigner instance
402
- * @param {SignerSessionStorage} storage The session storage to use
403
- * @return {Promise<SingerSession>} New signer session
553
+ * Sign an Avalanche P- or X-chain message.
554
+ * @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
555
+ * @param {AvaTx} tx Avalanche message (transaction) to sign
556
+ * @param {MfaReceipt} mfaReceipt Optional MFA receipt
557
+ * @return {Promise<AvaSignResponse | AcceptedResponse>} The response.
558
+ */
559
+ async signAva(
560
+ key: Key | string,
561
+ tx: AvaTx,
562
+ mfaReceipt?: MfaReceipt,
563
+ ): Promise<SignResponse<AvaSignResponse>> {
564
+ const pubkey = typeof key === "string" ? (key as string) : key.materialId;
565
+ const sign = async (headers?: HeadersInit) => {
566
+ const req = <AvaSignRequest>{
567
+ tx: tx as unknown,
568
+ };
569
+ const resp = await (
570
+ await this.sessionMgr.client()
571
+ ).post("/v0/org/{org_id}/ava/sign/{pubkey}", {
572
+ params: { path: { org_id: this.#orgId, pubkey } },
573
+ body: req,
574
+ headers,
575
+ parseAs: "json",
576
+ });
577
+ return assertOk(resp);
578
+ };
579
+ return await SignResponse.create(sign, mfaReceipt);
580
+ }
581
+
582
+ /**
583
+ * Obtain a proof of authentication.
584
+ *
585
+ * @return {Promise<IdentityProof>} Proof of authentication
404
586
  */
405
- static async loadSignerSession(
406
- cs: CubeSigner,
407
- storage: SignerSessionStorage,
408
- ): Promise<SignerSession> {
409
- const manager = await SignerSessionManager.loadFromStorage(cs, storage);
410
- return new SignerSession(cs, manager);
587
+ async proveIdentity(): Promise<IdentityProof> {
588
+ const client = await this.sessionMgr.client();
589
+ const resp = await client.post("/v0/org/{org_id}/identity/prove", {
590
+ params: { path: { org_id: this.#orgId } },
591
+ parseAs: "json",
592
+ });
593
+ return assertOk(resp);
411
594
  }
412
595
 
413
596
  /**
414
- * Loads an existing OIDC session from storage
415
- * @param {CubeSigner} cs The CubeSigner instance
416
- * @param {OidcSessionStorage} storage The storage to use
417
- * @return {Promise<SignerSession>} New signer session
597
+ * Loads an existing signer session from storage.
598
+ * @param {SignerSessionStorage} storage The session storage to use
599
+ * @return {Promise<SingerSession>} New signer session
418
600
  */
419
- static async loadOidcSession(
420
- cs: CubeSigner,
421
- storage: OidcSessionStorage,
422
- ): Promise<SignerSession> {
423
- const manager = await OidcSessionManager.loadFromStorage(storage);
424
- return new SignerSession(cs, manager);
601
+ static async loadSignerSession(storage: SignerSessionStorage): Promise<SignerSession> {
602
+ const manager = await SignerSessionManager.loadFromStorage(storage);
603
+ return new SignerSession(manager);
425
604
  }
426
605
 
427
606
  /**
428
607
  * Constructor.
429
- * @param {CubeSigner} cs The CubeSigner instance to use for requests
430
- * @param {OidcSessionManager | SignerSessionManager} sessionMgr The session manager to use
608
+ * @param {SignerSessionManager} sessionMgr The session manager to use
431
609
  * @internal
432
610
  */
433
- constructor(cs: CubeSigner, sessionMgr: OidcSessionManager | SignerSessionManager) {
434
- this.cs = cs;
611
+ constructor(sessionMgr: SignerSessionManager) {
435
612
  this.sessionMgr = sessionMgr;
436
613
  this.#orgId = sessionMgr.orgId;
437
614
  }
package/src/util.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import * as path from "path";
2
2
 
3
+ /** JSON map type */
4
+ export interface JsonMap {
5
+ [member: string]: string | number | boolean | null | JsonArray | JsonMap;
6
+ }
7
+
8
+ /** JSON array type */
9
+ export type JsonArray = Array<string | number | boolean | null | JsonArray | JsonMap>;
10
+
3
11
  /**
4
12
  * Directory where CubeSigner stores config files.
5
13
  * @return {string} Config dir
@@ -56,3 +64,36 @@ export function assertOk<D, T>(resp: ResponseType<D, T>, description?: string):
56
64
  }
57
65
  return resp.data;
58
66
  }
67
+
68
+ /**
69
+ * Browser-friendly helper for decoding a 'base64url'-encoded string into a byte array.
70
+ *
71
+ * @param {string} b64url The 'base64url'-encoded string to decode
72
+ * @return {Uint8Array} Decoded byte array
73
+ */
74
+ export function decodeBase64Url(b64url: string): Uint8Array {
75
+ const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/").replace(/=*$/g, "");
76
+
77
+ // NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
78
+ return typeof Buffer === "function"
79
+ ? Buffer.from(b64, "base64")
80
+ : Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
81
+ }
82
+
83
+ /**
84
+ * Browser-friendly helper for encoding a byte array into a 'base64url`-encoded string.
85
+ *
86
+ * @param {Iterable<number>} buffer The byte array to encode
87
+ * @return {string} The 'base64url' encoding of the byte array.
88
+ */
89
+ export function encodeToBase64Url(buffer: Iterable<number>): string {
90
+ const bytes = new Uint8Array(buffer);
91
+
92
+ // NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
93
+ const b64 =
94
+ typeof Buffer === "function"
95
+ ? Buffer.from(bytes).toString("base64")
96
+ : btoa(bytes.reduce((s, b) => s + String.fromCharCode(b), ""));
97
+
98
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
99
+ }