@vvedantb/native-ui 0.11.0 → 0.11.1
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/package.json +1 -1
- package/src/auth/SignInScreen.tsx +36 -7
- package/src/auth/authErrors.ts +49 -1
- package/src/auth/index.ts +10 -1
- package/src/auth/totp.ts +77 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vvedantb/native-ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Shared React Native / Expo UI primitives. Floating surfaces, forms, overlays, toasts, settings chrome, tab bar, AI chat parts, the family app marks, a NativeWind preset, the Expo web root and Clerk + Convex auth screens.",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,13 @@ import { PasswordInput } from "../PasswordInput";
|
|
|
9
9
|
import { toast } from "../toastStore";
|
|
10
10
|
import { useScheme } from "../useScheme";
|
|
11
11
|
import { AuthScreen, FieldError, OrDivider } from "./AuthScreen";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
authErrorMessage,
|
|
14
|
+
DEFAULT_STAGE_TIMEOUT_MS,
|
|
15
|
+
isAuthTimeoutError,
|
|
16
|
+
logAuthFailure,
|
|
17
|
+
withAuthTimeout,
|
|
18
|
+
} from "./authErrors";
|
|
13
19
|
import { SecondFactorScreen } from "./SecondFactorScreen";
|
|
14
20
|
import {
|
|
15
21
|
pickFirstFactorStrategy,
|
|
@@ -139,6 +145,12 @@ export type SignInScreenProps = AuthNavigationOptions & {
|
|
|
139
145
|
* `console.warn`. Ignored in `sign-up`.
|
|
140
146
|
*/
|
|
141
147
|
onIncomplete?: (signIn: IncompleteSignIn) => void;
|
|
148
|
+
/**
|
|
149
|
+
* Time for each Clerk call (create, prepare, attempt, reset password,
|
|
150
|
+
* set active), in milliseconds. A call that takes longer fails with code
|
|
151
|
+
* `auth_timeout`. Default 20000.
|
|
152
|
+
*/
|
|
153
|
+
stageTimeoutMs?: number;
|
|
142
154
|
};
|
|
143
155
|
|
|
144
156
|
/**
|
|
@@ -167,6 +179,7 @@ export function SignInScreen({
|
|
|
167
179
|
header,
|
|
168
180
|
footer,
|
|
169
181
|
onIncomplete,
|
|
182
|
+
stageTimeoutMs = DEFAULT_STAGE_TIMEOUT_MS,
|
|
170
183
|
}: SignInScreenProps) {
|
|
171
184
|
const signInState = useSignIn();
|
|
172
185
|
const signUpState = useSignUp();
|
|
@@ -187,7 +200,7 @@ export function SignInScreen({
|
|
|
187
200
|
const [pendingNewPassword, setPendingNewPassword] = useState(false);
|
|
188
201
|
const [newPassword, setNewPassword] = useState("");
|
|
189
202
|
const [confirmPassword, setConfirmPassword] = useState("");
|
|
190
|
-
const secondFactor = useSecondFactor();
|
|
203
|
+
const secondFactor = useSecondFactor({ stageTimeoutMs });
|
|
191
204
|
const [error, setError] = useState("");
|
|
192
205
|
const [submitting, setSubmitting] = useState(false);
|
|
193
206
|
const signUp = mode === "sign-up";
|
|
@@ -249,6 +262,7 @@ export function SignInScreen({
|
|
|
249
262
|
signIn,
|
|
250
263
|
strategy,
|
|
251
264
|
factor: list.find((factor) => factor.strategy === strategy),
|
|
265
|
+
timeoutMs: stageTimeoutMs,
|
|
252
266
|
});
|
|
253
267
|
if (!sent.ok) setFirstError(sent.error);
|
|
254
268
|
setFirstLoading(false);
|
|
@@ -268,7 +282,7 @@ export function SignInScreen({
|
|
|
268
282
|
// Clerk's `SignInStatus` omits `needs_client_trust`: widen before comparing.
|
|
269
283
|
const status: string | null = attempt.status;
|
|
270
284
|
if (status === "complete" && attempt.createdSessionId && setActive) {
|
|
271
|
-
await setActive({ session: attempt.createdSessionId });
|
|
285
|
+
await withAuthTimeout(setActive({ session: attempt.createdSessionId }), "set-active", stageTimeoutMs);
|
|
272
286
|
done();
|
|
273
287
|
} else if (status === "needs_new_password") {
|
|
274
288
|
setPendingFirstFactor(false);
|
|
@@ -288,7 +302,12 @@ export function SignInScreen({
|
|
|
288
302
|
if (!isLoaded || !signIn || firstLoading) return;
|
|
289
303
|
setFirstLoading(true);
|
|
290
304
|
setFirstError("");
|
|
291
|
-
const result = await submitFirstFactor({
|
|
305
|
+
const result = await submitFirstFactor({
|
|
306
|
+
signIn,
|
|
307
|
+
code: firstCode,
|
|
308
|
+
strategy: firstStrategy,
|
|
309
|
+
timeoutMs: stageTimeoutMs,
|
|
310
|
+
});
|
|
292
311
|
if (!result.ok) {
|
|
293
312
|
setFirstError(result.error);
|
|
294
313
|
setFirstLoading(false);
|
|
@@ -297,6 +316,7 @@ export function SignInScreen({
|
|
|
297
316
|
try {
|
|
298
317
|
await routeAttempt(result.attempt, false);
|
|
299
318
|
} catch (caught) {
|
|
319
|
+
if (isAuthTimeoutError(caught)) logAuthFailure("set-active", caught);
|
|
300
320
|
setFirstError(authErrorMessage(caught, AUTH_MESSAGES.verifyFailed));
|
|
301
321
|
} finally {
|
|
302
322
|
setFirstLoading(false);
|
|
@@ -316,9 +336,13 @@ export function SignInScreen({
|
|
|
316
336
|
setSubmitting(true);
|
|
317
337
|
setError("");
|
|
318
338
|
try {
|
|
319
|
-
const result = await
|
|
339
|
+
const result = await withAuthTimeout(
|
|
340
|
+
signIn.resetPassword({ password: newPassword }),
|
|
341
|
+
"reset-password",
|
|
342
|
+
stageTimeoutMs,
|
|
343
|
+
);
|
|
320
344
|
if (result.status === "complete" && result.createdSessionId) {
|
|
321
|
-
await setActive({ session: result.createdSessionId });
|
|
345
|
+
await withAuthTimeout(setActive({ session: result.createdSessionId }), "set-active", stageTimeoutMs);
|
|
322
346
|
done();
|
|
323
347
|
} else if (onIncomplete) {
|
|
324
348
|
onIncomplete(result);
|
|
@@ -327,6 +351,7 @@ export function SignInScreen({
|
|
|
327
351
|
fail(AUTH_MESSAGES.resetPasswordFailed, AUTH_MESSAGES.needsAnotherStep);
|
|
328
352
|
}
|
|
329
353
|
} catch (caught) {
|
|
354
|
+
if (isAuthTimeoutError(caught)) logAuthFailure("reset-password", caught);
|
|
330
355
|
fail(AUTH_MESSAGES.resetPasswordFailed, authErrorMessage(caught, AUTH_MESSAGES.resetPasswordFailed));
|
|
331
356
|
} finally {
|
|
332
357
|
setSubmitting(false);
|
|
@@ -340,7 +365,11 @@ export function SignInScreen({
|
|
|
340
365
|
setSubmitting(true);
|
|
341
366
|
setError("");
|
|
342
367
|
try {
|
|
343
|
-
const attempt = await
|
|
368
|
+
const attempt = await withAuthTimeout(
|
|
369
|
+
signIn.create({ identifier: email, password }),
|
|
370
|
+
"create",
|
|
371
|
+
stageTimeoutMs,
|
|
372
|
+
);
|
|
344
373
|
await routeAttempt(attempt, true);
|
|
345
374
|
} catch (caught) {
|
|
346
375
|
logAuthFailure("create", caught);
|
package/src/auth/authErrors.ts
CHANGED
|
@@ -49,13 +49,61 @@ function firstError(parsed: ParsedAuthError | null) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/** The code of a timed-out auth stage. */
|
|
53
|
+
export const AUTH_TIMEOUT_CODE = "auth_timeout";
|
|
54
|
+
|
|
55
|
+
/** Default time for one Clerk call, in milliseconds. */
|
|
56
|
+
export const DEFAULT_STAGE_TIMEOUT_MS = 20000;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The error `withAuthTimeout` rejects with. It is Clerk-shaped (`errors[0].code`
|
|
60
|
+
* is `auth_timeout`), so `authErrorMessage` and `logAuthFailure` read it.
|
|
61
|
+
*/
|
|
62
|
+
export class AuthTimeoutError extends Error {
|
|
63
|
+
readonly code = AUTH_TIMEOUT_CODE;
|
|
64
|
+
readonly stage: string;
|
|
65
|
+
readonly errors: { code: string; message: string }[];
|
|
66
|
+
constructor(stage: string, ms: number) {
|
|
67
|
+
const message = `The ${stage} step timed out after ${Math.round(ms / 1000)} seconds. Try again.`;
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "AuthTimeoutError";
|
|
70
|
+
this.stage = stage;
|
|
71
|
+
this.errors = [{ code: AUTH_TIMEOUT_CODE, message }];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isAuthTimeoutError(error: unknown): error is AuthTimeoutError {
|
|
76
|
+
return error instanceof AuthTimeoutError;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Rejects with `AuthTimeoutError` (code `auth_timeout`, message names `stage`)
|
|
81
|
+
* when `promise` has not settled after `ms`. The timer is cleared on settle.
|
|
82
|
+
*/
|
|
83
|
+
export function withAuthTimeout<T>(promise: Promise<T>, stage: string, ms: number = DEFAULT_STAGE_TIMEOUT_MS): Promise<T> {
|
|
84
|
+
return new Promise<T>((resolve, reject) => {
|
|
85
|
+
const timer = setTimeout(() => reject(new AuthTimeoutError(stage, ms)), ms);
|
|
86
|
+
promise.then(
|
|
87
|
+
(value) => {
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
resolve(value);
|
|
90
|
+
},
|
|
91
|
+
(error) => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
reject(error);
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
52
99
|
/**
|
|
53
100
|
* Logs a failed auth call as one structured `console.error`. It holds the
|
|
54
101
|
* Clerk code, message, longMessage and meta.paramName, plus stage, errorName,
|
|
55
|
-
* status and clerkTraceId when present. Only the error object is read, so a
|
|
102
|
+
* status and clerkTraceId when present. For `AuthTimeoutError` the logged stage is the error's own stage. Only the error object is read, so a
|
|
56
103
|
* password or one-time code is never logged.
|
|
57
104
|
*/
|
|
58
105
|
export function logAuthFailure(stage: string, error: unknown): void {
|
|
106
|
+
if (isAuthTimeoutError(error)) stage = error.stage;
|
|
59
107
|
const parsed = parseAuthError(error);
|
|
60
108
|
const { code, message, longMessage, paramName } = firstError(parsed);
|
|
61
109
|
const errorName = parsed?.errorName ?? (error instanceof Error ? error.name : undefined);
|
package/src/auth/index.ts
CHANGED
|
@@ -6,7 +6,16 @@ export { AuthProviders } from "./AuthProviders";
|
|
|
6
6
|
export type { AuthProvidersProps } from "./AuthProviders";
|
|
7
7
|
export { AuthScreen, OrDivider, FieldError } from "./AuthScreen";
|
|
8
8
|
export type { AuthScreenProps } from "./AuthScreen";
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
authErrorMessage,
|
|
11
|
+
logAuthFailure,
|
|
12
|
+
withAuthTimeout,
|
|
13
|
+
AuthTimeoutError,
|
|
14
|
+
isAuthTimeoutError,
|
|
15
|
+
AUTH_TIMEOUT_CODE,
|
|
16
|
+
DEFAULT_STAGE_TIMEOUT_MS,
|
|
17
|
+
CREDENTIAL_ERROR_CODES,
|
|
18
|
+
} from "./authErrors";
|
|
10
19
|
export type { AuthErrorOptions } from "./authErrors";
|
|
11
20
|
export {
|
|
12
21
|
GoogleSignInButton,
|
package/src/auth/totp.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { useSignIn } from "@clerk/clerk-expo";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
authErrorMessage,
|
|
5
|
+
DEFAULT_STAGE_TIMEOUT_MS,
|
|
6
|
+
isAuthTimeoutError,
|
|
7
|
+
logAuthFailure,
|
|
8
|
+
withAuthTimeout,
|
|
9
|
+
} from "./authErrors";
|
|
10
|
+
|
|
11
|
+
/** Logs a timed-out stage. Other failures are not logged here. */
|
|
12
|
+
const logTimeout = (stage: string, error: unknown) => {
|
|
13
|
+
if (isAuthTimeoutError(error)) logAuthFailure(stage, error);
|
|
14
|
+
};
|
|
4
15
|
|
|
5
16
|
/** The messages the second-factor step can show. Generic: none names a provider. */
|
|
6
17
|
export const TOTP_MESSAGES = {
|
|
@@ -76,25 +87,37 @@ export async function prepareSecondFactorIfNeeded(args: {
|
|
|
76
87
|
signIn: Pick<SecondFactorSignIn, "supportedSecondFactors" | "prepareSecondFactor">;
|
|
77
88
|
strategy: SecondFactorStrategy;
|
|
78
89
|
factor?: SecondFactor;
|
|
90
|
+
/** Time for the Clerk call. Default 20000 ms. */
|
|
91
|
+
timeoutMs?: number;
|
|
79
92
|
}): Promise<TotpResult> {
|
|
80
93
|
const { signIn, strategy } = args;
|
|
94
|
+
const ms = args.timeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS;
|
|
81
95
|
if (strategy !== "phone_code" && strategy !== "email_code") return { ok: true };
|
|
82
96
|
if (!signIn.prepareSecondFactor) return { ok: false, error: TOTP_MESSAGES.sendFailed };
|
|
83
97
|
const factor = args.factor ?? signIn.supportedSecondFactors?.find((item) => item.strategy === strategy);
|
|
84
98
|
try {
|
|
85
99
|
if (strategy === "phone_code") {
|
|
86
|
-
await
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
100
|
+
await withAuthTimeout(
|
|
101
|
+
signIn.prepareSecondFactor({
|
|
102
|
+
strategy,
|
|
103
|
+
...(factor?.phoneNumberId ? { phoneNumberId: factor.phoneNumberId } : {}),
|
|
104
|
+
}),
|
|
105
|
+
"prepare-second-factor",
|
|
106
|
+
ms,
|
|
107
|
+
);
|
|
90
108
|
} else {
|
|
91
|
-
await
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
await withAuthTimeout(
|
|
110
|
+
signIn.prepareSecondFactor({
|
|
111
|
+
strategy,
|
|
112
|
+
...(factor?.emailAddressId ? { emailAddressId: factor.emailAddressId } : {}),
|
|
113
|
+
}),
|
|
114
|
+
"prepare-second-factor",
|
|
115
|
+
ms,
|
|
116
|
+
);
|
|
95
117
|
}
|
|
96
118
|
return { ok: true };
|
|
97
119
|
} catch (caught) {
|
|
120
|
+
logTimeout("prepare-second-factor", caught);
|
|
98
121
|
return { ok: false, error: authErrorMessage(caught, TOTP_MESSAGES.sendFailed) };
|
|
99
122
|
}
|
|
100
123
|
}
|
|
@@ -111,15 +134,23 @@ export async function submitSecondFactor(args: {
|
|
|
111
134
|
setActive: TotpSetActive;
|
|
112
135
|
code: string;
|
|
113
136
|
strategy: SecondFactorStrategy;
|
|
137
|
+
/** Time for each Clerk call. Default 20000 ms. */
|
|
138
|
+
timeoutMs?: number;
|
|
114
139
|
}): Promise<TotpResult> {
|
|
140
|
+
const ms = args.timeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS;
|
|
115
141
|
try {
|
|
116
|
-
const attempt = await
|
|
142
|
+
const attempt = await withAuthTimeout(
|
|
143
|
+
args.signIn.attemptSecondFactor({ strategy: args.strategy, code: args.code }),
|
|
144
|
+
"attempt-second-factor",
|
|
145
|
+
ms,
|
|
146
|
+
);
|
|
117
147
|
if (attempt.status === "complete" && attempt.createdSessionId) {
|
|
118
|
-
await args.setActive({ session: attempt.createdSessionId });
|
|
148
|
+
await withAuthTimeout(args.setActive({ session: attempt.createdSessionId }), "set-active", ms);
|
|
119
149
|
return { ok: true };
|
|
120
150
|
}
|
|
121
151
|
return { ok: false, error: TOTP_MESSAGES.needsAnotherStep };
|
|
122
152
|
} catch (caught) {
|
|
153
|
+
logTimeout("attempt-second-factor", caught);
|
|
123
154
|
const fallback = args.strategy === "totp" ? TOTP_MESSAGES.invalidCode : TOTP_MESSAGES.invalidCodeGeneric;
|
|
124
155
|
return { ok: false, error: authErrorMessage(caught, fallback) };
|
|
125
156
|
}
|
|
@@ -187,20 +218,32 @@ export async function prepareFirstFactorIfNeeded(args: {
|
|
|
187
218
|
signIn: Pick<FirstFactorSignIn, "supportedFirstFactors" | "prepareFirstFactor">;
|
|
188
219
|
strategy: FirstFactorStrategy;
|
|
189
220
|
factor?: FirstFactor;
|
|
221
|
+
/** Time for the Clerk call. Default 20000 ms. */
|
|
222
|
+
timeoutMs?: number;
|
|
190
223
|
}): Promise<TotpResult> {
|
|
191
224
|
const { signIn, strategy } = args;
|
|
225
|
+
const ms = args.timeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS;
|
|
192
226
|
if (!signIn.prepareFirstFactor) return { ok: false, error: TOTP_MESSAGES.sendFailed };
|
|
193
227
|
const factor = args.factor ?? signIn.supportedFirstFactors?.find((item) => item.strategy === strategy);
|
|
194
228
|
try {
|
|
195
229
|
if (strategy === "phone_code") {
|
|
196
230
|
if (!factor?.phoneNumberId) return { ok: false, error: TOTP_MESSAGES.sendFailed };
|
|
197
|
-
await
|
|
231
|
+
await withAuthTimeout(
|
|
232
|
+
signIn.prepareFirstFactor({ strategy, phoneNumberId: factor.phoneNumberId }),
|
|
233
|
+
"prepare-first-factor",
|
|
234
|
+
ms,
|
|
235
|
+
);
|
|
198
236
|
} else {
|
|
199
237
|
if (!factor?.emailAddressId) return { ok: false, error: TOTP_MESSAGES.sendFailed };
|
|
200
|
-
await
|
|
238
|
+
await withAuthTimeout(
|
|
239
|
+
signIn.prepareFirstFactor({ strategy, emailAddressId: factor.emailAddressId }),
|
|
240
|
+
"prepare-first-factor",
|
|
241
|
+
ms,
|
|
242
|
+
);
|
|
201
243
|
}
|
|
202
244
|
return { ok: true };
|
|
203
245
|
} catch (caught) {
|
|
246
|
+
logTimeout("prepare-first-factor", caught);
|
|
204
247
|
return { ok: false, error: authErrorMessage(caught, TOTP_MESSAGES.sendFailed) };
|
|
205
248
|
}
|
|
206
249
|
}
|
|
@@ -214,11 +257,18 @@ export async function submitFirstFactor(args: {
|
|
|
214
257
|
signIn: Pick<FirstFactorSignIn, "attemptFirstFactor">;
|
|
215
258
|
code: string;
|
|
216
259
|
strategy: FirstFactorStrategy;
|
|
260
|
+
/** Time for the Clerk call. Default 20000 ms. */
|
|
261
|
+
timeoutMs?: number;
|
|
217
262
|
}): Promise<FirstFactorResult> {
|
|
218
263
|
try {
|
|
219
|
-
const attempt = await
|
|
264
|
+
const attempt = await withAuthTimeout(
|
|
265
|
+
args.signIn.attemptFirstFactor({ strategy: args.strategy, code: args.code }),
|
|
266
|
+
"attempt-first-factor",
|
|
267
|
+
args.timeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS,
|
|
268
|
+
);
|
|
220
269
|
return { ok: true, attempt };
|
|
221
270
|
} catch (caught) {
|
|
271
|
+
logTimeout("attempt-first-factor", caught);
|
|
222
272
|
return { ok: false, error: authErrorMessage(caught, TOTP_MESSAGES.invalidCodeGeneric) };
|
|
223
273
|
}
|
|
224
274
|
}
|
|
@@ -228,13 +278,22 @@ export function submitTotpSecondFactor(args: {
|
|
|
228
278
|
signIn: TotpSignIn;
|
|
229
279
|
setActive: TotpSetActive;
|
|
230
280
|
code: string;
|
|
281
|
+
timeoutMs?: number;
|
|
231
282
|
}): Promise<TotpResult> {
|
|
232
|
-
return submitSecondFactor({
|
|
283
|
+
return submitSecondFactor({
|
|
284
|
+
signIn: args.signIn,
|
|
285
|
+
setActive: args.setActive,
|
|
286
|
+
code: args.code,
|
|
287
|
+
strategy: "totp",
|
|
288
|
+
timeoutMs: args.timeoutMs,
|
|
289
|
+
});
|
|
233
290
|
}
|
|
234
291
|
|
|
235
292
|
export type UseSecondFactorOptions = {
|
|
236
293
|
/** The strategy `submit` uses until `setStrategy` changes it. Default `"totp"`. */
|
|
237
294
|
strategy?: SecondFactorStrategy;
|
|
295
|
+
/** Time for each Clerk call, in milliseconds. Default 20000. */
|
|
296
|
+
stageTimeoutMs?: number;
|
|
238
297
|
};
|
|
239
298
|
|
|
240
299
|
export type UseSecondFactorResult = {
|
|
@@ -265,6 +324,7 @@ export function useSecondFactor(options: UseSecondFactorOptions = {}): UseSecond
|
|
|
265
324
|
const strategy = chosen ?? options.strategy ?? "totp";
|
|
266
325
|
const [loading, setLoading] = useState(false);
|
|
267
326
|
const [error, setError] = useState("");
|
|
327
|
+
const timeoutMs = options.stageTimeoutMs ?? DEFAULT_STAGE_TIMEOUT_MS;
|
|
268
328
|
|
|
269
329
|
const submit = async (code: string): Promise<TotpResult> => {
|
|
270
330
|
if (loading) return { ok: false, error };
|
|
@@ -274,7 +334,7 @@ export function useSecondFactor(options: UseSecondFactorOptions = {}): UseSecond
|
|
|
274
334
|
}
|
|
275
335
|
setLoading(true);
|
|
276
336
|
setError("");
|
|
277
|
-
const result = await submitSecondFactor({ signIn, setActive, code, strategy });
|
|
337
|
+
const result = await submitSecondFactor({ signIn, setActive, code, strategy, timeoutMs });
|
|
278
338
|
if (!result.ok) setError(result.error);
|
|
279
339
|
setLoading(false);
|
|
280
340
|
return result;
|
|
@@ -290,7 +350,7 @@ export function useSecondFactor(options: UseSecondFactorOptions = {}): UseSecond
|
|
|
290
350
|
}
|
|
291
351
|
setLoading(true);
|
|
292
352
|
setError("");
|
|
293
|
-
const result = await prepareSecondFactorIfNeeded({ signIn, strategy: prepareStrategy, factor });
|
|
353
|
+
const result = await prepareSecondFactorIfNeeded({ signIn, strategy: prepareStrategy, factor, timeoutMs });
|
|
294
354
|
if (!result.ok) setError(result.error);
|
|
295
355
|
setLoading(false);
|
|
296
356
|
return result;
|