mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: finish a webmail hand-off with a TOTP code instead of failing
This commit is contained in:
@@ -140,12 +140,27 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo
|
|||||||
// that's a cancellation, not a failure, so leave them where they were.
|
// that's a cancellation, not a failure, so leave them where they were.
|
||||||
finishIfSignedIn(wasAuthenticated);
|
finishIfSignedIn(wasAuthenticated);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// The webmail hands back the password even when the account needs a
|
||||||
|
// second factor; finish the sign-in here with a code instead of
|
||||||
|
// failing (the user already proved the password once).
|
||||||
|
const pending = useAuthStore.getState().pendingTotpLogin;
|
||||||
|
if (err instanceof Error && err.name === 'TotpRequiredError' && pending) {
|
||||||
|
setEmail(pending.username);
|
||||||
|
setPassword(pending.password);
|
||||||
|
setServerUrl(pending.serverUrl);
|
||||||
|
setTotpRequired(true);
|
||||||
|
setBusy(null);
|
||||||
|
setNotice(describeLoginError(err, { serverUrl: target }));
|
||||||
|
setHistory((prev) => [...prev, step]);
|
||||||
|
setStep('password');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setNotice(describeLoginError(err, { serverUrl: target }));
|
setNotice(describeLoginError(err, { serverUrl: target }));
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(null);
|
setBusy(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[finishIfSignedIn, isAddMode, loginViaWebmail],
|
[finishIfSignedIn, isAddMode, loginViaWebmail, step],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEmailContinue = React.useCallback(async () => {
|
const handleEmailContinue = React.useCallback(async () => {
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ export interface AuthState {
|
|||||||
accountId: string | null;
|
accountId: string | null;
|
||||||
activeAccountId: string | null;
|
activeAccountId: string | null;
|
||||||
client: typeof jmapClient | null;
|
client: typeof jmapClient | null;
|
||||||
|
/**
|
||||||
|
* Set when a password sign-in was refused with "MFA code required"; the
|
||||||
|
* login screen re-runs it with a code via `login(..., { totp })`. Cleared
|
||||||
|
* on the next successful sign-in.
|
||||||
|
*/
|
||||||
|
pendingTotpLogin: { serverUrl: string; username: string; password: string } | null;
|
||||||
|
|
||||||
login: (
|
login: (
|
||||||
serverUrl: string,
|
serverUrl: string,
|
||||||
@@ -232,6 +238,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
accountId: null,
|
accountId: null,
|
||||||
activeAccountId: null,
|
activeAccountId: null,
|
||||||
client: null,
|
client: null,
|
||||||
|
pendingTotpLogin: null,
|
||||||
|
|
||||||
login: async (serverUrl, username, password, opts) => {
|
login: async (serverUrl, username, password, opts) => {
|
||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
@@ -270,8 +277,14 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
useEmailStore.getState().setActiveAccount(accountId);
|
useEmailStore.getState().setActiveAccount(accountId);
|
||||||
|
|
||||||
applyConnectedState(set, session, serverUrl.replace(/\/+$/, ''), username, accountId);
|
applyConnectedState(set, session, serverUrl.replace(/\/+$/, ''), username, accountId);
|
||||||
|
set({ pendingTotpLogin: null });
|
||||||
void syncAccountDisplayName(accountId);
|
void syncAccountDisplayName(accountId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.name === 'TotpRequiredError') {
|
||||||
|
// Keep what the user (or the webmail hand-off) supplied so the code
|
||||||
|
// step doesn't make them retype the password.
|
||||||
|
set({ pendingTotpLogin: { serverUrl: serverUrl.replace(/\/+$/, ''), username, password } });
|
||||||
|
}
|
||||||
const message = err instanceof Error && err.name === 'TotpRequiredError'
|
const message = err instanceof Error && err.name === 'TotpRequiredError'
|
||||||
? 'Two-factor code required'
|
? 'Two-factor code required'
|
||||||
: err instanceof AuthenticationError
|
: err instanceof AuthenticationError
|
||||||
|
|||||||
Reference in New Issue
Block a user