Skip to content

Commit d8b7ef4

Browse files
fix(auth): a user with two-factor sign-in on is asked for their code after the password (#29338)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} --> _Requested by **Steedos** · [project thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbRSWiurS43dXzxySsivhDtb)_ Fixes #29105 Clause-②: yes (widening) Metadata-protocol: none ## 维护者速读 - **改了什么**:开启了双重验证(身份验证器 TOTP)的用户用密码登录后,Console 登录页会出现第二步:输入身份验证器里的 6 位验证码,或改用备用码;验证通过即登录。验证码错误会在原处提示并可重试;"返回登录"回到密码步骤。Console 自己的登录页和 `DefaultLoginPage` 都有这一步,十种语言包都有文案。 - **为什么改**:服务端早已支持两步登录(密码通过后返回 `twoFactorRedirect`,再由 `/two-factor/verify-totp` 或 `/verify-backup-code` 完成),但 Console 把这个回答丢掉了,登录页原样不动、没有输入框也没有任何提示,这类用户完全进不了 Console。 - **风险与回滚**:只改前端(`@object-ui/auth`、`app-shell`、`i18n`、`console`),服务端零改动;未开启双重验证的用户登录路径不变(有测试钉住)。`@object-ui/auth` 的公开类型新增了成员(拓宽)。回滚即 revert 本 PR。 - **先看**:`ui/packages/auth/src/AuthProvider.tsx`(`signIn` 收到挑战时不登录、保存挑战;`verifySignInTwoFactor` 完成登录,挑战被服务端作废时清掉);`ui/packages/auth/src/TwoFactorChallengeForm.tsx`(新的验证码步骤);`ui/packages/auth/src/createAuthClient.ts`(识别 `twoFactorRedirect`、丢弃那次回答里已被删掉的会话 token)。 - **两分钟验证**:`OS_AUTH_TWO_FACTOR=true pnpm dev:crm -- --fresh -p 38417`,给 admin 开启 TOTP 后在 Console `/login` 用密码登录:出现验证码步骤;输错显示"That code is not valid…",输对进入 `/home`。 - **需要你做的**:无,席位落地。 ## What changes A password sign-in for an account with two-factor sign-in on is answered by better-auth's `twoFactor()` plugin with `{ twoFactorRedirect: true, twoFactorMethods }`, no session, and a signed `two_factor` cookie that holds the pending sign-in for ten minutes. The Console ignored that answer. Now: - **`@object-ui/auth` client.** `AuthClient.signIn` answers `{ twoFactor: { methods } }` for that case (new exported `SignInResult`, `TwoFactorChallenge`, `TwoFactorMethod`). The same answer carries `set-auth-token` for the session the server just deleted (measured: it resolves to no session), which the bearer fetch had stored; the client clears it. New `AuthClient.verifySignInTwoFactor(code, 'totp' | 'backup-code')` posts to the sign-in lane of `/two-factor/verify-totp` or `/two-factor/verify-backup-code` and stores the session token it answers. - **Provider.** `useAuth()` gains `twoFactorChallenge`, `verifySignInTwoFactor` and `cancelTwoFactorChallenge`; `signIn` resolves `{ twoFactorRequired }`. The challenge clears on success, on "Back to sign in", on sign-out, and when the server reports it spent or expired (`TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE`, `INVALID_TWO_FACTOR_COOKIE`); a wrong code (`INVALID_CODE`, `INVALID_BACKUP_CODE`) keeps it. - **`LoginForm`.** While a challenge is held it draws the new internal `TwoFactorChallengeForm` in place of its form: the code field (`inputMode="numeric"`, `autocomplete="one-time-code"`, spaces stripped), a link to a backup code and back, "Back to sign in", and the error in place. `onSuccess` fires only once the code is verified. Because the challenge lives in the provider, the console's dev-admin chip leads to the same step (its spinner now stops there). Controls use the phone floor #29136 introduced (`AUTH_TOUCH_TARGET_CLASS`, 44 px, 16 px input text). - **Strings.** Ten `LoginFormLabels` members (`twoFactor*`), passed by both login pages from `auth.login.twoFactor*`, and five refusal codes in `signInRefusalMessages` from `auth.login.errors.twoFactor*`, in all ten packs. No server package is touched; no metadata key or option is added. ## Tests - New `ui/packages/auth/src/__tests__/twoFactorSignIn-29105.test.tsx` (real `AuthProvider` + `LoginForm` over a stub client): a TOTP user signs in with password then code (`onSuccess` only after the code); a wrong code shows the mapped error in place and a retry succeeds; a backup code works; "Back to sign in" returns to the password step; a dropped challenge returns there with the reason; no authenticator method offers backup codes only; a user without two-factor is signed straight in, unchanged. - New `ui/packages/auth/src/__tests__/twoFactorSignIn-client-29105.test.ts`: the client maps `twoFactorRedirect` to a challenge and stores no token, keeps the plain session path, posts each code kind to its endpoint, and keeps the server `code` on a refusal. - `createAuthClient.test.ts`: one assertion reads `result.user?.email` for the widened `signIn` answer. - Ablation, after committing: `LoginForm`'s challenge branch disabled reds 6 of the 7 LoginForm cases (the unchanged non-two-factor case stays green, as it should); the client's `twoFactorRedirect` check disabled reds 2 of 6 client cases. `ablation-replace.mjs` restored each file (empty `git diff HEAD`). ## Run record Live, on `pnpm dev:crm -- --fresh` with `OS_AUTH_TWO_FACTOR=true`, the seeded admin enrolled through `/two-factor/enable` with a TOTP computed from the served secret. | Step | Answer | |---|---| | password sign-in, enrolled | 200 `{"twoFactorRedirect":true,"twoFactorMethods":["totp"]}`, session cookies expired, `two_factor` cookie set | | verify-totp, wrong code | 401 `INVALID_CODE` | | verify-totp, right code | 200 `{ token, user }`, session cookie set, `two_factor` expired; `get-session` with the token answers the admin | | verify-backup-code / wrong | 200 `{ token, user }` / 401 `INVALID_BACKUP_CODE` | | verify-totp with no challenge cookie | 401 `INVALID_TWO_FACTOR_COOKIE` | Real browser (Playwright Chromium, the Console from `ui/apps/console` source through Vite over that backend): at 1280 px the password step leads to the code step with no token stored; a wrong code shows "That code is not valid. Check your authenticator app and try again." in place; the right code (typed as "123 456") lands on `/home`; "Back to sign in" returns to the password step; a backup code signs in. At 375 x 812 (`isMobile`): the field, Verify and both links are 44 px tall, the field text is 16 px, no horizontal scroll, and with the viewport shrunk by a 336 px keyboard the field (263 to 307) and Verify (323 to 367) sit inside the 476 px left. Screenshots are in the project folder `issue-29105/`, not on GitHub, as the card asks. ## Acceptance notes - `AuthClient` gains a required method, `verifySignInTwoFactor`, like every earlier addition to that interface: a third-party `AuthClient` implementation must add it to type-check. Declared `Clause-②: yes (widening)` as claimed. - The password step's own lockout code `ACCOUNT_LOCKED` has no entry in `signInRefusalMessages` (before this PR too), so it shows the server's English message; this PR adds only the two-factor step's codes. - After verification the session is `{ token }`, as phone sign-in already does: the verify endpoints answer `{ token, user }` with no session envelope, and the provider's session reads only the token. ## Verification Readings at head `606f6f61` (main `f2bf2938` merged, which carries #29136 and #29159). Main `5780fa49` was then merged cleanly (head `d35fdccc`); its only overlap is the ten locale packs, so on `d35fdccc` the auth and i18n suites plus the every-member label pin were re-run (139 files, 1821 tests passed) with the five PR-body and changeset gates (all exit 0). CI's check-runs carry every family CI runs; they are not re-run here. - `node scripts/pm/dispatch-gates.mjs --ran`: 34 derived, 34 run, every exit 0 (`check-changeset-no-major`, `check-adr-0087-registration` and `check-closing-keyword-parity` read this body). - `node scripts/check-sdui-manifest.mjs`: exit 0. - `pnpm --dir ui exec vitest run scripts/`: 169 files passed, 2 skipped. - `pnpm --dir ui check:new-line-citations`: 0 new citations. <details><summary>Suites and type-check</summary> - `pnpm --dir ui exec vitest run packages/auth/ packages/i18n/` plus the 33 console and app-shell test files that name the login pages or `signInRefusalMessages` (the every-member label pin among them): Test Files 171 passed, Tests 2013 passed, 13 skipped. - `turbo run type-check --filter=@object-ui/auth --filter=@object-ui/app-shell --filter=@object-ui/console --filter=@object-ui/i18n`: 74 tasks successful. - Dogfood battery: not owed, no `!` in the title and no `(narrowing)` arm. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QsR2jU7rCo15awmBUAqJ2C --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 46e2400 commit d8b7ef4

26 files changed

Lines changed: 1041 additions & 14 deletions
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/auth': patch
3+
'@object-ui/app-shell': patch
4+
'@object-ui/i18n': patch
5+
'@object-ui/console': patch
6+
---
7+
8+
fix(auth): a user with two-factor sign-in on is asked for their code after the password, instead of staying on the sign-in page
9+
10+
Clause-②: yes (widening)
11+
Metadata-protocol: none
12+
13+
A user who turned on two-factor sign-in (an authenticator app) and signed in with their password could not get into the Console: the server accepted the password and asked for the second factor, the Console dropped that answer, and the sign-in page stayed as it was, with no code field and no message.
14+
15+
The sign-in form now shows a second step: the 6-digit code from the authenticator app, or one of the backup codes saved at set-up. A wrong code says so in place and the user can try again; "Back to sign in" returns to the password step. When the server ends the attempt (too many wrong codes, or ten minutes passed), the password step comes back with the reason. Both login pages (the Console's and `DefaultLoginPage`) show the step, in every language pack.
16+
17+
Added to `@object-ui/auth`: `AuthClient.signIn` answers `{ twoFactor: { methods } }` for such an account (exported `SignInResult`, `TwoFactorChallenge`, `TwoFactorMethod`); `AuthClient.verifySignInTwoFactor(code, 'totp' | 'backup-code')` finishes it (`SignInTwoFactorCodeKind`); `useAuth()` gains `twoFactorChallenge`, `verifySignInTwoFactor` and `cancelTwoFactorChallenge`, and its `signIn` resolves `{ twoFactorRequired }`; `LoginFormLabels` gains ten `twoFactor*` labels. `@object-ui/i18n` gains the matching `auth.login.twoFactor*` and `auth.login.errors.twoFactor*` keys.
18+
19+
What to do: nothing, unless you pass your own `AuthClient` to `AuthProvider`. `verifySignInTwoFactor` is a required member of `AuthClient`, so such an implementation must add it to type-check (and its `signIn` may now answer `{ twoFactor }`).

‎ui/apps/console/src/pages/auth/LoginPage.tsx‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,13 @@ function DevAdminRow({ seed }: { seed: DevSeedAdmin }) {
415415
setError(null);
416416
setPending(true);
417417
try {
418-
await signIn(seed.email, seed.password);
418+
const { twoFactorRequired } = await signIn(seed.email, seed.password);
419+
// The seeded admin enrolled two-factor sign-in: the form beside this
420+
// row now asks for the code (objectstack#29105), so this chip is idle.
421+
if (twoFactorRequired) {
422+
setPending(false);
423+
return;
424+
}
419425
// Signed in: `user` is set and the page swaps to its post-login state.
420426
} catch (err) {
421427
const authError = err instanceof Error ? err : new Error(String(err));
@@ -595,6 +601,18 @@ function LoginFormCard({
595601
noSignInMethodText: t('auth.login.noSignInMethodText', {
596602
defaultValue: 'No sign-in method is available. Contact your administrator.',
597603
}),
604+
// The two-factor step a password sign-in leads to when the account
605+
// has two-factor sign-in on (objectstack#29105).
606+
twoFactorTitle: t('auth.login.twoFactorTitle', { defaultValue: 'Two-factor authentication' }),
607+
twoFactorDescription: t('auth.login.twoFactorDescription', { defaultValue: 'Enter the 6-digit code from your authenticator app.' }),
608+
twoFactorBackupDescription: t('auth.login.twoFactorBackupDescription', { defaultValue: 'Enter one of the backup codes you saved when you set up two-factor authentication.' }),
609+
twoFactorCodeLabel: t('auth.login.twoFactorCodeLabel', { defaultValue: 'Authentication code' }),
610+
twoFactorBackupCodeLabel: t('auth.login.twoFactorBackupCodeLabel', { defaultValue: 'Backup code' }),
611+
twoFactorVerifyButton: t('auth.login.twoFactorVerifyButton', { defaultValue: 'Verify' }),
612+
twoFactorVerifyingButton: t('auth.login.twoFactorVerifyingButton', { defaultValue: 'Verifying…' }),
613+
twoFactorUseBackupCodeText: t('auth.login.twoFactorUseBackupCodeText', { defaultValue: 'Use a backup code instead' }),
614+
twoFactorUseAuthenticatorText: t('auth.login.twoFactorUseAuthenticatorText', { defaultValue: 'Use your authenticator app instead' }),
615+
twoFactorBackText: t('auth.login.twoFactorBackText', { defaultValue: 'Back to sign in' }),
598616
}}
599617
/>
600618
);

‎ui/packages/app-shell/src/console/auth/LoginPage.tsx‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,18 @@ export function LoginPage() {
204204
usePasswordText: t('auth.login.usePasswordText'),
205205
backToSsoText: t('auth.login.backToSsoText'),
206206
noSignInMethodText: t('auth.login.noSignInMethodText'),
207+
// The two-factor step a password sign-in leads to when the account
208+
// has two-factor sign-in on (objectstack#29105).
209+
twoFactorTitle: t('auth.login.twoFactorTitle'),
210+
twoFactorDescription: t('auth.login.twoFactorDescription'),
211+
twoFactorBackupDescription: t('auth.login.twoFactorBackupDescription'),
212+
twoFactorCodeLabel: t('auth.login.twoFactorCodeLabel'),
213+
twoFactorBackupCodeLabel: t('auth.login.twoFactorBackupCodeLabel'),
214+
twoFactorVerifyButton: t('auth.login.twoFactorVerifyButton'),
215+
twoFactorVerifyingButton: t('auth.login.twoFactorVerifyingButton'),
216+
twoFactorUseBackupCodeText: t('auth.login.twoFactorUseBackupCodeText'),
217+
twoFactorUseAuthenticatorText: t('auth.login.twoFactorUseAuthenticatorText'),
218+
twoFactorBackText: t('auth.login.twoFactorBackText'),
207219
}}
208220
/>
209221
);

‎ui/packages/app-shell/src/console/auth/signInRefusalMessages.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,24 @@ export function signInRefusalMessages(t: TranslateFn): Record<string, string> {
2626
EMAIL_NOT_VERIFIED: t('auth.login.errors.emailNotVerified', {
2727
defaultValue: 'Please verify your email address before signing in.',
2828
}),
29+
// objectstack#29105 — refusals of the two-factor step (better-auth's
30+
// `twoFactor()` codes). A wrong code is shown on the step itself; a spent
31+
// or expired challenge (the third and fourth) on the password step it
32+
// returns to.
33+
INVALID_CODE: t('auth.login.errors.twoFactorInvalidCode', {
34+
defaultValue: 'That code is not valid. Check your authenticator app and try again.',
35+
}),
36+
INVALID_BACKUP_CODE: t('auth.login.errors.twoFactorInvalidBackupCode', {
37+
defaultValue: 'That backup code is not valid or has already been used.',
38+
}),
39+
TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: t('auth.login.errors.twoFactorTooManyAttempts', {
40+
defaultValue: 'Too many incorrect codes. Sign in with your password again.',
41+
}),
42+
INVALID_TWO_FACTOR_COOKIE: t('auth.login.errors.twoFactorExpired', {
43+
defaultValue: 'This sign-in has expired. Sign in with your password again.',
44+
}),
45+
ACCOUNT_TEMPORARILY_LOCKED: t('auth.login.errors.twoFactorLocked', {
46+
defaultValue: 'Too many failed attempts. Your account is temporarily locked. Try again later.',
47+
}),
2948
};
3049
}

‎ui/packages/auth/src/AuthContext.ts‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { createContext } from 'react';
10-
import type { AuthUser, AuthClientSession, PreviewModeOptions, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthInvitationPreview, AuthPublicConfig, SignInWithProviderOptions, DelegableScope } from './types.js';
10+
import type { AuthUser, AuthClientSession, PreviewModeOptions, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthInvitationPreview, AuthPublicConfig, SignInWithProviderOptions, DelegableScope, TwoFactorChallenge, SignInTwoFactorCodeKind } from './types.js';
1111

1212
export interface AuthContextValue {
1313
/** Current authenticated user */
@@ -31,8 +31,30 @@ export interface AuthContextValue {
3131
isPreviewMode: boolean;
3232
/** Preview mode configuration (only set when isPreviewMode is true) */
3333
previewMode: PreviewModeOptions | null;
34-
/** Sign in with email and password */
35-
signIn: (email: string, password: string) => Promise<void>;
34+
/**
35+
* Sign in with email and password. `twoFactorRequired` is true when the
36+
* server accepted the password but the account has two-factor sign-in on
37+
* (objectstack#29105): no user is signed in yet, `twoFactorChallenge` is
38+
* set, and `verifySignInTwoFactor` finishes the sign-in.
39+
*/
40+
signIn: (email: string, password: string) => Promise<{ twoFactorRequired: boolean }>;
41+
/**
42+
* objectstack#29105 — the pending two-factor sign-in, or `null`. Set by a
43+
* `signIn` whose password the server accepted for an account with
44+
* two-factor sign-in on; cleared when the code is verified, when
45+
* `cancelTwoFactorChallenge` runs, and when the server reports the pending
46+
* sign-in spent or expired.
47+
*/
48+
twoFactorChallenge: TwoFactorChallenge | null;
49+
/**
50+
* Finish the pending two-factor sign-in with an authenticator code or a
51+
* backup code. Rejects with the server's `code` on a wrong code and keeps
52+
* the challenge; on a spent or expired challenge it also clears it, so the
53+
* user signs in with their password again.
54+
*/
55+
verifySignInTwoFactor: (code: string, kind: SignInTwoFactorCodeKind) => Promise<void>;
56+
/** Abandon the pending two-factor sign-in and go back to the password step. */
57+
cancelTwoFactorChallenge: () => void;
3658
/** Sign up with name, email, and password.
3759
* Returns `{ requiresVerification: true }` when the server accepted the
3860
* account but is gating sign-in on email verification; callers should

‎ui/packages/auth/src/AuthProvider.tsx‎

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
1010
import { authGateEvents } from './auth-gate-events.js';
11-
import type { AuthUser, AuthClient, AuthProviderOptions, PreviewModeOptions, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthInvitationPreview, AuthPublicConfig, SignInWithProviderOptions } from './types.js';
11+
import type { AuthUser, AuthClient, AuthProviderOptions, PreviewModeOptions, AuthOrganization, AuthOrganizationMember, AuthInvitation, AuthInvitationPreview, AuthPublicConfig, SignInWithProviderOptions, SignInTwoFactorCodeKind, TwoFactorChallenge } from './types.js';
12+
import { TWO_FACTOR_CHALLENGE_ENDED_CODES } from './two-factor-codes.js';
1213
import { AuthCtx, type AuthContextValue } from './AuthContext.js';
1314
import { createAuthClient, takePreflightSession, TokenStorage } from './createAuthClient.js';
1415
import { ActiveOrganizationStorage, SessionUserScope } from './ActiveOrganizationStorage.js';
@@ -191,6 +192,8 @@ export function AuthProvider({
191192
const bootSessionFor = useRef<string | null>(bootSession ? authUrl : null);
192193
const [error, setError] = useState<Error | null>(null);
193194
const [remediationRequired, setRemediationRequired] = useState<{ code: string; message: string } | null>(null);
195+
// objectstack#29105 — a password sign-in waiting for its second factor.
196+
const [twoFactorChallenge, setTwoFactorChallenge] = useState<TwoFactorChallenge | null>(null);
194197

195198
// Organization / workspace state
196199
const [organizations, setOrganizations] = useState<AuthOrganization[]>([]);
@@ -393,8 +396,16 @@ export function AuthProvider({
393396
setError(null);
394397
try {
395398
const result = await client.signIn({ email, password });
399+
if (result.twoFactor) {
400+
// Password accepted, no session yet: the account has two-factor
401+
// sign-in on. The sign-in page asks for the code (objectstack#29105).
402+
setTwoFactorChallenge(result.twoFactor);
403+
return { twoFactorRequired: true };
404+
}
405+
setTwoFactorChallenge(null);
396406
setUser(result.user);
397407
setSession(result.session);
408+
return { twoFactorRequired: false };
398409
} catch (err) {
399410
const authError = err instanceof Error ? err : new Error(String(err));
400411
setError(authError);
@@ -406,6 +417,33 @@ export function AuthProvider({
406417
[client],
407418
);
408419

420+
const verifySignInTwoFactor = useCallback(
421+
async (code: string, kind: SignInTwoFactorCodeKind) => {
422+
setError(null);
423+
try {
424+
const result = await client.verifySignInTwoFactor(code, kind);
425+
setTwoFactorChallenge(null);
426+
setUser(result.user);
427+
setSession(result.session);
428+
} catch (err) {
429+
const authError = err instanceof Error ? err : new Error(String(err));
430+
// The server has dropped the pending sign-in: too many wrong codes, or
431+
// its cookie expired. Another code can never succeed, so the challenge
432+
// ends here and the user signs in with their password again.
433+
const code = (authError as Error & { code?: string }).code;
434+
if (code && TWO_FACTOR_CHALLENGE_ENDED_CODES.has(code)) setTwoFactorChallenge(null);
435+
setError(authError);
436+
throw authError;
437+
}
438+
},
439+
[client],
440+
);
441+
442+
const cancelTwoFactorChallenge = useCallback(() => {
443+
setTwoFactorChallenge(null);
444+
setError(null);
445+
}, []);
446+
409447
const signUp = useCallback(
410448
async (name: string, email: string, password: string, callbackURL?: string, captchaToken?: string) => {
411449
setIsLoading(true);
@@ -445,6 +483,7 @@ export function AuthProvider({
445483
setUser(null);
446484
setSession(null);
447485
setError(null);
486+
setTwoFactorChallenge(null);
448487
// The organization block is the previous principal's data too: the list
449488
// is the workspaces THAT user belongs to (the switcher renders it), and
450489
// a lingering `activeOrganization` would additionally suppress the
@@ -1108,6 +1147,9 @@ export function AuthProvider({
11081147
isPreviewMode,
11091148
previewMode: isPreviewMode ? previewMode : null,
11101149
signIn,
1150+
twoFactorChallenge,
1151+
verifySignInTwoFactor,
1152+
cancelTwoFactorChallenge,
11111153
signUp,
11121154
signOut,
11131155
refreshSession,
@@ -1155,7 +1197,7 @@ export function AuthProvider({
11551197
}),
11561198
[
11571199
user, session, isAuthenticated, isAuthEnabled, isLoading, error, isPreviewMode, previewMode,
1158-
signIn, signUp, signOut, refreshSession, updateUser, forgotPassword, sendVerificationEmail, resetPassword, changePassword, setInitialPassword, hasLocalPassword, getAuthConfig, signInWithProvider,
1200+
signIn, twoFactorChallenge, verifySignInTwoFactor, cancelTwoFactorChallenge, signUp, signOut, refreshSession, updateUser, forgotPassword, sendVerificationEmail, resetPassword, changePassword, setInitialPassword, hasLocalPassword, getAuthConfig, signInWithProvider,
11591201
sendPhoneOtp, signInWithPhoneOtp, signInWithPhonePassword, requestPhonePasswordReset, resetPasswordWithPhoneOtp,
11601202
remediationRequired, enrollTotp, verifyTotp,
11611203
organizations, activeOrganization, activeMember, isOrganizationsLoading, isMembershipResolved, switchOrganization, createOrganization, refreshOrganizations,

0 commit comments

Comments
 (0)