@cubist-labs/cubesigner-sdk 0.1.23
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/LICENSE-APACHE +177 -0
- package/LICENSE-MIT +25 -0
- package/NOTICE +13 -0
- package/README.md +470 -0
- package/dist/examples/ethers.d.ts +1 -0
- package/dist/examples/ethers.js +142 -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/client.d.ts +10 -0
- package/dist/src/client.js +21 -0
- package/dist/src/env.d.ts +15 -0
- package/dist/src/env.js +35 -0
- package/dist/src/ethers/index.d.ts +50 -0
- package/dist/src/ethers/index.js +122 -0
- package/dist/src/index.d.ts +114 -0
- package/dist/src/index.js +205 -0
- package/dist/src/key.d.ts +114 -0
- package/dist/src/key.js +201 -0
- package/dist/src/mfa.d.ts +23 -0
- package/dist/src/mfa.js +63 -0
- package/dist/src/org.d.ts +161 -0
- package/dist/src/org.js +264 -0
- package/dist/src/role.d.ts +224 -0
- package/dist/src/role.js +256 -0
- package/dist/src/schema.d.ts +3049 -0
- package/dist/src/schema.js +7 -0
- package/dist/src/session/generic.d.ts +47 -0
- package/dist/src/session/generic.js +3 -0
- package/dist/src/session/management_session_manager.d.ts +59 -0
- package/dist/src/session/management_session_manager.js +111 -0
- package/dist/src/session/oidc_session_manager.d.ts +78 -0
- package/dist/src/session/oidc_session_manager.js +142 -0
- package/dist/src/session/session_manager.d.ts +74 -0
- package/dist/src/session/session_manager.js +79 -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 +88 -0
- package/dist/src/session/signer_session_manager.js +159 -0
- package/dist/src/sign.d.ts +114 -0
- package/dist/src/sign.js +248 -0
- package/dist/src/signer_session.d.ts +180 -0
- package/dist/src/signer_session.js +369 -0
- package/dist/src/util.d.ts +35 -0
- package/dist/src/util.js +75 -0
- package/dist/test/sessions.d.ts +35 -0
- package/dist/test/sessions.js +56 -0
- package/package.json +61 -0
- package/src/client.ts +12 -0
- package/src/env.ts +25 -0
- package/src/ethers/index.ts +131 -0
- package/src/index.ts +220 -0
- package/src/key.ts +249 -0
- package/src/org.ts +333 -0
- package/src/role.ts +385 -0
- package/src/schema.ts +3054 -0
- package/src/session/management_session_manager.ts +136 -0
- package/src/session/oidc_session_manager.ts +193 -0
- package/src/session/session_manager.ts +114 -0
- package/src/session/session_storage.ts +73 -0
- package/src/session/signer_session_manager.ts +211 -0
- package/src/signer_session.ts +464 -0
- package/src/util.ts +58 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { CubeSigner } from "..";
|
|
2
|
+
import { assertOk } from "../util";
|
|
3
|
+
import { components, paths, Client } from "../client";
|
|
4
|
+
import { HasEnv, OrgSessionManager } from "./session_manager";
|
|
5
|
+
import { SessionStorage } from "./session_storage";
|
|
6
|
+
|
|
7
|
+
export type ClientSessionInfo = components["schemas"]["ClientSessionInfo"];
|
|
8
|
+
|
|
9
|
+
export type CreateSignerSessionRequest =
|
|
10
|
+
paths["/v0/org/{org_id}/roles/{role_id}/tokens"]["post"]["requestBody"]["content"]["application/json"];
|
|
11
|
+
export type RefreshSignerSessionRequest =
|
|
12
|
+
paths["/v1/org/{org_id}/token/refresh"]["patch"]["requestBody"]["content"]["application/json"];
|
|
13
|
+
|
|
14
|
+
/** JSON representation of our "signer session" file format */
|
|
15
|
+
export interface SignerSessionObject {
|
|
16
|
+
/** The organization ID */
|
|
17
|
+
org_id: string;
|
|
18
|
+
/** The role ID */
|
|
19
|
+
role_id: string;
|
|
20
|
+
/** The purpose of the session token */
|
|
21
|
+
purpose: string;
|
|
22
|
+
/** The token to include in Authorization header */
|
|
23
|
+
token: string;
|
|
24
|
+
/** Session info */
|
|
25
|
+
session_info: ClientSessionInfo;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SignerSessionData extends SignerSessionObject, HasEnv {}
|
|
29
|
+
|
|
30
|
+
/** Type of storage required for signer sessions */
|
|
31
|
+
export type SignerSessionStorage = SessionStorage<SignerSessionData>;
|
|
32
|
+
|
|
33
|
+
export interface SignerSessionLifetime {
|
|
34
|
+
/** Session lifetime (in seconds). Defaults to one week (604800). */
|
|
35
|
+
session?: number;
|
|
36
|
+
/** Auth token lifetime (in seconds). Defaults to five minutes (300). */
|
|
37
|
+
auth: number;
|
|
38
|
+
/** Refresh token lifetime (in seconds). Defaults to one day (86400). */
|
|
39
|
+
refresh?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const defaultSignerSessionLifetime: SignerSessionLifetime = {
|
|
43
|
+
session: 604800,
|
|
44
|
+
auth: 300,
|
|
45
|
+
refresh: 86400,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Manager for signer sessions. */
|
|
49
|
+
export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
50
|
+
readonly cs?: CubeSigner;
|
|
51
|
+
readonly roleId: string;
|
|
52
|
+
#client: Client;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @return {string} The current auth token.
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
async token(): Promise<string> {
|
|
59
|
+
const session = await this.storage.retrieve();
|
|
60
|
+
return session.token;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns a client with the current session and refreshes the current
|
|
65
|
+
* session. May **UPDATE/MUTATE** self.
|
|
66
|
+
*/
|
|
67
|
+
async client(): Promise<Client> {
|
|
68
|
+
await this.refreshIfNeeded();
|
|
69
|
+
return this.#client;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Revokes the session. */
|
|
73
|
+
async revoke(): Promise<void> {
|
|
74
|
+
if (!this.cs) {
|
|
75
|
+
throw new Error("No management session available");
|
|
76
|
+
}
|
|
77
|
+
const session = await this.storage.retrieve();
|
|
78
|
+
const resp = await (
|
|
79
|
+
await this.cs.management()
|
|
80
|
+
).del("/v0/org/{org_id}/roles/{role_id}/tokens/{session_id}", {
|
|
81
|
+
params: {
|
|
82
|
+
path: {
|
|
83
|
+
org_id: session.org_id,
|
|
84
|
+
role_id: session.role_id,
|
|
85
|
+
session_id: session.session_info.session_id,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
parseAs: "json",
|
|
89
|
+
});
|
|
90
|
+
assertOk(resp);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Returns whether it's time to refresh this token.
|
|
95
|
+
* @return {boolean} Whether it's time to refresh this token.
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
async isStale(): Promise<boolean> {
|
|
99
|
+
const session = await this.storage.retrieve();
|
|
100
|
+
return this.hasExpired(session.session_info.auth_token_exp);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Refreshes the session and **UPDATES/MUTATES** self.
|
|
105
|
+
*/
|
|
106
|
+
async refresh(): Promise<void> {
|
|
107
|
+
const session = await this.storage.retrieve();
|
|
108
|
+
const csi = session.session_info;
|
|
109
|
+
const resp = await this.#client.patch("/v1/org/{org_id}/token/refresh", {
|
|
110
|
+
params: { path: { org_id: session.org_id } },
|
|
111
|
+
body: <RefreshSignerSessionRequest>{
|
|
112
|
+
epoch_num: csi.epoch,
|
|
113
|
+
epoch_token: csi.epoch_token,
|
|
114
|
+
other_token: csi.refresh_token,
|
|
115
|
+
},
|
|
116
|
+
parseAs: "json",
|
|
117
|
+
});
|
|
118
|
+
const data = assertOk(resp);
|
|
119
|
+
await this.storage.save(<SignerSessionData>{
|
|
120
|
+
...session,
|
|
121
|
+
session_info: data.session_info,
|
|
122
|
+
token: data.token,
|
|
123
|
+
});
|
|
124
|
+
this.#client = this.createClient(data.token);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create a new signer session.
|
|
129
|
+
* @param {CubeSigner} cs The CubeSigner instance
|
|
130
|
+
* @param {SessionStorage<SignerSessionObject>} storage The session storage to use
|
|
131
|
+
* @param {string} orgId Org ID
|
|
132
|
+
* @param {string} roleId Role ID
|
|
133
|
+
* @param {string} purpose The purpose of the session
|
|
134
|
+
* @param {SignerSessionLifetime} ttl Lifetime settings
|
|
135
|
+
* @return {Promise<SignerSessionManager>} New signer session
|
|
136
|
+
*/
|
|
137
|
+
static async create(
|
|
138
|
+
cs: CubeSigner,
|
|
139
|
+
storage: SignerSessionStorage,
|
|
140
|
+
orgId: string,
|
|
141
|
+
roleId: string,
|
|
142
|
+
purpose: string,
|
|
143
|
+
ttl?: SignerSessionLifetime,
|
|
144
|
+
): Promise<SignerSessionManager> {
|
|
145
|
+
const resp = await (
|
|
146
|
+
await cs.management()
|
|
147
|
+
).post("/v0/org/{org_id}/roles/{role_id}/tokens", {
|
|
148
|
+
params: { path: { org_id: orgId, role_id: roleId } },
|
|
149
|
+
body: {
|
|
150
|
+
purpose,
|
|
151
|
+
auth_lifetime: ttl?.auth || defaultSignerSessionLifetime.auth,
|
|
152
|
+
refresh_lifetime: ttl?.refresh || defaultSignerSessionLifetime.refresh,
|
|
153
|
+
session_lifetime: ttl?.session || defaultSignerSessionLifetime.session,
|
|
154
|
+
},
|
|
155
|
+
parseAs: "json",
|
|
156
|
+
});
|
|
157
|
+
const data = assertOk(resp);
|
|
158
|
+
const session_info = data.session_info;
|
|
159
|
+
if (!session_info) {
|
|
160
|
+
throw new Error("Signer session info missing");
|
|
161
|
+
}
|
|
162
|
+
await storage.save({
|
|
163
|
+
org_id: orgId,
|
|
164
|
+
role_id: roleId,
|
|
165
|
+
purpose,
|
|
166
|
+
token: data.token,
|
|
167
|
+
session_info,
|
|
168
|
+
// Keep compatibility with tokens produced by CLI
|
|
169
|
+
env: {
|
|
170
|
+
["Dev-CubeSignerStack"]: cs.env,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
return new SignerSessionManager(cs, orgId, roleId, data.token, storage);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Uses an existing session to create a new signer session manager.
|
|
178
|
+
* @param {CubeSigner} cs The CubeSigner instance
|
|
179
|
+
* @param {SessionStorage<SignerSessionObject>} storage The session storage to use
|
|
180
|
+
* @return {Promise<SingerSession>} New signer session manager
|
|
181
|
+
*/
|
|
182
|
+
static async loadFromStorage(
|
|
183
|
+
cs: CubeSigner,
|
|
184
|
+
storage: SignerSessionStorage,
|
|
185
|
+
): Promise<SignerSessionManager> {
|
|
186
|
+
const session = await storage.retrieve();
|
|
187
|
+
return new SignerSessionManager(cs, session.org_id, session.role_id, session.token, storage);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Constructor.
|
|
192
|
+
* @param {CubeSigner} cs CubeSigner
|
|
193
|
+
* @param {string} orgId The id of the org associated with this session
|
|
194
|
+
* @param {string} roleId The id of the role that this session assumes
|
|
195
|
+
* @param {string} token The authorization token to use
|
|
196
|
+
* @param {SignerSessionStorage} storage The session storage to use
|
|
197
|
+
* @internal
|
|
198
|
+
*/
|
|
199
|
+
private constructor(
|
|
200
|
+
cs: CubeSigner,
|
|
201
|
+
orgId: string,
|
|
202
|
+
roleId: string,
|
|
203
|
+
token: string,
|
|
204
|
+
storage: SignerSessionStorage,
|
|
205
|
+
) {
|
|
206
|
+
super(cs.env, orgId, storage);
|
|
207
|
+
this.cs = cs;
|
|
208
|
+
this.roleId = roleId;
|
|
209
|
+
this.#client = this.createClient(token);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { CubeSigner, Key, OidcSessionManager, OidcSessionStorage, Org } from ".";
|
|
3
|
+
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"];
|
|
8
|
+
|
|
9
|
+
/* eslint-disable */
|
|
10
|
+
export type EvmSignRequest =
|
|
11
|
+
paths["/v1/org/{org_id}/eth1/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
|
|
12
|
+
export type Eth2SignRequest =
|
|
13
|
+
paths["/v1/org/{org_id}/eth2/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
|
|
14
|
+
export type Eth2StakeRequest =
|
|
15
|
+
paths["/v1/org/{org_id}/eth2/stake"]["post"]["requestBody"]["content"]["application/json"];
|
|
16
|
+
export type Eth2UnstakeRequest =
|
|
17
|
+
paths["/v1/org/{org_id}/eth2/unstake/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
|
|
18
|
+
export type BlobSignRequest =
|
|
19
|
+
paths["/v1/org/{org_id}/blob/sign/{key_id}"]["post"]["requestBody"]["content"]["application/json"];
|
|
20
|
+
export type BtcSignRequest =
|
|
21
|
+
paths["/v0/org/{org_id}/btc/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
|
|
22
|
+
export type SolanaSignRequest =
|
|
23
|
+
paths["/v1/org/{org_id}/solana/sign/{pubkey}"]["post"]["requestBody"]["content"]["application/json"];
|
|
24
|
+
|
|
25
|
+
export type EvmSignResponse =
|
|
26
|
+
components["responses"]["Eth1SignResponse"]["content"]["application/json"];
|
|
27
|
+
export type Eth2SignResponse =
|
|
28
|
+
components["responses"]["Eth2SignResponse"]["content"]["application/json"];
|
|
29
|
+
export type Eth2StakeResponse =
|
|
30
|
+
components["responses"]["StakeResponse"]["content"]["application/json"];
|
|
31
|
+
export type Eth2UnstakeResponse =
|
|
32
|
+
components["responses"]["UnstakeResponse"]["content"]["application/json"];
|
|
33
|
+
export type BlobSignResponse =
|
|
34
|
+
components["responses"]["BlobSignResponse"]["content"]["application/json"];
|
|
35
|
+
export type BtcSignResponse =
|
|
36
|
+
components["responses"]["BtcSignResponse"]["content"]["application/json"];
|
|
37
|
+
export type SolanaSignResponse =
|
|
38
|
+
components["responses"]["SolanaSignResponse"]["content"]["application/json"];
|
|
39
|
+
export type MfaRequestInfo =
|
|
40
|
+
components["responses"]["MfaRequestInfo"]["content"]["application/json"];
|
|
41
|
+
|
|
42
|
+
export type AcceptedResponse = components["schemas"]["AcceptedResponse"];
|
|
43
|
+
export type ErrorResponse = components["schemas"]["ErrorResponse"];
|
|
44
|
+
export type BtcSignatureKind = components["schemas"]["BtcSignatureKind"];
|
|
45
|
+
/* eslint-enable */
|
|
46
|
+
|
|
47
|
+
/** MFA request kind */
|
|
48
|
+
export type MfaType = components["schemas"]["MfaType"];
|
|
49
|
+
|
|
50
|
+
type SignFn<U> = (headers?: HeadersInit) => Promise<U | AcceptedResponse>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A response of a signing request.
|
|
54
|
+
*/
|
|
55
|
+
export class SignResponse<U> {
|
|
56
|
+
readonly #cs: CubeSigner;
|
|
57
|
+
readonly #orgId: string;
|
|
58
|
+
readonly #signFn: SignFn<U>;
|
|
59
|
+
readonly #resp: U | AcceptedResponse;
|
|
60
|
+
|
|
61
|
+
/** @return {boolean} True if this signing request requires an MFA approval */
|
|
62
|
+
requiresMfa(): boolean {
|
|
63
|
+
return (this.#resp as AcceptedResponse).accepted?.MfaRequired !== undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** @return {U} The signed data */
|
|
67
|
+
data(): U {
|
|
68
|
+
return this.#resp as U;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
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.
|
|
75
|
+
*
|
|
76
|
+
* @param {SignerSession} session Signer session to use
|
|
77
|
+
* @param {string} code 6-digit TOTP code
|
|
78
|
+
* @return {SignResponse<U>} The result of signing with the approval
|
|
79
|
+
*/
|
|
80
|
+
async approveTotp(session: SignerSession, code: string): Promise<SignResponse<U>> {
|
|
81
|
+
const mfaId = this.#mfaId();
|
|
82
|
+
|
|
83
|
+
const mfaApproval = await session.totpApprove(mfaId, code);
|
|
84
|
+
assert(mfaApproval.id === mfaId);
|
|
85
|
+
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
86
|
+
|
|
87
|
+
if (!mfaConf) {
|
|
88
|
+
throw new Error("MfaRequest has not been approved yet");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return await this.#signWithMfaApproval(mfaConf!);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Approves the MFA request using CubeSigner's management session.
|
|
96
|
+
*
|
|
97
|
+
* Note: This only works for MFA requests that require a single approval.
|
|
98
|
+
*
|
|
99
|
+
* @return {SignResponse<U>} The result of signing with the approval
|
|
100
|
+
*/
|
|
101
|
+
async approve(): Promise<SignResponse<U>> {
|
|
102
|
+
const mfaId = this.#mfaId();
|
|
103
|
+
|
|
104
|
+
const mfaApproval = await Org.mfaApprove(this.#cs, this.#orgId, mfaId);
|
|
105
|
+
assert(mfaApproval.id === mfaId);
|
|
106
|
+
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
107
|
+
|
|
108
|
+
if (!mfaConf) {
|
|
109
|
+
throw new Error("MfaRequest has not been approved yet");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return await this.#signWithMfaApproval(mfaConf);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// --------------------------------------------------------------------------
|
|
116
|
+
// -- INTERNAL --------------------------------------------------------------
|
|
117
|
+
// --------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Constructor.
|
|
121
|
+
*
|
|
122
|
+
* @param {CubeSigner} cs The CubeSigner instance to use for requests
|
|
123
|
+
* @param {string} orgId The org id of the corresponding signing request
|
|
124
|
+
* @param {SignFn} signFn The signing function that this response is from.
|
|
125
|
+
* This argument is used to resend requests with
|
|
126
|
+
* different headers if needed.
|
|
127
|
+
* @param {U | AcceptedResponse} resp The response as returned by the OpenAPI
|
|
128
|
+
* client.
|
|
129
|
+
*/
|
|
130
|
+
constructor(cs: CubeSigner, orgId: string, signFn: SignFn<U>, resp: U | AcceptedResponse) {
|
|
131
|
+
this.#cs = cs;
|
|
132
|
+
this.#orgId = orgId;
|
|
133
|
+
this.#signFn = signFn;
|
|
134
|
+
this.#resp = resp;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {string} mfaConf MFA request approval confirmation code
|
|
139
|
+
* @return {Promise<SignResponse<U>>} The result of signing after MFA approval
|
|
140
|
+
*/
|
|
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));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @return {string} MFA id if MFA is required for this response; throws otherwise.
|
|
153
|
+
*/
|
|
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;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Signer session info. Can only be used to revoke a token, but not for authentication. */
|
|
164
|
+
export class SignerSessionInfo {
|
|
165
|
+
readonly #cs: CubeSigner;
|
|
166
|
+
readonly #orgId: string;
|
|
167
|
+
readonly #roleId: string;
|
|
168
|
+
readonly #sessionId: string;
|
|
169
|
+
public readonly purpose: string;
|
|
170
|
+
|
|
171
|
+
/** Revoke this token */
|
|
172
|
+
async revoke() {
|
|
173
|
+
await SignerSession.revoke(this.#cs, this.#orgId, this.#roleId, this.#sessionId);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// --------------------------------------------------------------------------
|
|
177
|
+
// -- INTERNAL --------------------------------------------------------------
|
|
178
|
+
// --------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Internal constructor.
|
|
182
|
+
* @param {CubeSigner} cs CubeSigner instance to use when calling `revoke`
|
|
183
|
+
* @param {string} orgId Organization ID
|
|
184
|
+
* @param {string} roleId Role ID
|
|
185
|
+
* @param {string} hash The hash of the token; can be used for revocation but not for auth
|
|
186
|
+
* @param {string} purpose Session purpose
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
constructor(cs: CubeSigner, orgId: string, roleId: string, hash: string, purpose: string) {
|
|
190
|
+
this.#cs = cs;
|
|
191
|
+
this.#orgId = orgId;
|
|
192
|
+
this.#roleId = roleId;
|
|
193
|
+
this.#sessionId = hash;
|
|
194
|
+
this.purpose = purpose;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Signer session. */
|
|
199
|
+
export class SignerSession {
|
|
200
|
+
readonly cs: CubeSigner;
|
|
201
|
+
sessionMgr: OidcSessionManager | SignerSessionManager;
|
|
202
|
+
readonly #orgId: string;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns the list of keys that this token grants access to.
|
|
206
|
+
* @return {Key[]} The list of keys.
|
|
207
|
+
*/
|
|
208
|
+
async keys(): Promise<Key[]> {
|
|
209
|
+
const resp = await (
|
|
210
|
+
await this.sessionMgr.client()
|
|
211
|
+
).get("/v0/org/{org_id}/token/keys", {
|
|
212
|
+
params: { path: { org_id: this.#orgId } },
|
|
213
|
+
parseAs: "json",
|
|
214
|
+
});
|
|
215
|
+
const data = assertOk(resp);
|
|
216
|
+
return data.keys.map((k: KeyInfo) => new Key(this.cs, this.#orgId, k));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Approve a pending MFA request using TOTP.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} mfaId The MFA request to approve
|
|
223
|
+
* @param {string} code The TOTP code
|
|
224
|
+
* @return {Promise<MfaRequestInfo>} The current status of the MFA request
|
|
225
|
+
*/
|
|
226
|
+
async totpApprove(mfaId: string, code: string): Promise<MfaRequestInfo> {
|
|
227
|
+
const resp = await (
|
|
228
|
+
await this.sessionMgr.client()
|
|
229
|
+
).patch("/v0/org/{org_id}/mfa/{mfa_id}/totp", {
|
|
230
|
+
params: { path: { org_id: this.#orgId, mfa_id: mfaId } },
|
|
231
|
+
body: { code },
|
|
232
|
+
parseAs: "json",
|
|
233
|
+
});
|
|
234
|
+
return assertOk(resp);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Submit an EVM sign request.
|
|
239
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
|
|
240
|
+
* @param {EvmSignRequest} req What to sign.
|
|
241
|
+
* @return {Promise<EvmSignResponse | AcceptedResponse>} Signature
|
|
242
|
+
*/
|
|
243
|
+
async signEvm(key: Key | string, req: EvmSignRequest): Promise<SignResponse<EvmSignResponse>> {
|
|
244
|
+
const pubkey = typeof key === "string" ? (key as string) : key.materialId;
|
|
245
|
+
const sign = async (headers?: HeadersInit) => {
|
|
246
|
+
const resp = await (
|
|
247
|
+
await this.sessionMgr.client()
|
|
248
|
+
).post("/v1/org/{org_id}/eth1/sign/{pubkey}", {
|
|
249
|
+
params: { path: { org_id: this.#orgId, pubkey } },
|
|
250
|
+
body: req,
|
|
251
|
+
headers,
|
|
252
|
+
parseAs: "json",
|
|
253
|
+
});
|
|
254
|
+
return assertOk(resp);
|
|
255
|
+
};
|
|
256
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Submit an 'eth2' sign request.
|
|
261
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
|
|
262
|
+
* @param {Eth2SignRequest} req What to sign.
|
|
263
|
+
* @return {Promise<Eth2SignResponse | AcceptedResponse>} Signature
|
|
264
|
+
*/
|
|
265
|
+
async signEth2(key: Key | string, req: Eth2SignRequest): Promise<SignResponse<Eth2SignResponse>> {
|
|
266
|
+
const pubkey = typeof key === "string" ? (key as string) : key.materialId;
|
|
267
|
+
const sign = async (headers?: HeadersInit) => {
|
|
268
|
+
const resp = await (
|
|
269
|
+
await this.sessionMgr.client()
|
|
270
|
+
).post("/v1/org/{org_id}/eth2/sign/{pubkey}", {
|
|
271
|
+
params: { path: { org_id: this.#orgId, pubkey } },
|
|
272
|
+
body: req,
|
|
273
|
+
headers,
|
|
274
|
+
parseAs: "json",
|
|
275
|
+
});
|
|
276
|
+
return assertOk(resp);
|
|
277
|
+
};
|
|
278
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Sign a stake request.
|
|
283
|
+
* @param {Eth2StakeRequest} req The request to sign.
|
|
284
|
+
* @return {Promise<Eth2StakeResponse | AcceptedResponse>} The response.
|
|
285
|
+
*/
|
|
286
|
+
async stake(req: Eth2StakeRequest): Promise<SignResponse<Eth2StakeResponse>> {
|
|
287
|
+
const sign = async (headers?: HeadersInit) => {
|
|
288
|
+
const resp = await (
|
|
289
|
+
await this.sessionMgr.client()
|
|
290
|
+
).post("/v1/org/{org_id}/eth2/stake", {
|
|
291
|
+
params: { path: { org_id: this.#orgId } },
|
|
292
|
+
body: req,
|
|
293
|
+
headers,
|
|
294
|
+
parseAs: "json",
|
|
295
|
+
});
|
|
296
|
+
return assertOk(resp);
|
|
297
|
+
};
|
|
298
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Sign an unstake request.
|
|
303
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
|
|
304
|
+
* @param {Eth2UnstakeRequest} req The request to sign.
|
|
305
|
+
* @return {Promise<Eth2UnstakeResponse | AcceptedResponse>} The response.
|
|
306
|
+
*/
|
|
307
|
+
async unstake(
|
|
308
|
+
key: Key | string,
|
|
309
|
+
req: Eth2UnstakeRequest,
|
|
310
|
+
): Promise<SignResponse<Eth2UnstakeResponse>> {
|
|
311
|
+
const pubkey = typeof key === "string" ? (key as string) : key.materialId;
|
|
312
|
+
const sign = async (headers?: HeadersInit) => {
|
|
313
|
+
const resp = await (
|
|
314
|
+
await this.sessionMgr.client()
|
|
315
|
+
).post("/v1/org/{org_id}/eth2/unstake/{pubkey}", {
|
|
316
|
+
params: { path: { org_id: this.#orgId, pubkey } },
|
|
317
|
+
body: req,
|
|
318
|
+
headers,
|
|
319
|
+
parseAs: "json",
|
|
320
|
+
});
|
|
321
|
+
return assertOk(resp);
|
|
322
|
+
};
|
|
323
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Sign a raw blob.
|
|
328
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its ID).
|
|
329
|
+
* @param {BlobSignRequest} req What to sign
|
|
330
|
+
* @return {Promise<BlobSignResponse | AcceptedResponse>} The response.
|
|
331
|
+
*/
|
|
332
|
+
async signBlob(key: Key | string, req: BlobSignRequest): Promise<SignResponse<BlobSignResponse>> {
|
|
333
|
+
const key_id = typeof key === "string" ? (key as string) : key.id;
|
|
334
|
+
const sign = async (headers?: HeadersInit) => {
|
|
335
|
+
const resp = await (
|
|
336
|
+
await this.sessionMgr.client()
|
|
337
|
+
).post("/v1/org/{org_id}/blob/sign/{key_id}", {
|
|
338
|
+
params: {
|
|
339
|
+
path: { org_id: this.#orgId, key_id },
|
|
340
|
+
},
|
|
341
|
+
body: req,
|
|
342
|
+
headers,
|
|
343
|
+
parseAs: "json",
|
|
344
|
+
});
|
|
345
|
+
return assertOk(resp);
|
|
346
|
+
};
|
|
347
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Sign a bitcoin message.
|
|
352
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
|
|
353
|
+
* @param {BtcSignRequest} req What to sign
|
|
354
|
+
* @return {Promise<BtcSignResponse | AcceptedResponse>} The response.
|
|
355
|
+
*/
|
|
356
|
+
async signBtc(key: Key | string, req: BtcSignRequest): Promise<SignResponse<BtcSignResponse>> {
|
|
357
|
+
const pubkey = typeof key === "string" ? (key as string) : key.materialId;
|
|
358
|
+
const sign = async (headers?: HeadersInit) => {
|
|
359
|
+
const resp = await (
|
|
360
|
+
await this.sessionMgr.client()
|
|
361
|
+
).post("/v0/org/{org_id}/btc/sign/{pubkey}", {
|
|
362
|
+
params: {
|
|
363
|
+
path: { org_id: this.#orgId, pubkey },
|
|
364
|
+
},
|
|
365
|
+
body: req,
|
|
366
|
+
headers: headers,
|
|
367
|
+
parseAs: "json",
|
|
368
|
+
});
|
|
369
|
+
return assertOk(resp);
|
|
370
|
+
};
|
|
371
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Sign a solana message.
|
|
376
|
+
* @param {Key | string} key The key to sign with (either {@link Key} or its material ID).
|
|
377
|
+
* @param {SolanaSignRequest} req What to sign
|
|
378
|
+
* @return {Promise<SolanaSignResponse | AcceptedResponse>} The response.
|
|
379
|
+
*/
|
|
380
|
+
async signSolana(
|
|
381
|
+
key: Key | string,
|
|
382
|
+
req: SolanaSignRequest,
|
|
383
|
+
): Promise<SignResponse<SolanaSignResponse>> {
|
|
384
|
+
const pubkey = typeof key === "string" ? (key as string) : key.materialId;
|
|
385
|
+
const sign = async (headers?: HeadersInit) => {
|
|
386
|
+
const resp = await (
|
|
387
|
+
await this.sessionMgr.client()
|
|
388
|
+
).post("/v1/org/{org_id}/solana/sign/{pubkey}", {
|
|
389
|
+
params: { path: { org_id: this.#orgId, pubkey } },
|
|
390
|
+
body: req,
|
|
391
|
+
headers,
|
|
392
|
+
parseAs: "json",
|
|
393
|
+
});
|
|
394
|
+
return assertOk(resp);
|
|
395
|
+
};
|
|
396
|
+
return new SignResponse(this.cs, this.#orgId, sign, await sign());
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
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
|
|
404
|
+
*/
|
|
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);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
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
|
|
418
|
+
*/
|
|
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);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Constructor.
|
|
429
|
+
* @param {CubeSigner} cs The CubeSigner instance to use for requests
|
|
430
|
+
* @param {OidcSessionManager | SignerSessionManager} sessionMgr The session manager to use
|
|
431
|
+
* @internal
|
|
432
|
+
*/
|
|
433
|
+
constructor(cs: CubeSigner, sessionMgr: OidcSessionManager | SignerSessionManager) {
|
|
434
|
+
this.cs = cs;
|
|
435
|
+
this.sessionMgr = sessionMgr;
|
|
436
|
+
this.#orgId = sessionMgr.orgId;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// --------------------------------------------------------------------------
|
|
440
|
+
// -- INTERNAL --------------------------------------------------------------
|
|
441
|
+
// --------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
/* eslint-disable require-jsdoc */
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Static method for revoking a token (used both from {SignerSession} and {SignerSessionInfo}).
|
|
447
|
+
* @param {CubeSigner} cs CubeSigner instance
|
|
448
|
+
* @param {string} orgId Organization ID
|
|
449
|
+
* @param {string} roleId Role ID
|
|
450
|
+
* @param {string} sessionId Signer session ID
|
|
451
|
+
* @internal
|
|
452
|
+
*/
|
|
453
|
+
static async revoke(cs: CubeSigner, orgId: string, roleId: string, sessionId: string) {
|
|
454
|
+
const resp = await (
|
|
455
|
+
await cs.management()
|
|
456
|
+
).del("/v0/org/{org_id}/roles/{role_id}/tokens/{session_id}", {
|
|
457
|
+
params: {
|
|
458
|
+
path: { org_id: orgId, role_id: roleId, session_id: sessionId },
|
|
459
|
+
},
|
|
460
|
+
parseAs: "json",
|
|
461
|
+
});
|
|
462
|
+
assertOk(resp);
|
|
463
|
+
}
|
|
464
|
+
}
|