Skip the pointless password prompt before first-time password setup

checkEmailRequest only checked whether the account existed, so a
first-login account (no password set yet) was still sent to the
normal "enter your password" screen before the backend revealed there
was no password to check against — only then did it route to the
create-password step. checkEmailRequest now detects the same
setup-required signal PasswordSetupRequiredError does and skips
straight to it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Suriya
2026-07-21 17:14:14 +05:30
parent f029c09dd7
commit bbcf987488
2 changed files with 29 additions and 7 deletions

View File

@@ -57,8 +57,13 @@ export default function LoginView({ onLogin }: LoginViewProps) {
setError('');
setCheckingEmail(true);
try {
const emailExists = await checkEmailRequest(email);
if (emailExists) {
const result = await checkEmailRequest(email);
if (result.exists && result.needsSetup && result.userid != null) {
// No point asking for a password this account doesn't have yet —
// go straight to creating one.
setSetupUserId(result.userid);
setStep('setup');
} else if (result.exists) {
setStep('password');
} else {
setError('Email not found. Please try again.');

View File

@@ -204,11 +204,20 @@ export async function loginRequest(email: string, password: string): Promise<Log
return { row, hasRole: Boolean(row && row[RESPONSE_FIELDS.roleid] != null), email: resolvedEmail };
}
export interface EmailCheckResult {
exists: boolean;
/** True when the account has never had a password set — skip straight to
* the create-password step instead of asking for a password it doesn't
* have yet (see PasswordSetupRequiredError for why this is detectable
* even from this password-less probe request). */
needsSetup: boolean;
userid?: number;
}
/**
* Checks if the email/authname exists and is registered by sending email and configid: 1.
* Returns true if the email exists, false if it is invalid.
*/
export async function checkEmailRequest(email: string): Promise<boolean> {
export async function checkEmailRequest(email: string): Promise<EmailCheckResult> {
const trimmedEmail = email.trim();
let res: Response;
@@ -226,16 +235,24 @@ export async function checkEmailRequest(email: string): Promise<boolean> {
}
const json = (await res.json().catch(() => null)) as
| { code?: number; status?: boolean; message?: string }
| { code?: number; status?: boolean; message?: string; details?: unknown }
| null;
// Account exists but has no password yet — fires regardless of the
// (absent) password in this probe request. See loginRequest's identical
// check for the full explanation.
const details = json?.details as { userid?: number; setup?: boolean } | undefined;
if (json?.code === 409 && details?.setup === true && typeof details.userid === 'number') {
return { exists: true, needsSetup: true, userid: details.userid };
}
// A 409 Invalid Email code means the email does not exist.
if (json && json.status === false && (json.code === 409 || json.message?.trim().toLowerCase() === 'invalid email')) {
return false;
return { exists: false, needsSetup: false };
}
// Any other result (such as 403 Unauthorized email) means the email is registered.
return true;
return { exists: true, needsSetup: false };
}