@cubist-labs/cubesigner-sdk 0.3.29 → 0.4.24-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -113
- package/dist/cjs/package.json +5 -2
- package/dist/cjs/src/api.d.ts +1 -2
- package/dist/cjs/src/api.js +2 -4
- package/dist/cjs/src/client/api_client.d.ts +611 -0
- package/dist/cjs/src/client/api_client.js +1211 -0
- package/dist/cjs/src/client/base_client.d.ts +92 -0
- package/dist/cjs/src/client/base_client.js +174 -0
- package/dist/cjs/src/client/session.d.ts +151 -0
- package/dist/cjs/src/client/session.js +193 -0
- package/dist/cjs/src/client.d.ts +83 -558
- package/dist/cjs/src/client.js +109 -358
- package/dist/cjs/src/error.d.ts +2 -2
- package/dist/cjs/src/error.js +4 -2
- package/dist/cjs/src/events.d.ts +34 -93
- package/dist/cjs/src/events.js +42 -198
- package/dist/cjs/src/evm/index.d.ts +10 -10
- package/dist/cjs/src/evm/index.js +33 -31
- package/dist/cjs/src/fetch.d.ts +53 -0
- package/dist/cjs/src/fetch.js +133 -0
- package/dist/cjs/src/index.d.ts +5 -170
- package/dist/cjs/src/index.js +8 -247
- package/dist/cjs/src/key.d.ts +116 -35
- package/dist/cjs/src/key.js +144 -37
- package/dist/cjs/src/mfa.d.ts +94 -13
- package/dist/cjs/src/mfa.js +156 -20
- package/dist/cjs/src/org.d.ts +358 -41
- package/dist/cjs/src/org.js +310 -37
- package/dist/cjs/src/org_event_processor.d.ts +57 -0
- package/dist/cjs/src/org_event_processor.js +137 -0
- package/dist/cjs/src/response.d.ts +22 -20
- package/dist/cjs/src/response.js +57 -40
- package/dist/cjs/src/retry.d.ts +36 -0
- package/dist/cjs/src/retry.js +42 -0
- package/dist/cjs/src/role.d.ts +46 -11
- package/dist/cjs/src/role.js +30 -34
- package/dist/cjs/src/schema.d.ts +2 -2
- package/dist/cjs/src/schema.js +1 -1
- package/dist/cjs/src/schema_types.d.ts +18 -4
- package/dist/cjs/src/schema_types.js +1 -1
- package/dist/cjs/src/signer_session.d.ts +3 -28
- package/dist/cjs/src/signer_session.js +9 -44
- package/dist/cjs/src/user_export.d.ts +1 -1
- package/dist/cjs/src/user_export.js +1 -1
- package/dist/esm/package.json +5 -2
- package/dist/esm/src/api.d.ts +1 -2
- package/dist/esm/src/api.js +2 -4
- package/dist/esm/src/client/api_client.d.ts +611 -0
- package/dist/esm/src/client/api_client.js +1207 -0
- package/dist/esm/src/client/base_client.d.ts +92 -0
- package/dist/esm/src/client/base_client.js +167 -0
- package/dist/esm/src/client/session.d.ts +151 -0
- package/dist/esm/src/client/session.js +182 -0
- package/dist/esm/src/client.d.ts +83 -558
- package/dist/esm/src/client.js +109 -358
- package/dist/esm/src/error.d.ts +2 -2
- package/dist/esm/src/error.js +4 -2
- package/dist/esm/src/events.d.ts +34 -93
- package/dist/esm/src/events.js +41 -194
- package/dist/esm/src/evm/index.d.ts +10 -10
- package/dist/esm/src/evm/index.js +33 -31
- package/dist/esm/src/fetch.d.ts +53 -0
- package/dist/esm/src/fetch.js +127 -0
- package/dist/esm/src/index.d.ts +5 -170
- package/dist/esm/src/index.js +6 -242
- package/dist/esm/src/key.d.ts +116 -35
- package/dist/esm/src/key.js +144 -36
- package/dist/esm/src/mfa.d.ts +94 -13
- package/dist/esm/src/mfa.js +154 -19
- package/dist/esm/src/org.d.ts +358 -41
- package/dist/esm/src/org.js +311 -38
- package/dist/esm/src/org_event_processor.d.ts +57 -0
- package/dist/esm/src/org_event_processor.js +133 -0
- package/dist/esm/src/response.d.ts +22 -20
- package/dist/esm/src/response.js +57 -40
- package/dist/esm/src/retry.d.ts +36 -0
- package/dist/esm/src/retry.js +36 -0
- package/dist/esm/src/role.d.ts +46 -11
- package/dist/esm/src/role.js +31 -35
- package/dist/esm/src/schema.d.ts +2 -2
- package/dist/esm/src/schema.js +1 -1
- package/dist/esm/src/schema_types.d.ts +18 -4
- package/dist/esm/src/schema_types.js +1 -1
- package/dist/esm/src/signer_session.d.ts +3 -28
- package/dist/esm/src/signer_session.js +8 -42
- package/dist/esm/src/user_export.d.ts +1 -1
- package/dist/esm/src/user_export.js +1 -1
- package/dist/package.json +36 -0
- package/dist/spec/env/beta.json +9 -0
- package/dist/spec/env/gamma.json +9 -0
- package/dist/spec/env/prod.json +9 -0
- package/dist/src/api.d.ts +634 -0
- package/dist/src/api.js +1309 -0
- package/dist/src/client.d.ts +575 -0
- package/dist/src/client.js +381 -0
- package/dist/src/env.d.ts +15 -0
- package/dist/src/env.js +35 -0
- package/dist/src/error.d.ts +29 -0
- package/dist/src/error.js +36 -0
- package/dist/src/events.d.ts +84 -0
- package/dist/src/events.js +195 -0
- package/dist/src/index.d.ts +207 -0
- package/dist/src/index.js +308 -0
- package/dist/src/key.d.ts +152 -0
- package/dist/src/key.js +242 -0
- package/dist/src/mfa.d.ts +94 -0
- package/dist/src/mfa.js +169 -0
- package/dist/src/org.d.ts +99 -0
- package/dist/src/org.js +95 -0
- package/dist/src/paginator.d.ts +76 -0
- package/dist/src/paginator.js +99 -0
- package/dist/src/response.d.ts +101 -0
- package/dist/src/response.js +164 -0
- package/dist/src/role.d.ts +283 -0
- package/dist/src/role.js +253 -0
- package/dist/src/schema.d.ts +6209 -0
- package/dist/src/schema.js +7 -0
- package/dist/src/schema_types.d.ts +113 -0
- package/dist/src/schema_types.js +3 -0
- package/dist/src/session/session_storage.d.ts +47 -0
- package/dist/src/session/session_storage.js +76 -0
- package/dist/src/session/signer_session_manager.d.ts +125 -0
- package/dist/src/session/signer_session_manager.js +239 -0
- package/dist/src/signer_session.d.ts +41 -0
- package/dist/src/signer_session.js +77 -0
- package/dist/src/user_export.d.ts +52 -0
- package/dist/src/user_export.js +129 -0
- package/dist/src/util.d.ts +61 -0
- package/dist/src/util.js +97 -0
- package/package.json +5 -2
- package/src/{api.ts → client/api_client.ts} +603 -753
- package/src/client/base_client.ts +226 -0
- package/src/client/session.ts +269 -0
- package/src/client.ts +108 -413
- package/src/error.ts +5 -3
- package/src/events.ts +49 -201
- package/src/evm/index.ts +36 -39
- package/src/fetch.ts +194 -0
- package/src/index.ts +5 -285
- package/src/key.ts +214 -56
- package/src/mfa.ts +179 -23
- package/src/org.ts +349 -40
- package/src/response.ts +56 -39
- package/src/retry.ts +57 -0
- package/src/role.ts +75 -41
- package/src/schema.ts +4 -1
- package/src/schema_types.ts +20 -4
- package/src/signer_session.ts +6 -42
- package/src/user_export.ts +1 -1
- package/src/session/session_storage.ts +0 -41
- package/src/session/signer_session_manager.ts +0 -315
package/README.md
CHANGED
|
@@ -39,6 +39,9 @@ cs login owner@example.com --env '<gamma|prod|...>'
|
|
|
39
39
|
|
|
40
40
|
## Getting started
|
|
41
41
|
|
|
42
|
+
> [!TIP]
|
|
43
|
+
> For migration from `v0.3.*` to `v0.4.*`, please see [MIGRATION.md](../../MIGRATION.md#v03-to-v04)
|
|
44
|
+
|
|
42
45
|
In this section we are going to walk through a simple CubeSigner
|
|
43
46
|
setup. We'll create a signing key, then sign some EVM
|
|
44
47
|
transactions, and then add a security policy to restrict the kinds of
|
|
@@ -53,32 +56,30 @@ examples below.
|
|
|
53
56
|
|
|
54
57
|
```typescript
|
|
55
58
|
import * as cs from "@cubist-labs/cubesigner-sdk";
|
|
56
|
-
import {
|
|
59
|
+
import { JsonFileSessionManager, defaultManagementSessionManager } from "@cubist-labs/cubesigner-sdk-fs-storage";
|
|
57
60
|
import assert from "assert";
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
### Instantiate `CubeSignerClient`
|
|
61
64
|
|
|
62
65
|
The first order of business is to create an instance of `CubeSignerClient`.
|
|
63
|
-
We can do that by simply loading
|
|
66
|
+
We can do that by simply loading a session token from the
|
|
64
67
|
default location on disk (which is where the `cs login` command saves
|
|
65
68
|
it):
|
|
66
69
|
|
|
67
70
|
```typescript
|
|
68
|
-
const cubesigner = await
|
|
71
|
+
const cubesigner = await cs.CubeSignerClient.create(defaultManagementSessionManager());
|
|
69
72
|
```
|
|
70
73
|
|
|
71
74
|
Alternatively, a `CubeSignerClient` instance can be created by explicitly
|
|
72
75
|
providing a session manager:
|
|
73
76
|
|
|
74
77
|
```typescript
|
|
75
|
-
//
|
|
76
|
-
const fileStorage = new
|
|
78
|
+
// Create a session manager backed by a JSON file
|
|
79
|
+
const fileStorage = new JsonFileSessionManager(
|
|
77
80
|
`${process.env.HOME}/.config/cubesigner/management-session.json`,
|
|
78
81
|
);
|
|
79
|
-
|
|
80
|
-
const sessionMgr = await cs.SignerSessionManager.loadFromStorage(fileStorage);
|
|
81
|
-
new cs.CubeSignerClient(sessionMgr);
|
|
82
|
+
await cs.CubeSignerClient.create(fileStorage);
|
|
82
83
|
```
|
|
83
84
|
|
|
84
85
|
### Get `User` and `Org` info
|
|
@@ -87,13 +88,13 @@ We can now obtain some information about the logged-in user and the
|
|
|
87
88
|
organization the user belongs to:
|
|
88
89
|
|
|
89
90
|
```typescript
|
|
90
|
-
const me = await cubesigner.
|
|
91
|
+
const me = await cubesigner.user();
|
|
91
92
|
console.log(me);
|
|
92
93
|
assert(me.user_id); // each user has a globally unique ID
|
|
93
94
|
assert(me.org_ids); // IDs of all organizations this user is a member of
|
|
94
95
|
assert(me.org_ids.length === 1); // assume that the user is a member of exactly one org
|
|
95
96
|
|
|
96
|
-
const org =
|
|
97
|
+
const org = await cubesigner.org();
|
|
97
98
|
assert(await org.enabled()); // assume that the org is enabled
|
|
98
99
|
```
|
|
99
100
|
|
|
@@ -117,41 +118,6 @@ assert(await secpKey.type(), cs.Secp256k1.Evm);
|
|
|
117
118
|
console.log(`Created '${cs.Secp256k1.Evm}' key ${secpKey.id}`);
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
### Create a `Role` and a `SignerSession`
|
|
121
|
-
|
|
122
|
-
CubeSigner differentiates between _management_ and _signer_ sessions
|
|
123
|
-
since managing keys (and an org) is often separate from using keys.
|
|
124
|
-
|
|
125
|
-
Everything we've done so far has been using a management session. To
|
|
126
|
-
sign a message with a key, we now need to create a signer
|
|
127
|
-
session. Signer sessions are associated with roles (which we discuss
|
|
128
|
-
in this section) or users (which we discuss [later](#create-a-session-for-an-oidc-user)).
|
|
129
|
-
|
|
130
|
-
You can think of roles as groups
|
|
131
|
-
that give certain users access to certain keys. To get started, let's
|
|
132
|
-
create a `Role` and then simply call `createSession` on it:
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
const role = await org.createRole();
|
|
136
|
-
const sessionStorage = new cs.MemorySessionStorage<cs.SignerSessionData>();
|
|
137
|
-
const session = await role.createSession(sessionStorage, "readme");
|
|
138
|
-
console.log(`Created signer session for role '${role.id}'`);
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Sessions have lifetimes, so they need to periodically refresh
|
|
142
|
-
themselves to stay valid. When that happens, the refreshed session
|
|
143
|
-
needs to be saved somewhere, which is why the `createSession` method
|
|
144
|
-
requires an instance of `SessionStorage`. In this example, we don't
|
|
145
|
-
plan to persist the session across multiple runs, so a simple
|
|
146
|
-
in-memory storage suffices; otherwise, opting for
|
|
147
|
-
`JsonFileSessionStorage` would be a better idea, i.e.,
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
// this storage persists the signer session token to a file
|
|
151
|
-
// named 'session.json' in the current working directory
|
|
152
|
-
new JsonFileSessionStorage("session.json");
|
|
153
|
-
```
|
|
154
|
-
|
|
155
121
|
### Sign an Ethereum transaction
|
|
156
122
|
|
|
157
123
|
Let's create a dummy `EvmSignRequest`.
|
|
@@ -172,39 +138,44 @@ const eth1Request = <cs.EvmSignRequest>{
|
|
|
172
138
|
|
|
173
139
|
It seems we have everything in place to sign this request with the
|
|
174
140
|
previously created key. However, attempting to do so fails with `403
|
|
175
|
-
Forbidden` saying something like `
|
|
176
|
-
access key 'Key#...'`:
|
|
141
|
+
Forbidden` saying something like `Session does not have the required scopes...`
|
|
177
142
|
|
|
178
143
|
```typescript
|
|
179
144
|
try {
|
|
180
|
-
console.log("Trying to sign
|
|
181
|
-
await
|
|
182
|
-
assert(false, "Must not be allowed to sign
|
|
145
|
+
console.log("Trying to sign with improper scopes");
|
|
146
|
+
await secpKey.signEvm(eth1Request);
|
|
147
|
+
assert(false, "Must not be allowed to sign without scopes");
|
|
183
148
|
} catch (e) {
|
|
184
|
-
assert(`${e}`.includes("not
|
|
149
|
+
assert(`${e}`.includes("Session does not have required scopes"));
|
|
185
150
|
}
|
|
186
151
|
```
|
|
187
152
|
|
|
188
|
-
By default,
|
|
189
|
-
|
|
190
|
-
After we do that, the `signEvm` call should succeed:
|
|
153
|
+
By default, the sessions created by the CLI cannot perform signing operations. All sessions have a series of *scopes* which
|
|
154
|
+
which determine what that session can be used for. We'll talk more about this later, but for now just know that we need a new session with the proper scopes:
|
|
191
155
|
|
|
192
156
|
```typescript
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
157
|
+
let signingClient = await cs.CubeSignerClient.create(
|
|
158
|
+
// declare the "sign:*" scope which allows us to sign using any keys the
|
|
159
|
+
// account has access to
|
|
160
|
+
await cubesigner.org().createSession("readme signing demo", ["sign:*"]));
|
|
161
|
+
const signingKey = new cs.Key(signingClient, secpKey.cached);
|
|
162
|
+
let sig = await signingKey.signEvm(eth1Request);
|
|
196
163
|
console.log(sig.data());
|
|
197
164
|
assert(sig.data().rlp_signed_tx);
|
|
198
165
|
```
|
|
199
166
|
|
|
200
167
|
### Using ethers.js instead of the SDK directly
|
|
201
168
|
|
|
169
|
+
If your application uses ethers.js, you can configure it to use CubeSigner to
|
|
170
|
+
sign transactions.
|
|
171
|
+
|
|
172
|
+
|
|
202
173
|
```typescript
|
|
203
174
|
import { Signer } from "@cubist-labs/cubesigner-sdk-ethers-v6";
|
|
204
175
|
import { ethers } from "ethers";
|
|
205
176
|
|
|
206
177
|
// Create new Signer
|
|
207
|
-
const ethersSigner = new Signer(secpKey.materialId,
|
|
178
|
+
const ethersSigner = new Signer(secpKey.materialId, signingClient);
|
|
208
179
|
assert((await ethersSigner.getAddress()) === ethers.getAddress(secpKey.materialId));
|
|
209
180
|
// sign transaction as usual:
|
|
210
181
|
console.log(
|
|
@@ -217,9 +188,32 @@ console.log(
|
|
|
217
188
|
);
|
|
218
189
|
```
|
|
219
190
|
|
|
191
|
+
|
|
192
|
+
### Access control with `Role`
|
|
193
|
+
|
|
194
|
+
CubeSigner uses roles to control access to keys when more than one user wants to
|
|
195
|
+
use them. You can think of roles as groups
|
|
196
|
+
that give certain users access to certain keys. To get started, let's
|
|
197
|
+
create a `Role` and then simply call `createSession` on it:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Create a role, implicitly adding ourselves as a member
|
|
201
|
+
const role = await org.createRole();
|
|
202
|
+
|
|
203
|
+
console.log("Adding key to role, then signing an Ethereum transaction");
|
|
204
|
+
await role.addKey(secpKey);
|
|
205
|
+
|
|
206
|
+
// Members of the role can create sessions which can only access keys in the role
|
|
207
|
+
const roleClient = await cs.CubeSignerClient.create(
|
|
208
|
+
// Role sessions implicitly have the "sign:*" scope
|
|
209
|
+
await role.createSession("readme")
|
|
210
|
+
);
|
|
211
|
+
console.log(`Created client for role '${role.id}'`);
|
|
212
|
+
```
|
|
213
|
+
|
|
220
214
|
### Set security policies
|
|
221
215
|
|
|
222
|
-
When we add a `Secp256k1.Evm` key to a role (as we did above), a
|
|
216
|
+
When we add a `Secp256k1.Evm` key to a role (as we did above), a client
|
|
223
217
|
associated with that role allows us to sign **any** Ethereum
|
|
224
218
|
transaction with that key. If that seems too permissive, we can attach a security
|
|
225
219
|
policy to restrict the allowed usages of this key in this role.
|
|
@@ -231,7 +225,8 @@ recipient, we can attach a `TxReceiver` policy to our key:
|
|
|
231
225
|
console.log("Setting transaction receiver policy");
|
|
232
226
|
await secpKey.setPolicy([{ TxReceiver: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b" }]);
|
|
233
227
|
console.log("Signing transaction");
|
|
234
|
-
|
|
228
|
+
const roleKey = new cs.Key(roleClient, secpKey.cached);
|
|
229
|
+
sig = await roleKey.signEvm(eth1Request);
|
|
235
230
|
assert(sig.data().rlp_signed_tx);
|
|
236
231
|
```
|
|
237
232
|
|
|
@@ -241,7 +236,7 @@ indeed gets rejected:
|
|
|
241
236
|
```typescript
|
|
242
237
|
console.log("Signing a transaction to a different receiver must be rejected");
|
|
243
238
|
try {
|
|
244
|
-
await
|
|
239
|
+
await roleKey.signEvm({
|
|
245
240
|
chain_id: 1,
|
|
246
241
|
tx: <any>{
|
|
247
242
|
...eth1Request.tx,
|
|
@@ -259,16 +254,18 @@ try {
|
|
|
259
254
|
> `Key::appendPolicy` instead of `Key::setPolicy` to append to
|
|
260
255
|
> existing policies.
|
|
261
256
|
|
|
257
|
+
|
|
258
|
+
|
|
262
259
|
### Sign a raw blob
|
|
263
260
|
|
|
264
|
-
The `
|
|
261
|
+
The `CubeSignerClient` class exposes the `signBlob` method, which signs
|
|
265
262
|
an arbitrary (raw, uninterpreted) bag of bytes with a given key. This
|
|
266
263
|
operation, however, is not permitted by default; it is permanently
|
|
267
264
|
disabled for `BLS` keys, and for other key types it can be enabled by
|
|
268
265
|
attaching an `"AllowRawBlobSigning"` policy:
|
|
269
266
|
|
|
270
267
|
```typescript
|
|
271
|
-
// Create a new Ed25519 key (e.g., for Cardano) and add it to our
|
|
268
|
+
// Create a new Ed25519 key (e.g., for Cardano) and add it to our roleClient's role
|
|
272
269
|
const edKey = await org.createKey(cs.Ed25519.Cardano);
|
|
273
270
|
await role.addKey(edKey);
|
|
274
271
|
console.log(`Created '${await edKey.type()}' key ${edKey.id} and added it to role ${role.id}`);
|
|
@@ -276,11 +273,12 @@ console.log(`Created '${await edKey.type()}' key ${edKey.id} and added it to rol
|
|
|
276
273
|
// Sign raw blobs with our new ed key and the secp we created before
|
|
277
274
|
for (const key of [edKey, secpKey]) {
|
|
278
275
|
console.log(`Confirming that raw blob with ${await key.type()} is rejected by default`);
|
|
276
|
+
const roleKey = new cs.Key(roleClient, key.cached);
|
|
279
277
|
const blobReq = <cs.BlobSignRequest>{
|
|
280
278
|
message_base64: "L1kE9g59xD3fzYQQSR7340BwU9fGrP6EMfIFcyX/YBc=",
|
|
281
279
|
};
|
|
282
280
|
try {
|
|
283
|
-
await
|
|
281
|
+
await roleKey.signBlob(blobReq);
|
|
284
282
|
assert(false, "Must be rejected by policy");
|
|
285
283
|
} catch (e) {
|
|
286
284
|
assert(`${e}`.includes("Raw blob signing not allowed"));
|
|
@@ -288,7 +286,7 @@ for (const key of [edKey, secpKey]) {
|
|
|
288
286
|
|
|
289
287
|
console.log("Signing raw blob after adding 'AllowRawBlobSigning' policy");
|
|
290
288
|
await key.appendPolicy(["AllowRawBlobSigning"]);
|
|
291
|
-
const blobSig = await
|
|
289
|
+
const blobSig = await roleKey.signBlob(blobReq);
|
|
292
290
|
console.log(blobSig.data());
|
|
293
291
|
assert(blobSig.data().signature);
|
|
294
292
|
}
|
|
@@ -300,46 +298,68 @@ for (const key of [edKey, secpKey]) {
|
|
|
300
298
|
Trying to sign an invalid message with a secp key will fail with
|
|
301
299
|
`400 Bad Request` saying `Signature scheme: InvalidMessage`.
|
|
302
300
|
|
|
303
|
-
|
|
301
|
+
## Session Management
|
|
302
|
+
|
|
303
|
+
In the above examples we've used 3 different sessions:
|
|
304
|
+
|
|
305
|
+
1. A management session that we created using the CLI
|
|
306
|
+
2. A signing session that we created using (1)
|
|
307
|
+
3. A role session that we created used (1)
|
|
304
308
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
management session.
|
|
309
|
+
All `CubeSignerClient`s require a valid session in order to operate. In this section
|
|
310
|
+
we'll dive into the specifics of sessions in the TypeScript SDK.
|
|
308
311
|
|
|
309
|
-
|
|
312
|
+
### Loading from disk
|
|
313
|
+
If you already have an active session in `cs` (the CubeSigner CLI), you can
|
|
314
|
+
load it into the TypeScript SDK with a simple helper (as we did earlier).
|
|
310
315
|
|
|
311
316
|
```typescript
|
|
312
|
-
|
|
313
|
-
// alternatively, save this object to a file
|
|
317
|
+
await cs.CubeSignerClient.create(defaultManagementSessionManager())
|
|
314
318
|
```
|
|
315
319
|
|
|
316
|
-
|
|
320
|
+
Or we can use the CLI to create our own session explicitly for our JavaScript client:
|
|
317
321
|
|
|
318
322
|
```bash
|
|
319
|
-
cs
|
|
323
|
+
cs session create --role-id $ROLE_ID --scope sign-all --output json > session.json
|
|
320
324
|
```
|
|
321
325
|
|
|
322
|
-
|
|
323
|
-
exported signer token, and just load the session from it.
|
|
324
|
-
|
|
326
|
+
Then we can load it like so:
|
|
325
327
|
```typescript
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
)
|
|
328
|
+
try {
|
|
329
|
+
await cs.CubeSignerClient.create(new JsonFileSessionManager("./session.json"));
|
|
330
|
+
} catch {
|
|
331
|
+
console.error("Unable to find file")
|
|
332
|
+
}
|
|
330
333
|
```
|
|
331
334
|
|
|
332
|
-
|
|
333
|
-
|
|
335
|
+
### Loading from Memory
|
|
336
|
+
If you already have the session information (`SessionData` in Typescript) in memory, you can
|
|
337
|
+
load that directly into a client.
|
|
334
338
|
|
|
335
339
|
```typescript
|
|
336
|
-
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
340
|
+
// Get the session data we want to use with the client
|
|
341
|
+
const sessionData: cs.SessionData = await role.createSession("readme");
|
|
342
|
+
await cs.CubeSignerClient.create(new cs.MemorySessionManager(sessionData))
|
|
343
|
+
// or, for short
|
|
344
|
+
await cs.CubeSignerClient.create(sessionData)
|
|
341
345
|
```
|
|
342
346
|
|
|
347
|
+
> [!WARNING]
|
|
348
|
+
> Clients created this way will automatically refresh the session. If you try to use this session with another client, they will both try to refresh, leading to failures.
|
|
349
|
+
|
|
350
|
+
### Managers, Storage and Refreshing
|
|
351
|
+
|
|
352
|
+
As we've seen in the examples above, all `CubeSignerClient`s are constructed using the `create` constructor. The `create`
|
|
353
|
+
constructor accepts either raw `SessionData` or a `SessionManager`. So far we've seen two different session managers:
|
|
354
|
+
- `JsonFileSessionManager`
|
|
355
|
+
- `MemorySessionManager`
|
|
356
|
+
|
|
357
|
+
These managers are responsible for keeping your session tokens refreshed
|
|
358
|
+
and (optionally) persisted. Whenever your session tokens are refreshed,
|
|
359
|
+
`JsonFileSessionManager` will write the new tokens to disk.
|
|
360
|
+
|
|
361
|
+
More complex managers can be written by implementing the `SessionManager` interface.
|
|
362
|
+
|
|
343
363
|
### Create a session for an OIDC user
|
|
344
364
|
|
|
345
365
|
CubeSigner supports the [OIDC](https://openid.net/developers/how-connect-works/)
|
|
@@ -360,7 +380,7 @@ assert(oidcToken);
|
|
|
360
380
|
Before we can use the OIDC token for authentication, we must add an org policy
|
|
361
381
|
to allow the particular issuer/audience pair from the token.
|
|
362
382
|
|
|
363
|
-
```
|
|
383
|
+
```
|
|
364
384
|
const oldOrgPolicy = await org.policy();
|
|
365
385
|
const oidcPayload = JSON.parse(atob(oidcToken.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
|
|
366
386
|
const oidcAuthSourcesPolicy = {
|
|
@@ -372,22 +392,16 @@ console.log("Setting org policy", oidcAuthSourcesPolicy);
|
|
|
372
392
|
await org.setPolicy([oidcAuthSourcesPolicy]);
|
|
373
393
|
```
|
|
374
394
|
|
|
375
|
-
Finally, exchange the OIDC token for
|
|
376
|
-
of `SignerSession`, required by all signing endpoints, e.g., `signEvm`)
|
|
395
|
+
Finally, exchange the OIDC token for a session.
|
|
377
396
|
|
|
378
397
|
```typescript
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
398
|
+
const oidcSessionResp = await cs.CubeSignerClient.createOidcSession(
|
|
399
|
+
cubesigner.env,
|
|
400
|
+
org.id, // org id to log into
|
|
401
|
+
oidcToken,
|
|
402
|
+
["manage:mfa", "sign:*"] // scopes for the session
|
|
382
403
|
);
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
or a _management_ session (i.e., and instance of `CubeSigner`,
|
|
386
|
-
required by all management endpoints, e.g., retrieving user
|
|
387
|
-
information, configuring user MFA methods, etc.).
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
const oidcCubeSigner = new cs.CubeSignerClient(await cubesigner.oidcAuth(oidcToken, ["manage:*"]));
|
|
404
|
+
const oidcClient = await cs.CubeSignerClient.create(oidcSessionResp.data());
|
|
391
405
|
```
|
|
392
406
|
|
|
393
407
|
> **Info**
|
|
@@ -398,7 +412,7 @@ const oidcCubeSigner = new cs.CubeSignerClient(await cubesigner.oidcAuth(oidcTok
|
|
|
398
412
|
|
|
399
413
|
### Set up TOTP for a user
|
|
400
414
|
|
|
401
|
-
To manage a user we need a
|
|
415
|
+
To manage a user we need a session bound to that user. It
|
|
402
416
|
doesn't matter if that user is native to CubeSigner or a third-party
|
|
403
417
|
OIDC user. For that purpose, in this section we are going to use the
|
|
404
418
|
previously created `oidcCubeSigner` instance.
|
|
@@ -408,12 +422,12 @@ TOTP reset procedure.
|
|
|
408
422
|
|
|
409
423
|
```typescript
|
|
410
424
|
console.log(`Setting up TOTP for user ${me.email}`);
|
|
411
|
-
let totpResetResp = await
|
|
425
|
+
let totpResetResp = await oidcClient.resetTotp();
|
|
412
426
|
```
|
|
413
427
|
|
|
414
428
|
If the user has already configured TOTP (or any other form of MFA),
|
|
415
429
|
this response will require multi factor authentication. In that case,
|
|
416
|
-
for example, call `
|
|
430
|
+
for example, call `totpApprove` and provide the code for the existing
|
|
417
431
|
TOTP to proceed:
|
|
418
432
|
|
|
419
433
|
```typescript
|
|
@@ -423,7 +437,7 @@ let totpSecret = process.env["CS_USER_TOTP_SECRET"]!;
|
|
|
423
437
|
if (totpResetResp.requiresMfa()) {
|
|
424
438
|
console.log("Resetting TOTP requires MFA");
|
|
425
439
|
const code = authenticator.generate(totpSecret);
|
|
426
|
-
totpResetResp = await totpResetResp.
|
|
440
|
+
totpResetResp = await totpResetResp.totpApprove(oidcClient, code);
|
|
427
441
|
assert(!totpResetResp.requiresMfa());
|
|
428
442
|
console.log("MFA approved using existing TOTP");
|
|
429
443
|
}
|
|
@@ -437,7 +451,7 @@ create an authenticator for automated testing.
|
|
|
437
451
|
|
|
438
452
|
```typescript
|
|
439
453
|
const totpChallenge = totpResetResp.data();
|
|
440
|
-
assert(totpChallenge.
|
|
454
|
+
assert(totpChallenge.url);
|
|
441
455
|
```
|
|
442
456
|
|
|
443
457
|
To complete the challenge, we must call `resetTotpComplete` and
|
|
@@ -455,14 +469,14 @@ is generating the correct code by calling `verifyTotp`
|
|
|
455
469
|
```typescript
|
|
456
470
|
console.log(`Verifying current TOTP code`);
|
|
457
471
|
let code = authenticator.generate(totpSecret);
|
|
458
|
-
await
|
|
472
|
+
await oidcClient.verifyTotp(code);
|
|
459
473
|
```
|
|
460
474
|
|
|
461
475
|
We can also check that the user's profile now indeed includes `Totp`
|
|
462
476
|
as one of the configured MFA factors.
|
|
463
477
|
|
|
464
478
|
```typescript
|
|
465
|
-
const mfa = (await
|
|
479
|
+
const mfa = (await oidcClient.user()).mfa;
|
|
466
480
|
console.log("Configured MFA types", mfa);
|
|
467
481
|
assert(mfa.map((m) => m.type).includes("totp"));
|
|
468
482
|
```
|
|
@@ -486,18 +500,19 @@ receive `202 Accepted` instead of `200 Ok`. The response body contains
|
|
|
486
500
|
an MFA ID, which we can use to fetch and inspect the associated MFA
|
|
487
501
|
request, see how many approvals it requires, what kind of MFA factors
|
|
488
502
|
it allows, etc. Instead, since we know that our key requires TOTP, we
|
|
489
|
-
can just call `
|
|
503
|
+
can just call `totpApprove` on the response and pass the current TOTP
|
|
490
504
|
code to it; if the code is correct, the call will succeed
|
|
491
505
|
and return the signature.
|
|
492
506
|
|
|
493
507
|
```typescript
|
|
494
508
|
console.log(`Signing a transaction now requires TOTP`);
|
|
495
|
-
|
|
509
|
+
const oidcKey = new cs.Key(oidcClient, secpKey.cached);
|
|
510
|
+
let resp = await oidcKey.signEvm(eth1Request);
|
|
496
511
|
assert(resp.requiresMfa());
|
|
497
512
|
|
|
498
513
|
console.log(`Approving with TOTP code`);
|
|
499
514
|
code = authenticator.generate(totpSecret);
|
|
500
|
-
resp = await resp.
|
|
515
|
+
resp = await resp.totpApprove(oidcClient, code);
|
|
501
516
|
assert(!resp.requiresMfa());
|
|
502
517
|
console.log(resp.data());
|
|
503
518
|
assert(resp.data().rlp_signed_tx);
|
|
@@ -510,7 +525,8 @@ we created.
|
|
|
510
525
|
|
|
511
526
|
```typescript
|
|
512
527
|
console.log("Cleaning up");
|
|
513
|
-
await
|
|
528
|
+
await signingClient.revokeSession();
|
|
529
|
+
await roleClient.revokeSession();
|
|
514
530
|
await role.delete();
|
|
515
531
|
```
|
|
516
532
|
|
package/dist/cjs/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubist-labs/cubesigner-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.24-0",
|
|
4
4
|
"description": "CubeSigner TypeScript SDK",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"author": "Cubist, Inc.",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typedoc": "typedoc"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"openapi-fetch": "0.
|
|
30
|
+
"openapi-fetch": "0.8.2"
|
|
31
31
|
},
|
|
32
32
|
"optionalDependencies": {
|
|
33
33
|
"@hpke/core": "^1.2.7"
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"directories": {
|
|
39
39
|
"test": "test"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"openapi-typescript-helpers": "^0.0.7"
|
|
40
43
|
}
|
|
41
44
|
}
|
package/dist/cjs/src/api.d.ts
CHANGED
|
@@ -332,10 +332,9 @@ export declare class CubeSignerApi {
|
|
|
332
332
|
* List all keys in the org.
|
|
333
333
|
* @param {KeyType?} type Optional key type to filter list for.
|
|
334
334
|
* @param {PageOpts?} page Pagination options. Defaults to fetching the entire result set.
|
|
335
|
-
* @param {string?} owner Optional key owner to filter list for.
|
|
336
335
|
* @return {Paginator<ListKeysResponse, KeyInfoApi>} Paginator for iterating over keys.
|
|
337
336
|
*/
|
|
338
|
-
keysList(type?: KeyType, page?: PageOpts
|
|
337
|
+
keysList(type?: KeyType, page?: PageOpts): Paginator<ListKeysResponse, KeyInfoApi>;
|
|
339
338
|
/**
|
|
340
339
|
* Create a new role.
|
|
341
340
|
*
|