@cubist-labs/cubesigner-sdk 0.3.1 → 0.3.11
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/cjs/package.json +1 -1
- package/dist/cjs/src/api.d.ts +27 -9
- package/dist/cjs/src/api.js +58 -22
- package/dist/cjs/src/client.d.ts +72 -3
- package/dist/cjs/src/client.js +79 -2
- package/dist/cjs/src/error.d.ts +26 -0
- package/dist/cjs/src/error.js +64 -1
- package/dist/cjs/src/events.d.ts +31 -9
- package/dist/cjs/src/events.js +56 -25
- package/dist/cjs/src/index.js +3 -2
- package/dist/cjs/src/key.d.ts +19 -2
- package/dist/cjs/src/key.js +22 -2
- package/dist/cjs/src/mfa.d.ts +6 -3
- package/dist/cjs/src/mfa.js +8 -5
- package/dist/cjs/src/response.d.ts +14 -1
- package/dist/cjs/src/response.js +65 -26
- package/dist/cjs/src/role.d.ts +6 -0
- package/dist/cjs/src/role.js +9 -1
- package/dist/cjs/src/schema.d.ts +339 -45
- package/dist/cjs/src/schema.js +1 -1
- package/dist/cjs/src/schema_types.d.ts +3 -0
- package/dist/cjs/src/schema_types.js +1 -1
- package/dist/cjs/src/session/signer_session_manager.js +3 -3
- package/dist/cjs/src/util.js +3 -2
- package/dist/esm/package.json +1 -1
- package/dist/esm/src/api.d.ts +27 -9
- package/dist/esm/src/api.js +56 -20
- package/dist/esm/src/client.d.ts +72 -3
- package/dist/esm/src/client.js +79 -2
- package/dist/esm/src/error.d.ts +26 -0
- package/dist/esm/src/error.js +64 -1
- package/dist/esm/src/events.d.ts +31 -9
- package/dist/esm/src/events.js +53 -23
- package/dist/esm/src/index.js +2 -2
- package/dist/esm/src/key.d.ts +19 -2
- package/dist/esm/src/key.js +22 -2
- package/dist/esm/src/mfa.d.ts +6 -3
- package/dist/esm/src/mfa.js +8 -5
- package/dist/esm/src/response.d.ts +14 -1
- package/dist/esm/src/response.js +65 -26
- package/dist/esm/src/role.d.ts +6 -0
- package/dist/esm/src/role.js +9 -1
- package/dist/esm/src/schema.d.ts +339 -45
- package/dist/esm/src/schema.js +1 -1
- package/dist/esm/src/schema_types.d.ts +3 -0
- package/dist/esm/src/schema_types.js +1 -1
- package/dist/esm/src/session/signer_session_manager.js +3 -3
- package/dist/esm/src/util.js +3 -2
- package/package.json +1 -1
- package/src/api.ts +66 -19
- package/src/client.ts +94 -2
- package/src/error.ts +73 -0
- package/src/events.ts +53 -24
- package/src/key.ts +31 -2
- package/src/mfa.ts +8 -4
- package/src/response.ts +50 -4
- package/src/role.ts +9 -0
- package/src/schema.ts +597 -45
- package/src/schema_types.ts +3 -0
- package/src/session/signer_session_manager.ts +2 -2
- package/src/util.ts +2 -3
- package/tsconfig.json +3 -3
package/src/events.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { ErrResponse } from "./error";
|
|
|
3
3
|
export type EventHandler<T> = (event: T) => Promise<void>;
|
|
4
4
|
export type ErrorEvent = ErrResponse;
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
export
|
|
6
|
+
/** Event emitted when a request fails because of an expired/invalid session */
|
|
7
|
+
export class SessionExpiredEvent {}
|
|
8
|
+
|
|
9
|
+
/** Event emitted when a request fails because user failed to answer an MFA challenge */
|
|
10
|
+
export class UserMfaFailedEvent extends ErrResponse {}
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Dispatcher for a single event type.
|
|
@@ -58,31 +61,13 @@ class EventDispatcher<T> {
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
const SessionExpiredRegexes = [
|
|
62
|
-
/^Session '(?<purpose>[^']*)' for '(?<identity>[^']*)' has expired$/,
|
|
63
|
-
/^Session '(?<purpose>[^']*)' for '(?<identity>[^']*)' has been revoked$/,
|
|
64
|
-
/^Auth token for epoch (?<epoch>\d+) has expired$/,
|
|
65
|
-
/^Refresh token for epoch (?<epoch_num>\d+) has expired$/,
|
|
66
|
-
/^Outdated session$/,
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Whether an error message matches one of several different "session expired" responses.
|
|
71
|
-
*
|
|
72
|
-
* @param {string} msg The string to test.
|
|
73
|
-
* @return {boolean} Whether the string matches.
|
|
74
|
-
* @internal Exported only so that it can be called from a unit test
|
|
75
|
-
*/
|
|
76
|
-
export function messageMatchesSessionExpired(msg: string): boolean {
|
|
77
|
-
return SessionExpiredRegexes.some((re) => re.test(msg));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
64
|
/**
|
|
81
65
|
* Class for registering and unregistering event handlers.
|
|
82
66
|
*/
|
|
83
67
|
export class Events {
|
|
84
68
|
readonly #onError = new EventDispatcher<ErrorEvent>();
|
|
85
69
|
readonly #onSessionExpired = new EventDispatcher<SessionExpiredEvent>();
|
|
70
|
+
readonly #onUserMfaFailed = new EventDispatcher<UserMfaFailedEvent>();
|
|
86
71
|
|
|
87
72
|
/**
|
|
88
73
|
* Register a handler for {@link ErrorEvent}: triggered every time a request to
|
|
@@ -104,6 +89,17 @@ export class Events {
|
|
|
104
89
|
this.#onSessionExpired.register(handler);
|
|
105
90
|
}
|
|
106
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Register a handler for {@link UserMfaFailedEvent}: triggered every time a
|
|
94
|
+
* request to a CubeSigner API endpoint fails because the user failed to
|
|
95
|
+
* answer an MFA challenge.
|
|
96
|
+
*
|
|
97
|
+
* @param {EventHandler<UserMfaFailedEvent>} handler The handler to register.
|
|
98
|
+
*/
|
|
99
|
+
onUserMfaFailed(handler: EventHandler<UserMfaFailedEvent>) {
|
|
100
|
+
this.#onUserMfaFailed.register(handler);
|
|
101
|
+
}
|
|
102
|
+
|
|
107
103
|
/**
|
|
108
104
|
* Unregister a handler for {@link ErrorEvent}.
|
|
109
105
|
*
|
|
@@ -124,9 +120,27 @@ export class Events {
|
|
|
124
120
|
return this.#onSessionExpired.unregister(handler);
|
|
125
121
|
}
|
|
126
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Unregister a handler for {@link UserMfaFailedEvent}.
|
|
125
|
+
*
|
|
126
|
+
* @param {EventHandler<UserMfaFailedEvent>} handler The handler to unregister.
|
|
127
|
+
* @return {boolean} Whether the handler was found (and unregistered).
|
|
128
|
+
*/
|
|
129
|
+
unregisterOnUserMfaFailed(handler: EventHandler<UserMfaFailedEvent>): boolean {
|
|
130
|
+
return this.#onUserMfaFailed.unregister(handler);
|
|
131
|
+
}
|
|
132
|
+
|
|
127
133
|
/** @internal */
|
|
128
134
|
async triggerSessionExpired() {
|
|
129
|
-
await this.#onSessionExpired.dispatch(
|
|
135
|
+
await this.#onSessionExpired.dispatch(new SessionExpiredEvent());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param {UserMfaFailedEvent} ev The event to emit
|
|
140
|
+
* @internal
|
|
141
|
+
*/
|
|
142
|
+
async triggerUserMfaFailed(ev: UserMfaFailedEvent) {
|
|
143
|
+
await this.#onUserMfaFailed.dispatch(ev);
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
/**
|
|
@@ -168,13 +182,17 @@ export class EventEmitter {
|
|
|
168
182
|
await ev.triggerErrorEvent(err);
|
|
169
183
|
}
|
|
170
184
|
|
|
171
|
-
|
|
185
|
+
if (err.isUserMfaError()) {
|
|
186
|
+
await this.emitUserMfaFailed(err);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// if status is 403 and error matches one of the "invalid session" error codes trigger onSessionExpired
|
|
172
190
|
//
|
|
173
191
|
// TODO: because errors returned by the authorizer lambda are not forwarded to the client
|
|
174
192
|
// we also trigger onSessionExpired when "signerSessionRefresh" fails
|
|
175
193
|
if (
|
|
176
194
|
err.status === 403 &&
|
|
177
|
-
(
|
|
195
|
+
(err.isSessionExpiredError() || err.operation == "signerSessionRefresh")
|
|
178
196
|
) {
|
|
179
197
|
await this.emitSessionExpired();
|
|
180
198
|
}
|
|
@@ -191,6 +209,17 @@ export class EventEmitter {
|
|
|
191
209
|
await e.triggerSessionExpired();
|
|
192
210
|
}
|
|
193
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Emits {@link UserMfaFailedEvent} to all subscribers
|
|
215
|
+
*
|
|
216
|
+
* @param {UserMfaFailedEvent} ev The event to emit.
|
|
217
|
+
*/
|
|
218
|
+
private async emitUserMfaFailed(ev: UserMfaFailedEvent) {
|
|
219
|
+
for (const e of this.#events) {
|
|
220
|
+
await e.triggerUserMfaFailed(ev);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
194
223
|
}
|
|
195
224
|
|
|
196
225
|
/**
|
package/src/key.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { KeyPolicy } from "./role";
|
|
2
|
-
import {
|
|
2
|
+
import { PageOpts } from "./paginator";
|
|
3
|
+
import {
|
|
4
|
+
KeyInfoApi,
|
|
5
|
+
KeyTypeApi,
|
|
6
|
+
UpdateKeyRequest,
|
|
7
|
+
SchemaKeyType,
|
|
8
|
+
KeyInRoleInfo,
|
|
9
|
+
} from "./schema_types";
|
|
3
10
|
import { CubeSignerClient } from "./client";
|
|
4
11
|
|
|
5
12
|
/** Secp256k1 key type */
|
|
@@ -138,6 +145,15 @@ export class Key {
|
|
|
138
145
|
await this.update({ enabled: false });
|
|
139
146
|
}
|
|
140
147
|
|
|
148
|
+
/**
|
|
149
|
+
* The list roles this key is in.
|
|
150
|
+
* @param {PageOpts} page Optional pagination options; by default, retrieves all roles this key is in.
|
|
151
|
+
* @return {Promise<KeyInRoleInfo[]>} Roles this key is in.
|
|
152
|
+
*/
|
|
153
|
+
async roles(page?: PageOpts): Promise<KeyInRoleInfo[]> {
|
|
154
|
+
return await this.csc.keyRolesList(this.id, page).fetch();
|
|
155
|
+
}
|
|
156
|
+
|
|
141
157
|
/**
|
|
142
158
|
* Set new policy (overwriting any policies previously set for this key)
|
|
143
159
|
* @param {KeyPolicy} policy The new policy to set
|
|
@@ -147,7 +163,20 @@ export class Key {
|
|
|
147
163
|
}
|
|
148
164
|
|
|
149
165
|
/**
|
|
150
|
-
*
|
|
166
|
+
* Set key metadata. The metadata must be at most 1024 characters
|
|
167
|
+
* and must match the following regex: ^[A-Za-z0-9_=+/ \-\.\,]{0,1024}$.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} metadata The new metadata to set.
|
|
170
|
+
*/
|
|
171
|
+
async setMetadata(metadata: string) {
|
|
172
|
+
await this.update({ metadata });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Append to existing key policy. This append is not atomic -- it uses {@link policy}
|
|
177
|
+
* to fetch the current policy and then {@link setPolicy} to set the policy -- and
|
|
178
|
+
* should not be used in across concurrent sessions.
|
|
179
|
+
*
|
|
151
180
|
* @param {KeyPolicy} policy The policy to append to the existing one.
|
|
152
181
|
*/
|
|
153
182
|
async appendPolicy(policy: KeyPolicy) {
|
package/src/mfa.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ApiAddFidoChallenge,
|
|
5
5
|
ApiMfaFidoChallenge,
|
|
6
6
|
MfaRequestInfo,
|
|
7
|
+
MfaVote,
|
|
7
8
|
PublicKeyCredential,
|
|
8
9
|
TotpInfo,
|
|
9
10
|
} from "./schema_types";
|
|
@@ -159,10 +160,12 @@ export class MfaFidoChallenge {
|
|
|
159
160
|
/**
|
|
160
161
|
* Answers this challenge by using the `CredentialsContainer` API to get a credential
|
|
161
162
|
* based on the the public key credential request options from this challenge.
|
|
163
|
+
*
|
|
164
|
+
* @param {MfaVote} vote Approve or reject the MFA request. Defaults to "approve".
|
|
162
165
|
*/
|
|
163
|
-
async createCredentialAndAnswer(): Promise<MfaRequestInfo> {
|
|
166
|
+
async createCredentialAndAnswer(vote?: MfaVote): Promise<MfaRequestInfo> {
|
|
164
167
|
const cred = await navigator.credentials.get({ publicKey: this.options });
|
|
165
|
-
return await this.answer(cred);
|
|
168
|
+
return await this.answer(cred, vote);
|
|
166
169
|
}
|
|
167
170
|
|
|
168
171
|
/**
|
|
@@ -175,8 +178,9 @@ export class MfaFidoChallenge {
|
|
|
175
178
|
*
|
|
176
179
|
* @param {any} cred Credential created by calling the `CredentialContainer`'s `get` method
|
|
177
180
|
* based on the public key credential request options from this challenge.
|
|
181
|
+
* @param {MfaVote} vote Approve or reject. Defaults to "approve".
|
|
178
182
|
*/
|
|
179
|
-
async answer(cred: any): Promise<MfaRequestInfo> {
|
|
183
|
+
async answer(cred: any, vote: MfaVote = "approve"): Promise<MfaRequestInfo> {
|
|
180
184
|
const answer = <PublicKeyCredential>{
|
|
181
185
|
id: cred.id,
|
|
182
186
|
response: {
|
|
@@ -185,6 +189,6 @@ export class MfaFidoChallenge {
|
|
|
185
189
|
signature: encodeToBase64Url(cred.response.signature),
|
|
186
190
|
},
|
|
187
191
|
};
|
|
188
|
-
return await this.#api.
|
|
192
|
+
return await this.#api.mfaVoteFidoComplete(this.mfaId, vote, this.challengeId, answer);
|
|
189
193
|
}
|
|
190
194
|
}
|
package/src/response.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CubeSignerClient, SignerSession } from ".";
|
|
1
|
+
import { CubeSignerClient, MfaVote, SignerSession } from ".";
|
|
2
2
|
import { MfaReceipt } from "./mfa";
|
|
3
3
|
import { AcceptedResponse, NewSessionResponse } from "./schema_types";
|
|
4
4
|
|
|
@@ -90,13 +90,39 @@ export class CubeSignerResponse<U> {
|
|
|
90
90
|
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
91
91
|
*/
|
|
92
92
|
async approveTotp(session: SignerSession, code: string): Promise<CubeSignerResponse<U>> {
|
|
93
|
+
return await this.#mfaVoteTotp(session, code, "approve");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reject the MFA request using a given session and a TOTP code.
|
|
98
|
+
*
|
|
99
|
+
* @param {SignerSession} session Signer session to use
|
|
100
|
+
* @param {string} code 6-digit TOTP code
|
|
101
|
+
*/
|
|
102
|
+
async rejectTotp(session: SignerSession, code: string) {
|
|
103
|
+
await this.#mfaVoteTotp(session, code, "reject");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Approve or reject an MFA request using a given session and a TOTP code.
|
|
108
|
+
*
|
|
109
|
+
* @param {SignerSession} session Signer session to use
|
|
110
|
+
* @param {string} code 6-digit TOTP code
|
|
111
|
+
* @param {MfaVote} vote Approve or reject
|
|
112
|
+
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
113
|
+
*/
|
|
114
|
+
async #mfaVoteTotp(
|
|
115
|
+
session: SignerSession,
|
|
116
|
+
code: string,
|
|
117
|
+
vote: MfaVote,
|
|
118
|
+
): Promise<CubeSignerResponse<U>> {
|
|
93
119
|
if (!this.requiresMfa()) {
|
|
94
120
|
return this;
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
const mfaId = this.mfaId();
|
|
98
124
|
const mfaOrgId = this.#mfaRequired!.org_id;
|
|
99
|
-
const mfaApproval = await session.
|
|
125
|
+
const mfaApproval = await session.mfaVoteTotp(mfaId, code, vote);
|
|
100
126
|
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
101
127
|
|
|
102
128
|
if (!mfaConf) {
|
|
@@ -107,12 +133,32 @@ export class CubeSignerResponse<U> {
|
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
/**
|
|
110
|
-
* Approve the MFA request using a given
|
|
136
|
+
* Approve the MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
|
|
111
137
|
*
|
|
112
138
|
* @param {CubeSignerClient} cs CubeSigner whose session to use
|
|
113
139
|
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
114
140
|
*/
|
|
115
141
|
async approve(cs: CubeSignerClient): Promise<CubeSignerResponse<U>> {
|
|
142
|
+
return await this.#mfaVoteCs(cs, "approve");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Reject the MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
|
|
147
|
+
*
|
|
148
|
+
* @param {CubeSignerClient} cs CubeSigner client whose session to use
|
|
149
|
+
*/
|
|
150
|
+
async reject(cs: CubeSignerClient) {
|
|
151
|
+
await this.#mfaVoteCs(cs, "reject");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Approve or reject an MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
|
|
156
|
+
*
|
|
157
|
+
* @param {CubeSignerClient} cs CubeSigner whose session to use
|
|
158
|
+
* @param {MfaVote} mfaVote Approve or reject
|
|
159
|
+
* @return {CubeSignerResponse<U>} The result of signing with the approval
|
|
160
|
+
*/
|
|
161
|
+
async #mfaVoteCs(cs: CubeSignerClient, mfaVote: MfaVote): Promise<CubeSignerResponse<U>> {
|
|
116
162
|
if (!this.requiresMfa()) {
|
|
117
163
|
return this;
|
|
118
164
|
}
|
|
@@ -120,7 +166,7 @@ export class CubeSignerResponse<U> {
|
|
|
120
166
|
const mfaId = this.#mfaRequired!.id;
|
|
121
167
|
const mfaOrgId = this.#mfaRequired!.org_id;
|
|
122
168
|
|
|
123
|
-
const mfaApproval = await cs.
|
|
169
|
+
const mfaApproval = await cs.mfaVoteCs(mfaId, mfaVote);
|
|
124
170
|
const mfaConf = mfaApproval.receipt?.confirmation;
|
|
125
171
|
|
|
126
172
|
if (!mfaConf) {
|
package/src/role.ts
CHANGED
|
@@ -278,6 +278,15 @@ export class Role {
|
|
|
278
278
|
await this.#csc.roleUserAdd(this.id, userId);
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Remove an existing user from an existing role.
|
|
283
|
+
*
|
|
284
|
+
* @param {string} userId The user-id of the user to remove from the role.
|
|
285
|
+
*/
|
|
286
|
+
async removeUser(userId: string) {
|
|
287
|
+
await this.#csc.roleUserRemove(this.id, userId);
|
|
288
|
+
}
|
|
289
|
+
|
|
281
290
|
/**
|
|
282
291
|
* The list of keys in the role.
|
|
283
292
|
* @example [
|