@cubist-labs/cubesigner-sdk 0.2.21 → 0.2.24

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/src/schema.ts CHANGED
@@ -886,7 +886,7 @@ export interface components {
886
886
  * https://www.w3.org/TR/webauthn-2/#dictdef-authenticatorselectioncriteria
887
887
  */
888
888
  AuthenticatorSelectionCriteria: {
889
- authenticator_attachment?: components["schemas"]["AuthenticatorAttachment"] | null;
889
+ authenticatorAttachment?: components["schemas"]["AuthenticatorAttachment"] | null;
890
890
  /**
891
891
  * @description This member is retained for backwards compatibility with WebAuthn Level
892
892
  * 1 and, for historical reasons, its naming retains the deprecated
@@ -895,9 +895,9 @@ export interface components {
895
895
  *
896
896
  * https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-requireresidentkey
897
897
  */
898
- require_resident_key?: boolean;
899
- resident_key?: components["schemas"]["ResidentKeyRequirement"] | null;
900
- user_verification?: components["schemas"]["UserVerificationRequirement"];
898
+ requireResidentKey?: boolean;
899
+ residentKey?: components["schemas"]["ResidentKeyRequirement"] | null;
900
+ userVerification?: components["schemas"]["UserVerificationRequirement"];
901
901
  };
902
902
  /**
903
903
  * @description Authenticators may implement various transports for communicating with
@@ -1580,25 +1580,6 @@ export interface components {
1580
1580
  */
1581
1581
  skip_email: boolean;
1582
1582
  };
1583
- /**
1584
- * @description Key material contained inside a [`JsonKeyPackage`], which can be either
1585
- * a raw secret or a mnemonic, password, and derivation path.
1586
- */
1587
- JsonKeyMaterial: {
1588
- /** @enum {string} */
1589
- material_type: "raw_secret";
1590
- /** @description The value of the raw secret */
1591
- secret: string;
1592
- } | {
1593
- /** @description The derivation path */
1594
- derivation_path: string;
1595
- /** @enum {string} */
1596
- material_type: "english_mnemonic";
1597
- /** @description The mnemonic */
1598
- mnemonic: string;
1599
- /** @description The password (which may be empty) */
1600
- password: string;
1601
- };
1602
1583
  /**
1603
1584
  * @description A [`KeyPackage`] serialized into a format that gives a tidier JSON
1604
1585
  * representation suitable for encryption in the user-export flow.
@@ -1669,9 +1650,21 @@ export interface components {
1669
1650
  * );
1670
1651
  * ```
1671
1652
  */
1672
- JsonKeyPackage: {
1673
- material_type: "JsonKeyPackage";
1674
- } & Omit<components["schemas"]["JsonKeyMaterial"], "material_type"> & {
1653
+ JsonKeyPackage: ({
1654
+ /** @enum {string} */
1655
+ material_type: "raw_secret";
1656
+ /** @description The value of the raw secret */
1657
+ secret: string;
1658
+ } | {
1659
+ /** @description The derivation path */
1660
+ derivation_path: string;
1661
+ /** @enum {string} */
1662
+ material_type: "english_mnemonic";
1663
+ /** @description The mnemonic */
1664
+ mnemonic: string;
1665
+ /** @description The password (which may be empty) */
1666
+ password: string;
1667
+ }) & {
1675
1668
  /** @description The type of key this package represents */
1676
1669
  key_type: string;
1677
1670
  };
@@ -1811,6 +1804,12 @@ export interface components {
1811
1804
  Network: "mainnet" | "prater" | "goerli" | "holesky";
1812
1805
  /** @description Information about a new session, returned from multiple endpoints (e.g., login, refresh, etc.). */
1813
1806
  NewSessionResponse: {
1807
+ /**
1808
+ * Format: int64
1809
+ * @description Session expiration (in seconds since UNIX epoch), beyond which it cannot be refreshed.
1810
+ * @example 1701879640
1811
+ */
1812
+ expiration?: number;
1814
1813
  session_info: components["schemas"]["ClientSessionInfo"];
1815
1814
  /**
1816
1815
  * @description New token to be used for authentication. Requests to signing endpoints
@@ -1901,6 +1900,13 @@ export interface components {
1901
1900
  * ]
1902
1901
  */
1903
1902
  policy?: Record<string, never>[];
1903
+ /**
1904
+ * Format: int32
1905
+ * @description The organization's currently configured TOTP failure limit, i.e., the number
1906
+ * of times a user can provide an incorrect TOTP code before being rate limited.
1907
+ * This value can be between 1 and 5 (inclusive).
1908
+ */
1909
+ totp_failure_limit: number;
1904
1910
  /**
1905
1911
  * Format: int64
1906
1912
  * @description The organization's currently configured user-export delay, i.e., the minimum
@@ -2659,6 +2665,12 @@ export interface components {
2659
2665
  * ]
2660
2666
  */
2661
2667
  policy?: Record<string, never>[] | null;
2668
+ /**
2669
+ * Format: int32
2670
+ * @description If set, update this org's TOTP failure limit. After this many failures,
2671
+ * the user is rate limited until the next 30-second TOTP window.
2672
+ */
2673
+ totp_failure_limit?: number | null;
2662
2674
  /**
2663
2675
  * Format: int64
2664
2676
  * @description If set, update this org's user-export delay, i.e., the amount of time
@@ -2704,6 +2716,11 @@ export interface components {
2704
2716
  * ]
2705
2717
  */
2706
2718
  policy?: Record<string, never>[] | null;
2719
+ /**
2720
+ * Format: int32
2721
+ * @description The new value of the TOTP failure limit
2722
+ */
2723
+ totp_failure_limit?: number | null;
2707
2724
  /**
2708
2725
  * Format: int64
2709
2726
  * @description The new value of user-export delay
@@ -3183,6 +3200,12 @@ export interface components {
3183
3200
  NewSessionResponse: {
3184
3201
  content: {
3185
3202
  "application/json": {
3203
+ /**
3204
+ * Format: int64
3205
+ * @description Session expiration (in seconds since UNIX epoch), beyond which it cannot be refreshed.
3206
+ * @example 1701879640
3207
+ */
3208
+ expiration?: number;
3186
3209
  session_info: components["schemas"]["ClientSessionInfo"];
3187
3210
  /**
3188
3211
  * @description New token to be used for authentication. Requests to signing endpoints
@@ -3234,6 +3257,13 @@ export interface components {
3234
3257
  * ]
3235
3258
  */
3236
3259
  policy?: Record<string, never>[];
3260
+ /**
3261
+ * Format: int32
3262
+ * @description The organization's currently configured TOTP failure limit, i.e., the number
3263
+ * of times a user can provide an incorrect TOTP code before being rate limited.
3264
+ * This value can be between 1 and 5 (inclusive).
3265
+ */
3266
+ totp_failure_limit: number;
3237
3267
  /**
3238
3268
  * Format: int64
3239
3269
  * @description The organization's currently configured user-export delay, i.e., the minimum
@@ -3517,6 +3547,11 @@ export interface components {
3517
3547
  * ]
3518
3548
  */
3519
3549
  policy?: Record<string, never>[] | null;
3550
+ /**
3551
+ * Format: int32
3552
+ * @description The new value of the TOTP failure limit
3553
+ */
3554
+ totp_failure_limit?: number | null;
3520
3555
  /**
3521
3556
  * Format: int64
3522
3557
  * @description The new value of user-export delay
@@ -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 SessionManager.hasExpired(new Date(session.expiration));
73
+ return SessionManager.isStale(new Date(session.expiration));
74
74
  }
75
75
 
76
76
  /**
@@ -2,6 +2,7 @@ import { Events } from "../events";
2
2
  import { EnvInterface } from "../env";
3
3
  import { Client, createHttpClient } from "../api";
4
4
  import { SessionStorage } from "./session_storage";
5
+ import { delay } from "../util";
5
6
 
6
7
  const DEFAULT_EXPIRATION_BUFFER_SECS = 30;
7
8
 
@@ -10,6 +11,7 @@ export abstract class SessionManager<U> {
10
11
  readonly env: EnvInterface;
11
12
  readonly storage: SessionStorage<U>;
12
13
  readonly events = new Events();
14
+ #refreshing: boolean = false;
13
15
 
14
16
  /**
15
17
  * @return {string} The current auth token.
@@ -40,9 +42,24 @@ export abstract class SessionManager<U> {
40
42
  */
41
43
  async refreshIfNeeded(): Promise<boolean> {
42
44
  if (await this.isStale()) {
43
- await this.refresh();
44
- return true;
45
+ if (this.#refreshing) {
46
+ // wait until done refreshing
47
+ while (this.#refreshing) {
48
+ await delay(100);
49
+ }
50
+ return false;
51
+ } else {
52
+ // refresh
53
+ this.#refreshing = true;
54
+ try {
55
+ await this.refresh();
56
+ return true;
57
+ } finally {
58
+ this.#refreshing = false;
59
+ }
60
+ }
45
61
  }
62
+
46
63
  return false;
47
64
  }
48
65
 
@@ -88,19 +105,29 @@ export abstract class SessionManager<U> {
88
105
  }
89
106
 
90
107
  /**
91
- * Check if a timestamp has expired.
108
+ * Check if a timestamp is within {@link bufferSeconds} seconds from expiration.
92
109
  * @param {Date} exp The timestamp to check
93
- * @param {number} bufferSeconds Time buffer in seconds (defaults to 30s)
110
+ * @param {number} bufferSeconds Time buffer in seconds (defaults to 0s)
94
111
  * @return {boolean} True if the timestamp has expired
95
112
  */
96
113
  protected static hasExpired(exp: Date, bufferSeconds?: number): boolean {
97
- bufferSeconds ??= DEFAULT_EXPIRATION_BUFFER_SECS;
114
+ bufferSeconds ??= 0;
98
115
  const expMsSinceEpoch = exp.getTime();
99
116
  const nowMsSinceEpoch = new Date().getTime();
100
117
  const bufferMs = bufferSeconds * 1000;
101
118
  return expMsSinceEpoch < nowMsSinceEpoch + bufferMs;
102
119
  }
103
120
 
121
+ /**
122
+ * Check if a timestamp is stale, i.e., it's within {@link bufferSeconds} seconds from expiration.
123
+ * @param {Date} exp The timestamp to check
124
+ * @param {number} bufferSeconds Time buffer in seconds (defaults to 30s)
125
+ * @return {boolean} True if the timestamp is stale
126
+ */
127
+ protected static isStale(exp: Date, bufferSeconds?: number): boolean {
128
+ return this.hasExpired(exp, bufferSeconds ?? DEFAULT_EXPIRATION_BUFFER_SECS);
129
+ }
130
+
104
131
  /**
105
132
  * Throws an error that says that some feature is unsupported.
106
133
  * @param {string} name The name of the feature that is not supported
@@ -21,6 +21,8 @@ export interface SignerSessionObject {
21
21
  token: string;
22
22
  /** Session info */
23
23
  session_info: ClientSessionInfo;
24
+ /** Session expiration (in seconds since UNIX epoch) beyond which it cannot be refreshed */
25
+ session_exp: number | undefined; // may be missing in legacy session files
24
26
  }
25
27
 
26
28
  /**
@@ -51,7 +53,7 @@ export interface SignerSessionLifetime {
51
53
  /** Manager for signer sessions. */
52
54
  export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
53
55
  readonly #eventEmitter: EventEmitter;
54
- #client: { client: Client; exp: Date };
56
+ #client: { client: Client; token_exp: Date; session_exp?: Date };
55
57
 
56
58
  /**
57
59
  * @return {string} The current auth token.
@@ -70,8 +72,12 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
70
72
  async client(): Promise<Client> {
71
73
  await this.refreshIfNeeded();
72
74
 
73
- // trigger "session expired" if for whatever reason the token is still stale
74
- if (SessionManager.hasExpired(this.#client.exp, /* buffer */ 0)) {
75
+ // trigger "session expired" if the session as a whole has expired
76
+ // or if (for whatever reason) the token is still stale
77
+ if (
78
+ SessionManager.hasExpired(this.#client.token_exp) ||
79
+ (this.#client.session_exp && SessionManager.hasExpired(this.#client.session_exp))
80
+ ) {
75
81
  await this.#eventEmitter.emitSessionExpired();
76
82
  }
77
83
 
@@ -92,7 +98,7 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
92
98
  * @internal
93
99
  */
94
100
  async isStale(): Promise<boolean> {
95
- return SessionManager.hasExpired(this.#client.exp);
101
+ return SessionManager.isStale(this.#client.token_exp);
96
102
  }
97
103
 
98
104
  /**
@@ -120,7 +126,10 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
120
126
  await this.storage.save(newSession);
121
127
  this.#client = {
122
128
  client: this.createClient(newSession.token),
123
- exp: secondsSinceEpochToDate(newSession.session_info.auth_token_exp),
129
+ token_exp: secondsSinceEpochToDate(newSession.session_info.auth_token_exp),
130
+ session_exp: newSession.session_exp
131
+ ? secondsSinceEpochToDate(newSession.session_exp)
132
+ : undefined,
124
133
  };
125
134
  }
126
135
 
@@ -145,6 +154,7 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
145
154
  token: session.token,
146
155
  purpose: "sign via oidc",
147
156
  session_info: session.session_info,
157
+ session_exp: session.expiration!,
148
158
  };
149
159
  storage ??= new MemorySessionStorage();
150
160
  await storage.save(sessionData);
@@ -187,7 +197,10 @@ export class SignerSessionManager extends OrgSessionManager<SignerSessionData> {
187
197
  this.#eventEmitter = new EventEmitter([this.events]);
188
198
  this.#client = {
189
199
  client: this.createClient(sessionData.token),
190
- exp: secondsSinceEpochToDate(sessionData.session_info.auth_token_exp),
200
+ token_exp: secondsSinceEpochToDate(sessionData.session_info.auth_token_exp),
201
+ session_exp: sessionData.session_exp
202
+ ? secondsSinceEpochToDate(sessionData.session_exp)
203
+ : undefined,
191
204
  };
192
205
  }
193
206
  }
package/src/util.ts CHANGED
@@ -71,3 +71,13 @@ export function encodeToBase64Url(buffer: Iterable<number>): string {
71
71
  // NOTE: there is no "base64url" encoding in the "buffer" module for the browser (unlike in node.js)
72
72
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, "");
73
73
  }
74
+
75
+ /**
76
+ * Sleeps for `ms` milliseconds.
77
+ *
78
+ * @param {number} ms Milliseconds to sleep
79
+ * @return {Promise<void>} A promise that is resolved after `ms` milliseconds.
80
+ */
81
+ export function delay(ms: number): Promise<void> {
82
+ return new Promise((resolve) => setTimeout(resolve, ms));
83
+ }