@getpara/user-management-client 3.3.0 → 3.5.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.
@@ -99,34 +99,52 @@ const handleResponseSuccess = (response) => {
99
99
  }
100
100
  throw new import_error.ParaApiError("Invalid status code");
101
101
  };
102
+ const UNKNOWN_ERROR_MESSAGE = "Unknown error";
103
+ const hasUsableString = (value) => typeof value === "string" && value.trim().length > 0;
104
+ const getFallbackErrorMessage = (error) => {
105
+ if (hasUsableString(error == null ? void 0 : error.message)) return error.message;
106
+ if (hasUsableString(error == null ? void 0 : error.code)) return error.code;
107
+ return UNKNOWN_ERROR_MESSAGE;
108
+ };
109
+ const throwParaApiError = (message, code, status, responseURL, data, cause) => {
110
+ const paraApiError = new import_error.ParaApiError(message, code, status, responseURL, data);
111
+ if (cause !== void 0) paraApiError.cause = cause;
112
+ throw paraApiError;
113
+ };
102
114
  const handleResponseError = (error) => {
103
- var _a, _b, _c;
115
+ var _a, _c, _d;
104
116
  if (error === null) throw new import_error.ParaApiError("Error is null");
105
117
  if (import_axios.default.isAxiosError(error)) {
106
118
  const data = (_a = error.response) == null ? void 0 : _a.data;
107
- let message = typeof data === "string" ? data : "Unknown error";
119
+ let message = hasUsableString(data) ? data : getFallbackErrorMessage(error);
108
120
  let serverCode;
109
121
  let serverData;
110
122
  if (data && typeof data === "object") {
111
123
  const body = data;
112
- if (typeof body.message === "string") message = body.message;
124
+ if (hasUsableString(body.message)) message = body.message;
113
125
  serverCode = body.code;
114
- serverData = body.data;
126
+ if (body.data && typeof body.data === "object") {
127
+ serverData = body.data;
128
+ } else {
129
+ const _b = body, { message: _message, code: _code } = _b, rest = __objRest(_b, ["message", "code"]);
130
+ serverData = Object.keys(rest).length > 0 ? rest : void 0;
131
+ }
115
132
  }
116
133
  if (error.code === "ERR_NETWORK") {
117
134
  message = "Connection error";
118
135
  } else if (error.code === "ERR_CANCELED") {
119
136
  message = "Connection canceled";
120
137
  }
121
- throw new import_error.ParaApiError(
138
+ throwParaApiError(
122
139
  message,
123
140
  serverCode != null ? serverCode : error.code,
124
- (_b = error.response) == null ? void 0 : _b.status,
125
- (_c = error.request) == null ? void 0 : _c.responseURL,
126
- serverData
141
+ (_c = error.response) == null ? void 0 : _c.status,
142
+ (_d = error.request) == null ? void 0 : _d.responseURL,
143
+ serverData,
144
+ error
127
145
  );
128
146
  }
129
- throw new import_error.ParaApiError("Unknown error");
147
+ throwParaApiError(getFallbackErrorMessage(error), void 0, void 0, void 0, void 0, error);
130
148
  };
131
149
  const isRetryableError = (error) => {
132
150
  var _a, _b, _c, _d;
@@ -143,7 +161,9 @@ class Client {
143
161
  retrieveSessionCookie,
144
162
  persistSessionCookie,
145
163
  partnerConfigOverride,
146
- staticTraceContext
164
+ staticTraceContext,
165
+ httpAgent,
166
+ httpsAgent
147
167
  }) {
148
168
  this.inFlightFaucetRequests = /* @__PURE__ */ new Map();
149
169
  this.signUpOrLogIn = (body) => __async(this, null, function* () {
@@ -176,6 +196,33 @@ class Client {
176
196
  const res = yield this.baseRequest.post("/users/verify-oauth");
177
197
  return res.data;
178
198
  });
199
+ // POST /internal/v1/auth/2fa/enroll
200
+ // Login-time 2FA enrollment (ENG-6906). Authenticated by the active session.
201
+ // Returns the otpauth URI to render as a QR plus the one-time backup codes.
202
+ this.enrollMfa = () => __async(this, null, function* () {
203
+ const res = yield this.baseRequest.post("/internal/v1/auth/2fa/enroll");
204
+ return res.data;
205
+ });
206
+ // POST /internal/v1/auth/2fa/verify
207
+ // Login-time 2FA verification (ENG-6906). On success the backend stamps the
208
+ // session so the keyshare/sign gate passes; callers should then re-poll the login
209
+ // status (verify-oauth) to advance to `stage:'done'`. A wrong code is a 401 with
210
+ // `code:'UNAUTHORIZED'` carrying the remaining attempt budget — we surface that as
211
+ // a typed `{ ok:false, attemptsRemaining }` rather than throwing, so the UI can
212
+ // re-prompt. Other failures (e.g. NOT_ENROLLED 400, network) still throw.
213
+ this.verifyMfa = (code) => __async(this, null, function* () {
214
+ try {
215
+ yield this.baseRequest.post("/internal/v1/auth/2fa/verify", { code });
216
+ return { ok: true };
217
+ } catch (error) {
218
+ const apiError = error;
219
+ if ((apiError == null ? void 0 : apiError.isParaApiError) && (apiError.code === "UNAUTHORIZED" || apiError.status === 401)) {
220
+ const data = apiError.data;
221
+ return { ok: false, attemptsRemaining: data == null ? void 0 : data.attemptsRemaining };
222
+ }
223
+ throw error;
224
+ }
225
+ });
179
226
  this.loginExternalWallet = (_0) => __async(this, [_0], function* ({
180
227
  externalWallet,
181
228
  shouldTrackUser,
@@ -636,12 +683,12 @@ class Client {
636
683
  // plain-text bodies for backward compat with older SDKs.
637
684
  [import_shared.STRUCTURED_ERRORS_HEADER]: "1"
638
685
  });
639
- const axiosConfig = {
686
+ const axiosConfig = __spreadValues(__spreadValues({
640
687
  baseURL: userManagementHost,
641
688
  withCredentials: true,
642
689
  timeout: 6e4,
643
690
  headers
644
- };
691
+ }, httpAgent ? { httpAgent } : {}), httpsAgent ? { httpsAgent } : {});
645
692
  if (retrieveSessionCookie) {
646
693
  const defaultTransformRequest = Array.isArray(import_axios.default.defaults.transformRequest) ? import_axios.default.defaults.transformRequest : [import_axios.default.defaults.transformRequest];
647
694
  axiosConfig.transformRequest = [
@@ -30,34 +30,52 @@ const handleResponseSuccess = (response) => {
30
30
  }
31
31
  throw new ParaApiError("Invalid status code");
32
32
  };
33
+ const UNKNOWN_ERROR_MESSAGE = "Unknown error";
34
+ const hasUsableString = (value) => typeof value === "string" && value.trim().length > 0;
35
+ const getFallbackErrorMessage = (error) => {
36
+ if (hasUsableString(error == null ? void 0 : error.message)) return error.message;
37
+ if (hasUsableString(error == null ? void 0 : error.code)) return error.code;
38
+ return UNKNOWN_ERROR_MESSAGE;
39
+ };
40
+ const throwParaApiError = (message, code, status, responseURL, data, cause) => {
41
+ const paraApiError = new ParaApiError(message, code, status, responseURL, data);
42
+ if (cause !== void 0) paraApiError.cause = cause;
43
+ throw paraApiError;
44
+ };
33
45
  const handleResponseError = (error) => {
34
- var _a, _b, _c;
46
+ var _a, _c, _d;
35
47
  if (error === null) throw new ParaApiError("Error is null");
36
48
  if (axios.isAxiosError(error)) {
37
49
  const data = (_a = error.response) == null ? void 0 : _a.data;
38
- let message = typeof data === "string" ? data : "Unknown error";
50
+ let message = hasUsableString(data) ? data : getFallbackErrorMessage(error);
39
51
  let serverCode;
40
52
  let serverData;
41
53
  if (data && typeof data === "object") {
42
54
  const body = data;
43
- if (typeof body.message === "string") message = body.message;
55
+ if (hasUsableString(body.message)) message = body.message;
44
56
  serverCode = body.code;
45
- serverData = body.data;
57
+ if (body.data && typeof body.data === "object") {
58
+ serverData = body.data;
59
+ } else {
60
+ const _b = body, { message: _message, code: _code } = _b, rest = __objRest(_b, ["message", "code"]);
61
+ serverData = Object.keys(rest).length > 0 ? rest : void 0;
62
+ }
46
63
  }
47
64
  if (error.code === "ERR_NETWORK") {
48
65
  message = "Connection error";
49
66
  } else if (error.code === "ERR_CANCELED") {
50
67
  message = "Connection canceled";
51
68
  }
52
- throw new ParaApiError(
69
+ throwParaApiError(
53
70
  message,
54
71
  serverCode != null ? serverCode : error.code,
55
- (_b = error.response) == null ? void 0 : _b.status,
56
- (_c = error.request) == null ? void 0 : _c.responseURL,
57
- serverData
72
+ (_c = error.response) == null ? void 0 : _c.status,
73
+ (_d = error.request) == null ? void 0 : _d.responseURL,
74
+ serverData,
75
+ error
58
76
  );
59
77
  }
60
- throw new ParaApiError("Unknown error");
78
+ throwParaApiError(getFallbackErrorMessage(error), void 0, void 0, void 0, void 0, error);
61
79
  };
62
80
  const isRetryableError = (error) => {
63
81
  var _a, _b, _c, _d;
@@ -74,7 +92,9 @@ class Client {
74
92
  retrieveSessionCookie,
75
93
  persistSessionCookie,
76
94
  partnerConfigOverride,
77
- staticTraceContext
95
+ staticTraceContext,
96
+ httpAgent,
97
+ httpsAgent
78
98
  }) {
79
99
  this.inFlightFaucetRequests = /* @__PURE__ */ new Map();
80
100
  this.signUpOrLogIn = (body) => __async(this, null, function* () {
@@ -107,6 +127,33 @@ class Client {
107
127
  const res = yield this.baseRequest.post("/users/verify-oauth");
108
128
  return res.data;
109
129
  });
130
+ // POST /internal/v1/auth/2fa/enroll
131
+ // Login-time 2FA enrollment (ENG-6906). Authenticated by the active session.
132
+ // Returns the otpauth URI to render as a QR plus the one-time backup codes.
133
+ this.enrollMfa = () => __async(this, null, function* () {
134
+ const res = yield this.baseRequest.post("/internal/v1/auth/2fa/enroll");
135
+ return res.data;
136
+ });
137
+ // POST /internal/v1/auth/2fa/verify
138
+ // Login-time 2FA verification (ENG-6906). On success the backend stamps the
139
+ // session so the keyshare/sign gate passes; callers should then re-poll the login
140
+ // status (verify-oauth) to advance to `stage:'done'`. A wrong code is a 401 with
141
+ // `code:'UNAUTHORIZED'` carrying the remaining attempt budget — we surface that as
142
+ // a typed `{ ok:false, attemptsRemaining }` rather than throwing, so the UI can
143
+ // re-prompt. Other failures (e.g. NOT_ENROLLED 400, network) still throw.
144
+ this.verifyMfa = (code) => __async(this, null, function* () {
145
+ try {
146
+ yield this.baseRequest.post("/internal/v1/auth/2fa/verify", { code });
147
+ return { ok: true };
148
+ } catch (error) {
149
+ const apiError = error;
150
+ if ((apiError == null ? void 0 : apiError.isParaApiError) && (apiError.code === "UNAUTHORIZED" || apiError.status === 401)) {
151
+ const data = apiError.data;
152
+ return { ok: false, attemptsRemaining: data == null ? void 0 : data.attemptsRemaining };
153
+ }
154
+ throw error;
155
+ }
156
+ });
110
157
  this.loginExternalWallet = (_0) => __async(this, [_0], function* ({
111
158
  externalWallet,
112
159
  shouldTrackUser,
@@ -567,12 +614,12 @@ class Client {
567
614
  // plain-text bodies for backward compat with older SDKs.
568
615
  [STRUCTURED_ERRORS_HEADER]: "1"
569
616
  });
570
- const axiosConfig = {
617
+ const axiosConfig = __spreadValues(__spreadValues({
571
618
  baseURL: userManagementHost,
572
619
  withCredentials: true,
573
620
  timeout: 6e4,
574
621
  headers
575
- };
622
+ }, httpAgent ? { httpAgent } : {}), httpsAgent ? { httpsAgent } : {});
576
623
  if (retrieveSessionCookie) {
577
624
  const defaultTransformRequest = Array.isArray(axios.defaults.transformRequest) ? axios.defaults.transformRequest : [axios.defaults.transformRequest];
578
625
  axiosConfig.transformRequest = [
@@ -24,6 +24,13 @@ type ClientConfig = {
24
24
  * interceptor that pulls from context.active().
25
25
  */
26
26
  staticTraceContext?: Record<string, string>;
27
+ /**
28
+ * Node-only keep-alive agents, set by the server SDK so server-to-server
29
+ * callers reuse connections instead of re-handshaking TCP+TLS per request.
30
+ * Left undefined in the browser, where the runtime pools sockets itself.
31
+ */
32
+ httpAgent?: unknown;
33
+ httpsAgent?: unknown;
27
34
  };
28
35
  interface createUserIdRes {
29
36
  protocolId: string;
@@ -156,6 +163,24 @@ interface EncryptedWalletPrivateKey {
156
163
  biometricPublicKey?: string;
157
164
  passwordId?: string;
158
165
  }
166
+ /**
167
+ * Login-time 2FA (MFA) enroll/verify response shapes (ENG-6906). The challenge
168
+ * payload itself (`MfaChallenge`) lives in @getpara/shared and flows through the
169
+ * login auth-state poll; these are the bodies of the two dedicated session-authed
170
+ * endpoints under /internal/v1/auth/2fa.
171
+ */
172
+ export type EnrollMfaResponse = {
173
+ /** otpauth:// URI for the authenticator app. */
174
+ uri: string;
175
+ /** One-time backup codes, shown to the user exactly once at enrollment. */
176
+ backupCodes: string[];
177
+ };
178
+ export type VerifyMfaResult = {
179
+ ok: true;
180
+ } | {
181
+ ok: false;
182
+ attemptsRemaining?: number;
183
+ };
159
184
  export type SDKType = 'WEB' | 'SERVER' | 'BRIDGE' | 'REACT_NATIVE';
160
185
  export type VerifyTelegramRes = {
161
186
  isValid: true;
@@ -168,12 +193,12 @@ export type VerifyTelegramRes = {
168
193
  isValid: false;
169
194
  };
170
195
  export declare const handleResponseSuccess: (response: AxiosResponse<any, any>) => AxiosResponse<any, any>;
171
- export declare const handleResponseError: (error: any) => never;
196
+ export declare const handleResponseError: (error: any) => void;
172
197
  export declare const isRetryableError: (error: AxiosError) => boolean;
173
198
  declare class Client {
174
199
  private baseRequest;
175
200
  private inFlightFaucetRequests;
176
- constructor({ userManagementHost, apiKey, partnerId, version, clientType, opts, retrieveSessionCookie, persistSessionCookie, partnerConfigOverride, staticTraceContext, }: ClientConfig);
201
+ constructor({ userManagementHost, apiKey, partnerId, version, clientType, opts, retrieveSessionCookie, persistSessionCookie, partnerConfigOverride, staticTraceContext, httpAgent, httpsAgent, }: ClientConfig);
177
202
  signUpOrLogIn: (body: VerifiedAuth & VerificationEmailProps) => Promise<SignUpOrLogInResponse>;
178
203
  /**
179
204
  * @deprecated
@@ -185,6 +210,8 @@ declare class Client {
185
210
  sessionLookupId?: string;
186
211
  }) => Promise<VerifyTelegramResponse>;
187
212
  verifyOAuth: () => Promise<VerifyThirdPartyAuth | null>;
213
+ enrollMfa: () => Promise<EnrollMfaResponse>;
214
+ verifyMfa: (code: string) => Promise<VerifyMfaResult>;
188
215
  loginExternalWallet: ({ externalWallet, shouldTrackUser, chainId, uri, }: {
189
216
  externalWallet: ExternalWalletInfo;
190
217
  shouldTrackUser?: boolean;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@getpara/user-management-client",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "dependencies": {
5
- "@getpara/shared": "^1.21.0",
5
+ "@getpara/shared": "^1.23.0",
6
6
  "@opentelemetry/api": "^1.9.1",
7
7
  "axios": "^1.8.4",
8
8
  "axios-retry": "^4.5.0",
@@ -22,7 +22,7 @@
22
22
  "dist",
23
23
  "package.json"
24
24
  ],
25
- "gitHead": "d9b54921d4187e2dd25f314b62ed99988e9b5b1c",
25
+ "gitHead": "2e70485f8f206ff50f58b339ebf0922fd259858f",
26
26
  "main": "dist/cjs/index.js",
27
27
  "module": "dist/esm/index.js",
28
28
  "scripts": {