@cubist-labs/cubesigner-sdk 0.1.77 → 0.2.15
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/dist/package.json +68 -0
- package/dist/src/api.d.ts +493 -0
- package/dist/src/api.js +1166 -0
- package/dist/src/client.d.ts +534 -10
- package/dist/src/client.js +355 -19
- package/dist/src/ethers/index.d.ts +34 -9
- package/dist/src/ethers/index.js +63 -19
- package/dist/src/index.d.ts +51 -70
- package/dist/src/index.js +83 -237
- package/dist/src/key.d.ts +35 -64
- package/dist/src/key.js +32 -96
- package/dist/src/mfa.d.ts +85 -14
- package/dist/src/mfa.js +146 -40
- package/dist/src/org.d.ts +42 -194
- package/dist/src/org.js +52 -336
- package/dist/src/paginator.js +1 -1
- package/dist/src/response.d.ts +101 -0
- package/dist/src/response.js +164 -0
- package/dist/src/role.d.ts +87 -83
- package/dist/src/role.js +79 -136
- package/dist/src/schema.d.ts +936 -28
- package/dist/src/schema.js +1 -1
- package/dist/src/schema_types.d.ts +109 -0
- package/dist/src/schema_types.js +3 -0
- package/dist/src/session/cognito_manager.d.ts +15 -3
- package/dist/src/session/cognito_manager.js +23 -5
- package/dist/src/session/session_manager.d.ts +1 -1
- package/dist/src/session/session_manager.js +3 -11
- package/dist/src/session/session_storage.js +1 -1
- package/dist/src/session/signer_session_manager.d.ts +10 -29
- package/dist/src/session/signer_session_manager.js +21 -80
- package/dist/src/signer_session.d.ts +15 -252
- package/dist/src/signer_session.js +25 -424
- package/dist/src/user_export.d.ts +52 -0
- package/dist/src/user_export.js +129 -0
- package/dist/src/util.d.ts +15 -0
- package/dist/src/util.js +33 -11
- package/package.json +13 -11
- package/src/api.ts +1395 -0
- package/src/client.ts +413 -12
- package/src/ethers/index.ts +74 -28
- package/src/index.ts +96 -273
- package/src/key.ts +36 -131
- package/src/{fido.ts → mfa.ts} +62 -38
- package/src/org.ts +54 -405
- package/src/response.ts +196 -0
- package/src/role.ts +113 -184
- package/src/schema.ts +936 -28
- package/src/schema_types.ts +110 -0
- package/src/session/cognito_manager.ts +33 -6
- package/src/session/session_manager.ts +2 -8
- package/src/session/signer_session_manager.ts +29 -110
- package/src/signer_session.ts +22 -597
- package/src/user_export.ts +116 -0
- package/src/util.ts +29 -10
package/src/client.ts
CHANGED
|
@@ -1,12 +1,413 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { SignerSessionManager, SignerSessionStorage } from "./session/signer_session_manager";
|
|
2
|
+
import { CognitoSessionManager } from "./session/cognito_manager";
|
|
3
|
+
import { CubeSignerApi, OidcClient } from "./api";
|
|
4
|
+
import { KeyType, Key } from "./key";
|
|
5
|
+
import { OrgInfo, RatchetConfig } from "./schema_types";
|
|
6
|
+
import { MfaReceipt } from "./mfa";
|
|
7
|
+
import { PageOpts } from "./paginator";
|
|
8
|
+
import { Role } from "./role";
|
|
9
|
+
|
|
10
|
+
// used in doc comments
|
|
11
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
12
|
+
import { AddFidoChallenge, MfaFidoChallenge, TotpChallenge } from "./mfa";
|
|
13
|
+
import { MemorySessionStorage } from "./session/session_storage";
|
|
14
|
+
|
|
15
|
+
/** Options for logging in with OIDC token */
|
|
16
|
+
export interface OidcAuthOptions {
|
|
17
|
+
/** Optional token lifetimes */
|
|
18
|
+
lifetimes?: RatchetConfig;
|
|
19
|
+
/** Optional MFA receipt */
|
|
20
|
+
mfaReceipt?: MfaReceipt;
|
|
21
|
+
/** Optional storage to use for the returned session (defaults to {@link MemorySessionStorage}) */
|
|
22
|
+
storage?: SignerSessionStorage;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Client to use to send requests to CubeSigner services
|
|
27
|
+
* when authenticating using a CubeSigner session token.
|
|
28
|
+
*/
|
|
29
|
+
export class CubeSignerClient extends CubeSignerApi {
|
|
30
|
+
/**
|
|
31
|
+
* Constructor.
|
|
32
|
+
* @param {SignerSessionManager} sessionMgr The session manager to use
|
|
33
|
+
* @param {string?} orgId Optional organization ID; if omitted, uses the org ID from the session manager.
|
|
34
|
+
*/
|
|
35
|
+
constructor(sessionMgr: SignerSessionManager, orgId?: string) {
|
|
36
|
+
super(sessionMgr, orgId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns a new instance of this class using the same session manager but targeting a different organization.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} orgId The organization ID.
|
|
43
|
+
* @return {CubeSignerClient} A new instance of this class using the same session manager but targeting different organization.
|
|
44
|
+
*/
|
|
45
|
+
withOrg(orgId?: string): CubeSignerClient {
|
|
46
|
+
return orgId ? new CubeSignerClient(this.sessionMgr, orgId) : this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Loads an existing management session and creates a {@link CubeSignerClient} instance.
|
|
51
|
+
*
|
|
52
|
+
* @return {Promise<CubeSignerClient>} New CubeSigner instance
|
|
53
|
+
*/
|
|
54
|
+
static async loadManagementSession(): Promise<CubeSignerClient> {
|
|
55
|
+
const mgr = await CognitoSessionManager.loadManagementSession();
|
|
56
|
+
// HACK: Ignore that sessionMgr may be a CognitoSessionManager and pretend that it
|
|
57
|
+
// is a SignerSessionManager; that's fine because the CubeSignerClient will
|
|
58
|
+
// almost always just call `await token()` on it, which works in both cases.
|
|
59
|
+
// NOTE: This will go away once `cs login` starts producing signer sessions.
|
|
60
|
+
return new CubeSignerClient(mgr as unknown as SignerSessionManager);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a new signing key.
|
|
65
|
+
* @param {KeyType} type The type of key to create.
|
|
66
|
+
* @param {string?} ownerId The owner of the key. Defaults to the session's user.
|
|
67
|
+
* @return {Key[]} The new keys.
|
|
68
|
+
*/
|
|
69
|
+
async createKey(type: KeyType, ownerId?: string): Promise<Key> {
|
|
70
|
+
return (await this.createKeys(type, 1, ownerId))[0];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create new signing keys.
|
|
75
|
+
* @param {KeyType} type The type of key to create.
|
|
76
|
+
* @param {number} count The number of keys to create.
|
|
77
|
+
* @param {string?} ownerId The owner of the keys. Defaults to the session's user.
|
|
78
|
+
* @return {Key[]} The new keys.
|
|
79
|
+
*/
|
|
80
|
+
async createKeys(type: KeyType, count: number, ownerId?: string): Promise<Key[]> {
|
|
81
|
+
const keys = await this.keysCreate(type, count, ownerId);
|
|
82
|
+
return keys.map((k) => new Key(this, k));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Derive a key of the given type using the given derivation path and mnemonic.
|
|
87
|
+
* The owner of the derived key will be the owner of the mnemonic.
|
|
88
|
+
*
|
|
89
|
+
* @param {KeyType} type Type of key to derive from the mnemonic.
|
|
90
|
+
* @param {string} derivationPath Mnemonic derivation path used to generate new key.
|
|
91
|
+
* @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
|
|
92
|
+
*
|
|
93
|
+
* @return {Key} newly derived key or undefined if it already exists.
|
|
94
|
+
*/
|
|
95
|
+
async deriveKey(
|
|
96
|
+
type: KeyType,
|
|
97
|
+
derivationPath: string,
|
|
98
|
+
mnemonicId: string,
|
|
99
|
+
): Promise<Key | undefined> {
|
|
100
|
+
return (await this.deriveKeys(type, [derivationPath], mnemonicId))[0];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Derive a set of keys of the given type using the given derivation paths and mnemonic.
|
|
105
|
+
*
|
|
106
|
+
* The owner of the derived keys will be the owner of the mnemonic.
|
|
107
|
+
*
|
|
108
|
+
* @param {KeyType} type Type of key to derive from the mnemonic.
|
|
109
|
+
* @param {string[]} derivationPaths Mnemonic derivation paths used to generate new key.
|
|
110
|
+
* @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
|
|
111
|
+
*
|
|
112
|
+
* @return {Key[]} newly derived keys.
|
|
113
|
+
*/
|
|
114
|
+
async deriveKeys(type: KeyType, derivationPaths: string[], mnemonicId: string): Promise<Key[]> {
|
|
115
|
+
const keys = await this.keysDerive(type, derivationPaths, mnemonicId);
|
|
116
|
+
return keys.map((k) => new Key(this, k));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create a new {@link OidcClient} that will use a given OIDC token for auth.
|
|
121
|
+
* @param {string} oidcToken The authentication token to use
|
|
122
|
+
* @return {OidcClient} New OIDC client.
|
|
123
|
+
*/
|
|
124
|
+
newOidcClient(oidcToken: string): OidcClient {
|
|
125
|
+
return new OidcClient(this.sessionMgr.env, this.orgId, oidcToken);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Authenticate an OIDC user and create a new session manager for them.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} oidcToken The OIDC token
|
|
132
|
+
* @param {List<string>} scopes The scopes of the resulting session
|
|
133
|
+
* @param {OidcAuthOptions} options Options.
|
|
134
|
+
* @return {Promise<SignerSessionManager>} The signer session manager
|
|
135
|
+
*/
|
|
136
|
+
async oidcAuth(
|
|
137
|
+
oidcToken: string,
|
|
138
|
+
scopes: Array<string>,
|
|
139
|
+
options?: OidcAuthOptions,
|
|
140
|
+
): Promise<SignerSessionManager> {
|
|
141
|
+
const oidcClient = this.newOidcClient(oidcToken);
|
|
142
|
+
const resp = await oidcClient.sessionCreate(scopes, options?.lifetimes, options?.mfaReceipt);
|
|
143
|
+
return await SignerSessionManager.loadFromStorage(new MemorySessionStorage(resp.data()));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create a new user in the organization and sends an invitation to that user.
|
|
148
|
+
*
|
|
149
|
+
* Same as {@link orgUserInvite}.
|
|
150
|
+
*/
|
|
151
|
+
get createUser() {
|
|
152
|
+
return this.orgUserInvite.bind(this);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a new OIDC user.
|
|
157
|
+
*
|
|
158
|
+
* Same as {@link orgUserCreateOidc}.
|
|
159
|
+
*/
|
|
160
|
+
get createOidcUser() {
|
|
161
|
+
return this.orgUserCreateOidc.bind(this);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Delete an existing OIDC user.
|
|
166
|
+
*
|
|
167
|
+
* Same as {@link orgUserDeleteOidc}.
|
|
168
|
+
*/
|
|
169
|
+
get deleteOidcUser() {
|
|
170
|
+
return this.orgUserDeleteOidc.bind(this);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* List users in the organization.
|
|
175
|
+
*
|
|
176
|
+
* Same as {@link orgUsersList}
|
|
177
|
+
*/
|
|
178
|
+
get users() {
|
|
179
|
+
return this.orgUsersList.bind(this);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Obtain information about the current user.
|
|
184
|
+
*
|
|
185
|
+
* Same as {@link userGet}
|
|
186
|
+
*/
|
|
187
|
+
get user() {
|
|
188
|
+
return this.userGet.bind(this);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get information about a specific org.
|
|
193
|
+
*
|
|
194
|
+
* @param {string?} orgId The ID or name of the org
|
|
195
|
+
* @return {Promise<OrgInfo>} CubeSigner client for the requested org.
|
|
196
|
+
*/
|
|
197
|
+
async org(orgId?: string): Promise<OrgInfo> {
|
|
198
|
+
return await this.withOrg(orgId).orgGet();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Obtain information about the current user.
|
|
203
|
+
*
|
|
204
|
+
* Same as {@link userGet}
|
|
205
|
+
*/
|
|
206
|
+
get aboutMe() {
|
|
207
|
+
return this.userGet.bind(this);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get a key by id.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} keyId The id of the key to get.
|
|
214
|
+
* @return {Key} The key.
|
|
215
|
+
*/
|
|
216
|
+
async getKey(keyId: string): Promise<Key> {
|
|
217
|
+
const keyInfo = await this.keyGet(keyId);
|
|
218
|
+
return new Key(this, keyInfo);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get all keys in the org.
|
|
223
|
+
*
|
|
224
|
+
* @param {KeyType?} type Optional key type to filter list for.
|
|
225
|
+
* @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
|
|
226
|
+
* @return {Promise<Key[]>} The keys.
|
|
227
|
+
*/
|
|
228
|
+
async orgKeys(type?: KeyType, page?: PageOpts): Promise<Key[]> {
|
|
229
|
+
const paginator = this.keysList(type, page);
|
|
230
|
+
const keys = await paginator.fetch();
|
|
231
|
+
return keys.map((k) => new Key(this, k));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Create a new role.
|
|
236
|
+
*
|
|
237
|
+
* @param {string?} name The name of the role.
|
|
238
|
+
* @return {Role} The new role.
|
|
239
|
+
*/
|
|
240
|
+
async createRole(name?: string): Promise<Role> {
|
|
241
|
+
const roleId = await this.roleCreate(name);
|
|
242
|
+
const roleInfo = await this.roleGet(roleId);
|
|
243
|
+
return new Role(this, roleInfo);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get a role by id or name.
|
|
248
|
+
*
|
|
249
|
+
* @param {string} roleId The id or name of the role to get.
|
|
250
|
+
* @return {Role} The role.
|
|
251
|
+
*/
|
|
252
|
+
async getRole(roleId: string): Promise<Role> {
|
|
253
|
+
const roleInfo = await this.roleGet(roleId);
|
|
254
|
+
return new Role(this, roleInfo);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* List all roles in the org.
|
|
259
|
+
*
|
|
260
|
+
* @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
|
|
261
|
+
* @return {Role[]} The roles.
|
|
262
|
+
*/
|
|
263
|
+
async listRoles(page?: PageOpts): Promise<Role[]> {
|
|
264
|
+
const roles = await this.rolesList(page).fetch();
|
|
265
|
+
return roles.map((r) => new Role(this, r));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* List all users in the org.
|
|
270
|
+
*
|
|
271
|
+
* Same as {@link orgUsersList}
|
|
272
|
+
*/
|
|
273
|
+
get listUsers() {
|
|
274
|
+
return this.orgUsersList.bind(this);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Approve a pending MFA request.
|
|
279
|
+
*
|
|
280
|
+
* Same as {@link mfaApprove}
|
|
281
|
+
*/
|
|
282
|
+
get approveMfaRequest() {
|
|
283
|
+
return this.mfaApprove.bind(this);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Approve a pending MFA request using TOTP.
|
|
288
|
+
*
|
|
289
|
+
* Same as {@link mfaApproveTotp}
|
|
290
|
+
*/
|
|
291
|
+
get totpApprove() {
|
|
292
|
+
return this.mfaApproveTotp.bind(this);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Initiate approval of an existing MFA request using FIDO.
|
|
297
|
+
*
|
|
298
|
+
* Returns a {@link MfaFidoChallenge} that must be answered by calling
|
|
299
|
+
* {@link MfaFidoChallenge.answer} or {@link fidoApproveComplete}.
|
|
300
|
+
*
|
|
301
|
+
* Same as {@link mfaApproveFidoInit}
|
|
302
|
+
*/
|
|
303
|
+
get fidoApproveStart() {
|
|
304
|
+
return this.mfaApproveFidoInit.bind(this);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Answer the MFA approval with FIDO challenge issued by {@link fidoApproveStart}.
|
|
309
|
+
*
|
|
310
|
+
* Same as {@link mfaApproveFidoComplete}
|
|
311
|
+
*/
|
|
312
|
+
get fidoApproveComplete() {
|
|
313
|
+
return this.mfaApproveFidoComplete.bind(this);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get a pending MFA request by its id.
|
|
318
|
+
*
|
|
319
|
+
* Same as {@link CubeSignerClient.getMfaInfo}
|
|
320
|
+
*/
|
|
321
|
+
get getMfaInfo() {
|
|
322
|
+
return this.mfaGet.bind(this);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* List pending MFA requests accessible to the current user.
|
|
327
|
+
*
|
|
328
|
+
* Same as {@link CubeSignerClient.mfaList}
|
|
329
|
+
*/
|
|
330
|
+
get listMfaInfos() {
|
|
331
|
+
return this.mfaList.bind(this);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Obtain a proof of authentication.
|
|
336
|
+
*
|
|
337
|
+
* Same as {@link CubeSignerClient.identityProve}
|
|
338
|
+
*/
|
|
339
|
+
get proveIdentity() {
|
|
340
|
+
return this.identityProve.bind(this);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Check if a given proof of OIDC authentication is valid.
|
|
345
|
+
*
|
|
346
|
+
* Same as {@link CubeSignerClient.identityVerify}
|
|
347
|
+
*/
|
|
348
|
+
get verifyIdentity() {
|
|
349
|
+
return this.identityVerify.bind(this);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Creates a request to add a new FIDO device.
|
|
354
|
+
*
|
|
355
|
+
* Returns a {@link AddFidoChallenge} that must be answered by calling {@link AddFidoChallenge.answer}.
|
|
356
|
+
*
|
|
357
|
+
* MFA may be required.
|
|
358
|
+
*
|
|
359
|
+
* Same as {@link CubeSignerClient.userRegisterFidoInit}
|
|
360
|
+
*/
|
|
361
|
+
get addFidoStart() {
|
|
362
|
+
return this.userRegisterFidoInit.bind(this);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Creates a request to change user's TOTP. Returns a {@link TotpChallenge}
|
|
367
|
+
* that must be answered by calling {@link TotpChallenge.answer} or
|
|
368
|
+
* {@link resetTotpComplete}.
|
|
369
|
+
*
|
|
370
|
+
* Same as {@link userResetTotpInit}
|
|
371
|
+
*/
|
|
372
|
+
get resetTotpStart() {
|
|
373
|
+
return this.userResetTotpInit.bind(this);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Answer the TOTP challenge issued by {@link resetTotpStart}. If successful,
|
|
378
|
+
* user's TOTP configuration will be updated to that of the TOTP challenge.
|
|
379
|
+
*
|
|
380
|
+
* Same as {@link userResetTotpComplete}
|
|
381
|
+
*/
|
|
382
|
+
get resetTotpComplete() {
|
|
383
|
+
return this.userResetTotpComplete.bind(this);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Verifies a given TOTP code against the current user's TOTP configuration.
|
|
388
|
+
* Throws an error if the verification fails.
|
|
389
|
+
*
|
|
390
|
+
* Same as {@link userVerifyTotp}
|
|
391
|
+
*/
|
|
392
|
+
get verifyTotp() {
|
|
393
|
+
return this.userVerifyTotp.bind(this);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Sign a stake request.
|
|
398
|
+
*
|
|
399
|
+
* Same as {@link signStake}
|
|
400
|
+
*/
|
|
401
|
+
get stake() {
|
|
402
|
+
return this.signStake.bind(this);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Sign an unstake request.
|
|
407
|
+
*
|
|
408
|
+
* Same as {@link signUnstake}
|
|
409
|
+
*/
|
|
410
|
+
get unstake() {
|
|
411
|
+
return this.signUnstake.bind(this);
|
|
412
|
+
}
|
|
413
|
+
}
|
package/src/ethers/index.ts
CHANGED
|
@@ -7,15 +7,10 @@ import {
|
|
|
7
7
|
getBytes,
|
|
8
8
|
toBeHex,
|
|
9
9
|
} from "ethers";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
MfaRequestInfo,
|
|
14
|
-
SignerSession,
|
|
15
|
-
SignResponse,
|
|
16
|
-
} from "../signer_session";
|
|
10
|
+
import { SignerSession } from "../signer_session";
|
|
11
|
+
import { CubeSignerResponse } from "../response";
|
|
12
|
+
import { BlobSignRequest, EvmSignRequest, MfaRequestInfo } from "../schema_types";
|
|
17
13
|
import { KeyInfo } from "../key";
|
|
18
|
-
import { CubeSigner } from "..";
|
|
19
14
|
|
|
20
15
|
/** Options for the signer */
|
|
21
16
|
interface SignerOptions {
|
|
@@ -31,8 +26,6 @@ interface SignerOptions {
|
|
|
31
26
|
* updates. Default is 1000ms
|
|
32
27
|
*/
|
|
33
28
|
mfaPollIntervalMs?: number;
|
|
34
|
-
/** Optional management session. Used to check for MFA updates */
|
|
35
|
-
managementSession?: CubeSigner;
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
/**
|
|
@@ -57,10 +50,8 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
57
50
|
/** The amount of time to wait between checks for MFA updates */
|
|
58
51
|
readonly #mfaPollIntervalMs: number;
|
|
59
52
|
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
/** Create new Signer instance
|
|
53
|
+
/**
|
|
54
|
+
* Create new Signer instance
|
|
64
55
|
* @param {KeyInfo | string} address The key or the eth address of the account to use.
|
|
65
56
|
* @param {SignerSession} signerSession The underlying Signer session.
|
|
66
57
|
* @param {SignerOptions} options The options to use for the Signer instance
|
|
@@ -76,7 +67,6 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
76
67
|
this.#signerSession = signerSession;
|
|
77
68
|
this.#onMfaPoll = options?.onMfaPoll ?? ((/* _mfaInfo: MfaRequestInfo */) => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
|
78
69
|
this.#mfaPollIntervalMs = options?.mfaPollIntervalMs ?? 1000;
|
|
79
|
-
this.#managementSession = options?.managementSession;
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
/** Resolves to the signer address. */
|
|
@@ -94,11 +84,13 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
94
84
|
}
|
|
95
85
|
|
|
96
86
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
87
|
+
* Construct a signing request from a transaction. This populates the transaction
|
|
88
|
+
* type to `0x02` (EIP-1559) unless set.
|
|
89
|
+
*
|
|
90
|
+
* @param {ethers.TransactionRequest} tx The transaction
|
|
91
|
+
* @return {EvmSignRequest} The EVM sign request to be sent to CubeSigner
|
|
100
92
|
*/
|
|
101
|
-
async
|
|
93
|
+
async evmSignRequestFromTx(tx: ethers.TransactionRequest): Promise<EvmSignRequest> {
|
|
102
94
|
// get the chain id from the network or tx
|
|
103
95
|
let chainId = tx.chainId;
|
|
104
96
|
if (chainId === undefined) {
|
|
@@ -116,17 +108,26 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
116
108
|
JsonRpcApiProvider.prototype.getRpcTransaction.call(null, tx);
|
|
117
109
|
rpcTx.type = toBeHex(tx.type ?? 0x02, 1); // we expect 0x0[0-2]
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
return <EvmSignRequest>{
|
|
120
112
|
chain_id: Number(chainId),
|
|
121
113
|
tx: rpcTx,
|
|
122
114
|
};
|
|
115
|
+
}
|
|
123
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Sign a transaction. This method will block if the key requires MFA approval.
|
|
119
|
+
* @param {ethers.TransactionRequest} tx The transaction to sign.
|
|
120
|
+
* @return {Promise<string>} Hex-encoded RLP encoding of the transaction and its signature.
|
|
121
|
+
*/
|
|
122
|
+
async signTransaction(tx: ethers.TransactionRequest): Promise<string> {
|
|
123
|
+
const req = await this.evmSignRequestFromTx(tx);
|
|
124
124
|
const res = await this.#signerSession.signEvm(this.#address, req);
|
|
125
125
|
const data = await this.#handleMfa(res);
|
|
126
126
|
return data.rlp_signed_tx;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Signs arbitrary messages. This uses ethers.js's [hashMessage](https://docs.ethers.org/v6/api/hashing/#hashMessage)
|
|
130
131
|
* to compute the EIP-191 digest and signs this digest using {@link Key#signBlob}.
|
|
131
132
|
* The key (for this session) must have the `"AllowRawBlobSigning"` policy attached.
|
|
132
133
|
* @param {string | Uint8Array} message The message to sign.
|
|
@@ -137,7 +138,8 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
137
138
|
return this.signBlob(digest);
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Signs EIP-712 typed data. This uses ethers.js's
|
|
141
143
|
* [TypedDataEncoder.hash](https://docs.ethers.org/v6/api/hashing/#TypedDataEncoder_hash)
|
|
142
144
|
* to compute the EIP-712 digest and signs this digest using {@link Key#signBlob}.
|
|
143
145
|
* The key (for this session) must have the `"AllowRawBlobSigning"` policy attached.
|
|
@@ -155,7 +157,8 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
155
157
|
return this.signBlob(digest);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* Sign arbitrary digest. This uses {@link Key#signBlob}.
|
|
159
162
|
* @param {string} digest The digest to sign.
|
|
160
163
|
* @return {Promise<string>} The signature.
|
|
161
164
|
*/
|
|
@@ -174,21 +177,64 @@ export class Signer extends ethers.AbstractSigner {
|
|
|
174
177
|
|
|
175
178
|
const res = await this.#signerSession.signBlob(this.#key.key_id, blobReq);
|
|
176
179
|
const data = await this.#handleMfa(res);
|
|
177
|
-
|
|
180
|
+
|
|
181
|
+
const v_adj = (parseInt(data.signature.slice(128), 16) + 27).toString(16);
|
|
182
|
+
return data.signature.slice(0, 128) + v_adj;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Initialize the signing a message using MFA approvals. This method populates
|
|
187
|
+
* missing fields. If the signing does not require MFA, this method throws.
|
|
188
|
+
* @param {ethers.TransactionRequest} tx The transaction to send.
|
|
189
|
+
* @return {string} The MFA id associated with the signing request.
|
|
190
|
+
*/
|
|
191
|
+
async sendTransactionMfaInit(tx: ethers.TransactionRequest): Promise<string> {
|
|
192
|
+
const popTx = await this.populateTransaction(tx);
|
|
193
|
+
const req = await this.evmSignRequestFromTx(popTx);
|
|
194
|
+
const res = await this.#signerSession.signEvm(this.#address, req);
|
|
195
|
+
return res.mfaId();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Send a transaction from an approved MFA request. The MFA request contains
|
|
200
|
+
* information about the approved signing request, which this method will
|
|
201
|
+
* execute.
|
|
202
|
+
* @param {MfaRequestInfo} mfaInfo The approved MFA request.
|
|
203
|
+
* @return {ethers.TransactionResponse} The result of submitting the transaction
|
|
204
|
+
*/
|
|
205
|
+
async sendTransactionMfaApproved(mfaInfo: MfaRequestInfo): Promise<ethers.TransactionResponse> {
|
|
206
|
+
if (!mfaInfo.request.path.includes("/eth1/sign/")) {
|
|
207
|
+
throw new Error(`Expected EVM transaction signing request, got ${mfaInfo.request.path}`);
|
|
208
|
+
}
|
|
209
|
+
if (!mfaInfo.request.path.includes(this.#address)) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Expected signing request for ${this.#address} but got ${mfaInfo.request.path}`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const signedTx = await this.#signerSession.signEvm(
|
|
216
|
+
this.#address,
|
|
217
|
+
mfaInfo.request.body as EvmSignRequest,
|
|
218
|
+
{
|
|
219
|
+
mfaId: mfaInfo.id,
|
|
220
|
+
mfaOrgId: this.#signerSession.orgId,
|
|
221
|
+
mfaConf: mfaInfo.receipt!.confirmation,
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
return await this.provider!.broadcastTransaction(signedTx.data().rlp_signed_tx);
|
|
178
225
|
}
|
|
179
226
|
|
|
180
227
|
/**
|
|
181
228
|
* If the sign request requires MFA, this method waits for approvals
|
|
182
|
-
*
|
|
183
|
-
* @param {SignResponse<U>} res The response of a sign request
|
|
229
|
+
* @param {CubeSignerResponse<U>} res The response of a sign request
|
|
184
230
|
* @return {Promise<U>} The sign data after MFA approvals
|
|
185
231
|
*/
|
|
186
|
-
async #handleMfa<U>(res:
|
|
232
|
+
async #handleMfa<U>(res: CubeSignerResponse<U>): Promise<U> {
|
|
187
233
|
while (res.requiresMfa()) {
|
|
188
234
|
await new Promise((resolve) => setTimeout(resolve, this.#mfaPollIntervalMs));
|
|
189
235
|
|
|
190
236
|
const mfaId = res.mfaId();
|
|
191
|
-
const mfaInfo = await this.#signerSession.getMfaInfo(
|
|
237
|
+
const mfaInfo = await this.#signerSession.getMfaInfo(mfaId);
|
|
192
238
|
this.#onMfaPoll(mfaInfo);
|
|
193
239
|
if (mfaInfo.receipt) {
|
|
194
240
|
res = await res.signWithMfaApproval({
|