@cubist-labs/cubesigner-sdk 0.2.17 → 0.2.21
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 +6 -6
- package/dist/src/api.d.ts +145 -15
- package/dist/src/api.js +340 -252
- package/dist/src/client.d.ts +28 -8
- package/dist/src/client.js +33 -13
- package/dist/src/events.d.ts +84 -0
- package/dist/src/events.js +195 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +10 -6
- package/dist/src/mfa.js +3 -3
- package/dist/src/schema.d.ts +81 -0
- package/dist/src/schema.js +1 -1
- package/dist/src/schema_types.d.ts +1 -0
- package/dist/src/schema_types.js +1 -1
- package/dist/src/session/cognito_manager.js +2 -2
- package/dist/src/session/session_manager.d.ts +6 -4
- package/dist/src/session/session_manager.js +11 -5
- package/dist/src/session/signer_session_manager.d.ts +8 -2
- package/dist/src/session/signer_session_manager.js +43 -16
- package/dist/src/util.d.ts +0 -30
- package/dist/src/util.js +2 -38
- package/package.json +6 -6
- package/src/api.ts +424 -250
- package/src/client.ts +34 -12
- package/src/events.ts +197 -0
- package/src/index.ts +6 -4
- package/src/mfa.ts +2 -2
- package/src/schema.ts +81 -0
- package/src/schema_types.ts +2 -0
- package/src/session/cognito_manager.ts +2 -2
- package/src/session/session_manager.ts +11 -5
- package/src/session/signer_session_manager.ts +51 -18
- package/src/util.ts +0 -45
package/src/client.ts
CHANGED
|
@@ -316,7 +316,7 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
316
316
|
/**
|
|
317
317
|
* Get a pending MFA request by its id.
|
|
318
318
|
*
|
|
319
|
-
* Same as {@link
|
|
319
|
+
* Same as {@link mfaGet}
|
|
320
320
|
*/
|
|
321
321
|
get getMfaInfo() {
|
|
322
322
|
return this.mfaGet.bind(this);
|
|
@@ -325,7 +325,7 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
325
325
|
/**
|
|
326
326
|
* List pending MFA requests accessible to the current user.
|
|
327
327
|
*
|
|
328
|
-
* Same as {@link
|
|
328
|
+
* Same as {@link mfaList}
|
|
329
329
|
*/
|
|
330
330
|
get listMfaInfos() {
|
|
331
331
|
return this.mfaList.bind(this);
|
|
@@ -334,7 +334,7 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
334
334
|
/**
|
|
335
335
|
* Obtain a proof of authentication.
|
|
336
336
|
*
|
|
337
|
-
* Same as {@link
|
|
337
|
+
* Same as {@link identityProve}
|
|
338
338
|
*/
|
|
339
339
|
get proveIdentity() {
|
|
340
340
|
return this.identityProve.bind(this);
|
|
@@ -343,7 +343,7 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
343
343
|
/**
|
|
344
344
|
* Check if a given proof of OIDC authentication is valid.
|
|
345
345
|
*
|
|
346
|
-
* Same as {@link
|
|
346
|
+
* Same as {@link identityVerify}
|
|
347
347
|
*/
|
|
348
348
|
get verifyIdentity() {
|
|
349
349
|
return this.identityVerify.bind(this);
|
|
@@ -356,10 +356,21 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
356
356
|
*
|
|
357
357
|
* MFA may be required.
|
|
358
358
|
*
|
|
359
|
-
* Same as {@link
|
|
359
|
+
* Same as {@link userFidoRegisterInit}
|
|
360
360
|
*/
|
|
361
361
|
get addFidoStart() {
|
|
362
|
-
return this.
|
|
362
|
+
return this.userFidoRegisterInit.bind(this);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Delete a FIDO key from the user's account.
|
|
367
|
+
* Allowed only if TOTP is also defined.
|
|
368
|
+
* MFA via TOTP is always required.
|
|
369
|
+
*
|
|
370
|
+
* Same as {@link userFidoDelete}
|
|
371
|
+
*/
|
|
372
|
+
get deleteFido() {
|
|
373
|
+
return this.userFidoDelete.bind(this);
|
|
363
374
|
}
|
|
364
375
|
|
|
365
376
|
/**
|
|
@@ -367,30 +378,41 @@ export class CubeSignerClient extends CubeSignerApi {
|
|
|
367
378
|
* that must be answered by calling {@link TotpChallenge.answer} or
|
|
368
379
|
* {@link resetTotpComplete}.
|
|
369
380
|
*
|
|
370
|
-
* Same as {@link
|
|
381
|
+
* Same as {@link userTotpResetInit}
|
|
371
382
|
*/
|
|
372
383
|
get resetTotpStart() {
|
|
373
|
-
return this.
|
|
384
|
+
return this.userTotpResetInit.bind(this);
|
|
374
385
|
}
|
|
375
386
|
|
|
376
387
|
/**
|
|
377
388
|
* Answer the TOTP challenge issued by {@link resetTotpStart}. If successful,
|
|
378
389
|
* user's TOTP configuration will be updated to that of the TOTP challenge.
|
|
379
390
|
*
|
|
380
|
-
* Same as {@link
|
|
391
|
+
* Same as {@link userTotpResetComplete}
|
|
381
392
|
*/
|
|
382
393
|
get resetTotpComplete() {
|
|
383
|
-
return this.
|
|
394
|
+
return this.userTotpResetComplete.bind(this);
|
|
384
395
|
}
|
|
385
396
|
|
|
386
397
|
/**
|
|
387
398
|
* Verifies a given TOTP code against the current user's TOTP configuration.
|
|
388
399
|
* Throws an error if the verification fails.
|
|
389
400
|
*
|
|
390
|
-
* Same as {@link
|
|
401
|
+
* Same as {@link userTotpVerify}
|
|
391
402
|
*/
|
|
392
403
|
get verifyTotp() {
|
|
393
|
-
return this.
|
|
404
|
+
return this.userTotpVerify.bind(this);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Delete TOTP from the user's account.
|
|
409
|
+
* Allowed only if at least one FIDO key is registered with the user's account.
|
|
410
|
+
* MFA via FIDO is always required.
|
|
411
|
+
*
|
|
412
|
+
* Same as {@link userTotpDelete}.
|
|
413
|
+
*/
|
|
414
|
+
get deleteTotp() {
|
|
415
|
+
return this.userTotpDelete.bind(this);
|
|
394
416
|
}
|
|
395
417
|
|
|
396
418
|
/**
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { ErrResponse } from "./api";
|
|
2
|
+
|
|
3
|
+
export type EventHandler<T> = (event: T) => Promise<void>;
|
|
4
|
+
export type ErrorEvent = ErrResponse;
|
|
5
|
+
export interface SessionExpiredEvent {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Dispatcher for a single event type.
|
|
9
|
+
*
|
|
10
|
+
* Provides methods for registering and unregistering handlers,
|
|
11
|
+
* as well as dispatching events to all registered handlers.
|
|
12
|
+
*/
|
|
13
|
+
class EventDispatcher<T> {
|
|
14
|
+
readonly #handlers: EventHandler<T>[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Constructor.
|
|
18
|
+
*/
|
|
19
|
+
constructor() {
|
|
20
|
+
this.#handlers = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register a new handler.
|
|
25
|
+
*
|
|
26
|
+
* @param {EventHandler<T>} handler Event handler to register
|
|
27
|
+
* @return {EventDispatcher<T>} This instance to allow for chaining.
|
|
28
|
+
*/
|
|
29
|
+
register(handler: EventHandler<T>): EventDispatcher<T> {
|
|
30
|
+
this.#handlers.push(handler);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Unregister a handler. If {@link handler} is not already registered, it's a no-op.
|
|
36
|
+
*
|
|
37
|
+
* @param {EventHandler<T>} handler Event handler to unregister
|
|
38
|
+
* @return {boolean} Whether the handler was found (and unregistered).
|
|
39
|
+
*/
|
|
40
|
+
unregister(handler: EventHandler<T>): boolean {
|
|
41
|
+
const idx = this.#handlers.indexOf(handler);
|
|
42
|
+
if (idx >= 0) {
|
|
43
|
+
this.#handlers.splice(idx, 1);
|
|
44
|
+
return true;
|
|
45
|
+
} else {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Dispatch an event to all registered handlers.
|
|
52
|
+
* @param {T} event Event to dispatch.
|
|
53
|
+
*/
|
|
54
|
+
async dispatch(event: T): Promise<void> {
|
|
55
|
+
await Promise.all(this.#handlers.map((h) => h(event)));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const SessionExpiredRegexes = [
|
|
60
|
+
/^Session '(?<purpose>[^']*)' for '(?<identity>[^']*)' has expired$/,
|
|
61
|
+
/^Session '(?<purpose>[^']*)' for '(?<identity>[^']*)' has been revoked$/,
|
|
62
|
+
/^Auth token for epoch (?<epoch>\d+) has expired$/,
|
|
63
|
+
/^Refresh token for epoch (?<epoch_num>\d+) has expired$/,
|
|
64
|
+
/^Outdated session$/,
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether an error message matches one of several different "session expired" responses.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} msg The string to test.
|
|
71
|
+
* @return {boolean} Whether the string matches.
|
|
72
|
+
* @internal Exported only so that it can be called from a unit test
|
|
73
|
+
*/
|
|
74
|
+
export function messageMatchesSessionExpired(msg: string): boolean {
|
|
75
|
+
return SessionExpiredRegexes.some((re) => re.test(msg));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Class for registering and unregistering event handlers.
|
|
80
|
+
*/
|
|
81
|
+
export class Events {
|
|
82
|
+
readonly #onError = new EventDispatcher<ErrorEvent>();
|
|
83
|
+
readonly #onSessionExpired = new EventDispatcher<SessionExpiredEvent>();
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Register a handler for {@link ErrorEvent}: triggered every time a request to
|
|
87
|
+
* a CubeSigner API endpoint returns a non-success response.
|
|
88
|
+
*
|
|
89
|
+
* @param {EventHandler<ErrorEvent>} handler The handler to register.
|
|
90
|
+
*/
|
|
91
|
+
onError(handler: EventHandler<ErrorEvent>) {
|
|
92
|
+
this.#onError.register(handler);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Register a handler for {@link SessionExpiredEvent}: triggered every time a
|
|
97
|
+
* request to a CubeSigner API endpoint fails because of an expired session.
|
|
98
|
+
*
|
|
99
|
+
* @param {EventHandler<SessionExpiredEvent>} handler The handler to register.
|
|
100
|
+
*/
|
|
101
|
+
onSessionExpired(handler: EventHandler<SessionExpiredEvent>) {
|
|
102
|
+
this.#onSessionExpired.register(handler);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Unregister a handler for {@link ErrorEvent}.
|
|
107
|
+
*
|
|
108
|
+
* @param {EventHandler<ErrorEvent>} handler The handler to unregister.
|
|
109
|
+
* @return {boolean} Whether the handler was found (and unregistered).
|
|
110
|
+
*/
|
|
111
|
+
unregisterOnError(handler: EventHandler<ErrorEvent>): boolean {
|
|
112
|
+
return this.#onError.unregister(handler);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Unregister a handler for {@link SessionExpiredEvent}.
|
|
117
|
+
*
|
|
118
|
+
* @param {EventHandler<SessionExpiredEvent>} handler The handler to unregister.
|
|
119
|
+
* @return {boolean} Whether the handler was found (and unregistered).
|
|
120
|
+
*/
|
|
121
|
+
unregisterOnSessionExpired(handler: EventHandler<SessionExpiredEvent>): boolean {
|
|
122
|
+
return this.#onSessionExpired.unregister(handler);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @internal */
|
|
126
|
+
async triggerSessionExpired() {
|
|
127
|
+
await this.#onSessionExpired.dispatch(<SessionExpiredEvent>{});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {ErrorEvent} event Event to trigger
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
async triggerErrorEvent(event: ErrorEvent) {
|
|
135
|
+
await this.#onError.dispatch(event);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Used to classify and emit events to one or more {@link Events} instances.
|
|
141
|
+
*/
|
|
142
|
+
export class EventEmitter {
|
|
143
|
+
readonly #events: Events[];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
*
|
|
147
|
+
* @param {Events[]} events Instances to which to emit events
|
|
148
|
+
* @param {boolean} skipGlobal Whether to include the global events instance {@link GlobalEvents}
|
|
149
|
+
*/
|
|
150
|
+
constructor(events: Events[], skipGlobal?: boolean) {
|
|
151
|
+
skipGlobal ??= false;
|
|
152
|
+
this.#events = events;
|
|
153
|
+
if (!skipGlobal) {
|
|
154
|
+
this.#events.push(GlobalEvents);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Called by {@link CubeSignerApi} when an API response indicates an error.
|
|
160
|
+
*
|
|
161
|
+
* @param {ErrorEvent} err The error to dispatch.
|
|
162
|
+
* @internal
|
|
163
|
+
*/
|
|
164
|
+
async classifyAndEmitError(err: ErrorEvent) {
|
|
165
|
+
for (const ev of this.#events) {
|
|
166
|
+
await ev.triggerErrorEvent(err);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// if status is 403 and error matches one of the SessionExpiredRegexes trigger onSessionExpired
|
|
170
|
+
//
|
|
171
|
+
// TODO: because errors returned by the authorizer lambda are not forwarded to the client
|
|
172
|
+
// we also trigger onSessionExpired when "signerSessionRefresh" fails
|
|
173
|
+
if (
|
|
174
|
+
err.status === 403 &&
|
|
175
|
+
(messageMatchesSessionExpired(err.message) || err.operation == "signerSessionRefresh")
|
|
176
|
+
) {
|
|
177
|
+
await this.emitSessionExpired();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Called by {@link SignerSessionManager} to notify that the session is expired
|
|
183
|
+
* beyond the possibility of refreshing, meaning that full re-login is required.
|
|
184
|
+
*
|
|
185
|
+
* @internal
|
|
186
|
+
*/
|
|
187
|
+
async emitSessionExpired() {
|
|
188
|
+
for (const e of this.#events) {
|
|
189
|
+
await e.triggerSessionExpired();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Global events.
|
|
196
|
+
*/
|
|
197
|
+
export const GlobalEvents = new Events();
|
package/src/index.ts
CHANGED
|
@@ -180,7 +180,7 @@ export class CubeSigner {
|
|
|
180
180
|
|
|
181
181
|
/** Initiate adding a new FIDO device. MFA may be required. */
|
|
182
182
|
get addFidoStart() {
|
|
183
|
-
return this.csc.
|
|
183
|
+
return this.csc.userFidoRegisterInit.bind(this.csc);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
@@ -188,7 +188,7 @@ export class CubeSigner {
|
|
|
188
188
|
* that must be answered by calling `resetTotpComplete`
|
|
189
189
|
*/
|
|
190
190
|
get resetTotpStart() {
|
|
191
|
-
return this.csc.
|
|
191
|
+
return this.csc.userTotpResetInit.bind(this.#csc);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
@@ -196,7 +196,7 @@ export class CubeSigner {
|
|
|
196
196
|
* TOTP configuration will be updated to that of the TOTP challenge.he TOTP configuration from the challenge.
|
|
197
197
|
*/
|
|
198
198
|
get resetTotpComplete() {
|
|
199
|
-
return this.csc.
|
|
199
|
+
return this.csc.userTotpResetComplete.bind(this.#csc);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
/**
|
|
@@ -204,7 +204,7 @@ export class CubeSigner {
|
|
|
204
204
|
* Throws an error if the verification fails.
|
|
205
205
|
*/
|
|
206
206
|
get verifyTotp() {
|
|
207
|
-
return this.csc.
|
|
207
|
+
return this.csc.userTotpVerify.bind(this.#csc);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
/**
|
|
@@ -294,6 +294,8 @@ export class CubeSigner {
|
|
|
294
294
|
export * from "./api";
|
|
295
295
|
/** Client */
|
|
296
296
|
export * from "./client";
|
|
297
|
+
/** Callbacks */
|
|
298
|
+
export { Events, EventHandler, ErrorEvent, GlobalEvents, SessionExpiredEvent } from "./events";
|
|
297
299
|
/** Organizations */
|
|
298
300
|
export * from "./org";
|
|
299
301
|
/** Keys */
|
package/src/mfa.ts
CHANGED
|
@@ -53,7 +53,7 @@ export class TotpChallenge {
|
|
|
53
53
|
throw new Error(`Invalid TOTP code: ${code}; it must be a 6-digit string`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
await this.#api.
|
|
56
|
+
await this.#api.userTotpResetComplete(this.totpId, code);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -118,7 +118,7 @@ export class AddFidoChallenge {
|
|
|
118
118
|
attestationObject: encodeToBase64Url(cred.response.attestationObject),
|
|
119
119
|
},
|
|
120
120
|
};
|
|
121
|
-
await this.#api.
|
|
121
|
+
await this.#api.userFidoRegisterComplete(this.challengeId, answer);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
package/src/schema.ts
CHANGED
|
@@ -504,6 +504,15 @@ export interface paths {
|
|
|
504
504
|
*/
|
|
505
505
|
patch: operations["userRegisterFidoComplete"];
|
|
506
506
|
};
|
|
507
|
+
"/v0/org/{org_id}/user/me/fido/{fido_id}": {
|
|
508
|
+
/**
|
|
509
|
+
* Delete FIDO key
|
|
510
|
+
* @description Delete FIDO key
|
|
511
|
+
*
|
|
512
|
+
* Deletes a FIDO key from the user's account (if the key is not the sole MFA factor). MFA is always required.
|
|
513
|
+
*/
|
|
514
|
+
delete: operations["userDeleteFido"];
|
|
515
|
+
};
|
|
507
516
|
"/v0/org/{org_id}/user/me/totp": {
|
|
508
517
|
/**
|
|
509
518
|
* Initialize TOTP Reset
|
|
@@ -518,6 +527,13 @@ export interface paths {
|
|
|
518
527
|
* otherwise, MFA is required.
|
|
519
528
|
*/
|
|
520
529
|
post: operations["userResetTotpInit"];
|
|
530
|
+
/**
|
|
531
|
+
* Delete TOTP
|
|
532
|
+
* @description Delete TOTP
|
|
533
|
+
*
|
|
534
|
+
* Deletes TOTP from the user's account (if TOTP is not the sole MFA factor). MFA is always required.
|
|
535
|
+
*/
|
|
536
|
+
delete: operations["userDeleteTotp"];
|
|
521
537
|
/**
|
|
522
538
|
* Finalize resetting TOTP
|
|
523
539
|
* @description Finalize resetting TOTP
|
|
@@ -5385,6 +5401,41 @@ export interface operations {
|
|
|
5385
5401
|
};
|
|
5386
5402
|
};
|
|
5387
5403
|
};
|
|
5404
|
+
/**
|
|
5405
|
+
* Delete FIDO key
|
|
5406
|
+
* @description Delete FIDO key
|
|
5407
|
+
*
|
|
5408
|
+
* Deletes a FIDO key from the user's account (if the key is not the sole MFA factor). MFA is always required.
|
|
5409
|
+
*/
|
|
5410
|
+
userDeleteFido: {
|
|
5411
|
+
parameters: {
|
|
5412
|
+
path: {
|
|
5413
|
+
/**
|
|
5414
|
+
* @description Name or ID of the desired Org
|
|
5415
|
+
* @example Org#124dfe3e-3bbd-487d-80c0-53c55e8ab87a
|
|
5416
|
+
*/
|
|
5417
|
+
org_id: string;
|
|
5418
|
+
/**
|
|
5419
|
+
* @description Name or ID of the desired FidoKey
|
|
5420
|
+
* @example FidoKey#124dfe3e-3bbd-487d-80c0-53c55e8ab87a
|
|
5421
|
+
*/
|
|
5422
|
+
fido_id: string;
|
|
5423
|
+
};
|
|
5424
|
+
};
|
|
5425
|
+
requestBody: {
|
|
5426
|
+
content: {
|
|
5427
|
+
"application/json": components["schemas"]["Empty"];
|
|
5428
|
+
};
|
|
5429
|
+
};
|
|
5430
|
+
responses: {
|
|
5431
|
+
200: components["responses"]["EmptyImpl"];
|
|
5432
|
+
default: {
|
|
5433
|
+
content: {
|
|
5434
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5435
|
+
};
|
|
5436
|
+
};
|
|
5437
|
+
};
|
|
5438
|
+
};
|
|
5388
5439
|
/**
|
|
5389
5440
|
* Initialize TOTP Reset
|
|
5390
5441
|
* @description Initialize TOTP Reset
|
|
@@ -5426,6 +5477,36 @@ export interface operations {
|
|
|
5426
5477
|
};
|
|
5427
5478
|
};
|
|
5428
5479
|
};
|
|
5480
|
+
/**
|
|
5481
|
+
* Delete TOTP
|
|
5482
|
+
* @description Delete TOTP
|
|
5483
|
+
*
|
|
5484
|
+
* Deletes TOTP from the user's account (if TOTP is not the sole MFA factor). MFA is always required.
|
|
5485
|
+
*/
|
|
5486
|
+
userDeleteTotp: {
|
|
5487
|
+
parameters: {
|
|
5488
|
+
path: {
|
|
5489
|
+
/**
|
|
5490
|
+
* @description Name or ID of the desired Org
|
|
5491
|
+
* @example Org#124dfe3e-3bbd-487d-80c0-53c55e8ab87a
|
|
5492
|
+
*/
|
|
5493
|
+
org_id: string;
|
|
5494
|
+
};
|
|
5495
|
+
};
|
|
5496
|
+
requestBody: {
|
|
5497
|
+
content: {
|
|
5498
|
+
"application/json": components["schemas"]["Empty"];
|
|
5499
|
+
};
|
|
5500
|
+
};
|
|
5501
|
+
responses: {
|
|
5502
|
+
200: components["responses"]["EmptyImpl"];
|
|
5503
|
+
default: {
|
|
5504
|
+
content: {
|
|
5505
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5506
|
+
};
|
|
5507
|
+
};
|
|
5508
|
+
};
|
|
5509
|
+
};
|
|
5429
5510
|
/**
|
|
5430
5511
|
* Finalize resetting TOTP
|
|
5431
5512
|
* @description Finalize resetting TOTP
|
package/src/schema_types.ts
CHANGED
|
@@ -85,6 +85,8 @@ export type UserExportCompleteResponse = schemas["UserExportCompleteResponse"];
|
|
|
85
85
|
export type UserExportListResponse = schemas["PaginatedUserExportListResponse"];
|
|
86
86
|
export type UserExportKeyMaterial = schemas["JsonKeyPackage"];
|
|
87
87
|
|
|
88
|
+
export type Empty = schemas["EmptyImpl"];
|
|
89
|
+
|
|
88
90
|
/** Options for a new OIDC user */
|
|
89
91
|
export interface CreateOidcUserOptions {
|
|
90
92
|
/** The role of an OIDC user, default is "Alien" */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { Client } from "../api";
|
|
3
3
|
import { EnvInterface } from "../env";
|
|
4
|
-
import { HasEnv, OrgSessionManager } from "./session_manager";
|
|
4
|
+
import { HasEnv, OrgSessionManager, SessionManager } from "./session_manager";
|
|
5
5
|
import { JsonFileSessionStorage, SessionStorage } from "./session_storage";
|
|
6
6
|
import { configDir } from "../util";
|
|
7
7
|
|
|
@@ -70,7 +70,7 @@ export class CognitoSessionManager extends OrgSessionManager<CognitoSessionInfo>
|
|
|
70
70
|
*/
|
|
71
71
|
async isStale(): Promise<boolean> {
|
|
72
72
|
const session = await this.storage.retrieve();
|
|
73
|
-
return
|
|
73
|
+
return SessionManager.hasExpired(new Date(session.expiration));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Events } from "../events";
|
|
2
2
|
import { EnvInterface } from "../env";
|
|
3
3
|
import { Client, createHttpClient } from "../api";
|
|
4
|
+
import { SessionStorage } from "./session_storage";
|
|
4
5
|
|
|
5
6
|
const DEFAULT_EXPIRATION_BUFFER_SECS = 30;
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ const DEFAULT_EXPIRATION_BUFFER_SECS = 30;
|
|
|
8
9
|
export abstract class SessionManager<U> {
|
|
9
10
|
readonly env: EnvInterface;
|
|
10
11
|
readonly storage: SessionStorage<U>;
|
|
12
|
+
readonly events = new Events();
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* @return {string} The current auth token.
|
|
@@ -87,12 +89,16 @@ export abstract class SessionManager<U> {
|
|
|
87
89
|
|
|
88
90
|
/**
|
|
89
91
|
* Check if a timestamp has expired.
|
|
90
|
-
* @param {
|
|
91
|
-
* @param {number}
|
|
92
|
+
* @param {Date} exp The timestamp to check
|
|
93
|
+
* @param {number} bufferSeconds Time buffer in seconds (defaults to 30s)
|
|
92
94
|
* @return {boolean} True if the timestamp has expired
|
|
93
95
|
*/
|
|
94
|
-
protected hasExpired(exp:
|
|
95
|
-
|
|
96
|
+
protected static hasExpired(exp: Date, bufferSeconds?: number): boolean {
|
|
97
|
+
bufferSeconds ??= DEFAULT_EXPIRATION_BUFFER_SECS;
|
|
98
|
+
const expMsSinceEpoch = exp.getTime();
|
|
99
|
+
const nowMsSinceEpoch = new Date().getTime();
|
|
100
|
+
const bufferMs = bufferSeconds * 1000;
|
|
101
|
+
return expMsSinceEpoch < nowMsSinceEpoch + bufferMs;
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
/**
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { EnvInterface } from "..";
|
|
2
1
|
import {
|
|
3
2
|
ClientSessionInfo,
|
|
4
3
|
NewSessionResponse,
|
|
5
4
|
RefreshSignerSessionRequest,
|
|
6
5
|
} from "../schema_types";
|
|
7
|
-
import { Client } from "../api";
|
|
8
|
-
import { HasEnv, OrgSessionManager } from "./session_manager";
|
|
6
|
+
import { Client, OpClient } from "../api";
|
|
7
|
+
import { HasEnv, OrgSessionManager, SessionManager } from "./session_manager";
|
|
9
8
|
import { MemorySessionStorage, SessionStorage } from "./session_storage";
|
|
10
|
-
import {
|
|
9
|
+
import { EventEmitter } from "../events";
|
|
10
|
+
import { EnvInterface } from "../env";
|
|
11
11
|
|
|
12
12
|
/** JSON representation of our "signer session" file format */
|
|
13
13
|
export interface SignerSessionObject {
|
|
@@ -23,6 +23,15 @@ export interface SignerSessionObject {
|
|
|
23
23
|
session_info: ClientSessionInfo;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Constructs {@link Date} from a number representing seconds since unix epoch.
|
|
28
|
+
* @param {number} secs Seconds since unix epoch.
|
|
29
|
+
* @return {Date} The equivalent date.
|
|
30
|
+
*/
|
|
31
|
+
function secondsSinceEpochToDate(secs: number): Date {
|
|
32
|
+
return new Date(secs * 1000);
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
export interface SignerSessionData extends SignerSessionObject, HasEnv {}
|
|
27
36
|
|
|
28
37
|
/** Type of storage required for signer sessions */
|
|
@@ -41,7 +50,8 @@ export interface SignerSessionLifetime {
|
|
|
41
50
|
|
|
42
51
|
/** Manager for signer sessions. */
|
|
43
52
|
export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
44
|
-
#
|
|
53
|
+
readonly #eventEmitter: EventEmitter;
|
|
54
|
+
#client: { client: Client; exp: Date };
|
|
45
55
|
|
|
46
56
|
/**
|
|
47
57
|
* @return {string} The current auth token.
|
|
@@ -59,17 +69,21 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
59
69
|
*/
|
|
60
70
|
async client(): Promise<Client> {
|
|
61
71
|
await this.refreshIfNeeded();
|
|
62
|
-
|
|
72
|
+
|
|
73
|
+
// trigger "session expired" if for whatever reason the token is still stale
|
|
74
|
+
if (SessionManager.hasExpired(this.#client.exp, /* buffer */ 0)) {
|
|
75
|
+
await this.#eventEmitter.emitSessionExpired();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this.#client.client;
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
/** Revokes the session. */
|
|
66
82
|
async revoke(): Promise<void> {
|
|
67
|
-
const client = await this.client();
|
|
68
|
-
|
|
83
|
+
const client = new OpClient("revokeCurrentSession", await this.client(), this.#eventEmitter);
|
|
84
|
+
await client.del("/v0/org/{org_id}/session/self", {
|
|
69
85
|
params: { path: { org_id: this.orgId } },
|
|
70
|
-
parseAs: "json",
|
|
71
86
|
});
|
|
72
|
-
assertOk(resp);
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
/**
|
|
@@ -78,8 +92,7 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
78
92
|
* @internal
|
|
79
93
|
*/
|
|
80
94
|
async isStale(): Promise<boolean> {
|
|
81
|
-
|
|
82
|
-
return this.hasExpired(session.session_info.auth_token_exp * 1000);
|
|
95
|
+
return SessionManager.hasExpired(this.#client.exp);
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
/**
|
|
@@ -88,17 +101,16 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
88
101
|
async refresh(): Promise<void> {
|
|
89
102
|
const currSession = await this.storage.retrieve();
|
|
90
103
|
|
|
104
|
+
const client = new OpClient("signerSessionRefresh", this.#client.client, this.#eventEmitter);
|
|
91
105
|
const csi = currSession.session_info;
|
|
92
|
-
const
|
|
106
|
+
const data = await client.patch("/v1/org/{org_id}/token/refresh", {
|
|
93
107
|
params: { path: { org_id: this.orgId } },
|
|
94
108
|
body: <RefreshSignerSessionRequest>{
|
|
95
109
|
epoch_num: csi.epoch,
|
|
96
110
|
epoch_token: csi.epoch_token,
|
|
97
111
|
other_token: csi.refresh_token,
|
|
98
112
|
},
|
|
99
|
-
parseAs: "json",
|
|
100
113
|
});
|
|
101
|
-
const data = assertOk(resp);
|
|
102
114
|
const newSession = <SignerSessionData>{
|
|
103
115
|
...currSession,
|
|
104
116
|
session_info: data.session_info,
|
|
@@ -106,7 +118,10 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
106
118
|
};
|
|
107
119
|
|
|
108
120
|
await this.storage.save(newSession);
|
|
109
|
-
this.#client =
|
|
121
|
+
this.#client = {
|
|
122
|
+
client: this.createClient(newSession.token),
|
|
123
|
+
exp: secondsSinceEpochToDate(newSession.session_info.auth_token_exp),
|
|
124
|
+
};
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
/**
|
|
@@ -136,6 +151,20 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
136
151
|
return await SignerSessionManager.loadFromStorage(storage);
|
|
137
152
|
}
|
|
138
153
|
|
|
154
|
+
/**
|
|
155
|
+
* @param {SignerSessionData} sessionData The session information.
|
|
156
|
+
* @param {SignerSessionStorage} storage The storage to use for saving the session.
|
|
157
|
+
* @return {Promise<SignerSessionManager>} New signer session manager.
|
|
158
|
+
*/
|
|
159
|
+
static async createFromSessionData(
|
|
160
|
+
sessionData: SignerSessionData,
|
|
161
|
+
storage?: SignerSessionStorage,
|
|
162
|
+
): Promise<SignerSessionManager> {
|
|
163
|
+
storage ??= new MemorySessionStorage();
|
|
164
|
+
await storage.save(sessionData);
|
|
165
|
+
return await SignerSessionManager.loadFromStorage(storage);
|
|
166
|
+
}
|
|
167
|
+
|
|
139
168
|
/**
|
|
140
169
|
* Uses an existing session to create a new signer session manager.
|
|
141
170
|
*
|
|
@@ -153,8 +182,12 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
|
|
|
153
182
|
* @param {SignerSessionData} sessionData Session data
|
|
154
183
|
* @param {SignerSessionStorage} storage The session storage to use.
|
|
155
184
|
*/
|
|
156
|
-
|
|
185
|
+
protected constructor(sessionData: SignerSessionData, storage: SignerSessionStorage) {
|
|
157
186
|
super(sessionData.env["Dev-CubeSignerStack"], sessionData.org_id, storage);
|
|
158
|
-
this.#
|
|
187
|
+
this.#eventEmitter = new EventEmitter([this.events]);
|
|
188
|
+
this.#client = {
|
|
189
|
+
client: this.createClient(sessionData.token),
|
|
190
|
+
exp: secondsSinceEpochToDate(sessionData.session_info.auth_token_exp),
|
|
191
|
+
};
|
|
159
192
|
}
|
|
160
193
|
}
|