Stop pretending the two-factor field can work
Signing in with a two-factor code failed with a bare 401 and "Invalid credentials", which sent the user off to check a password that was perfectly good (#75). It cannot work, and the app already knew. Stalwart accepts a TOTP code only through an OAuth flow -- its own web interface is an OAuth client, which is why signing in *there* succeeds -- and it offers only the authorization-code and device flows. There is no password grant, so a client holding a username and password has nowhere to exchange them plus a code for a token. The concatenated `password$code` form this README claimed was accepted is not a route the server has, and appears never to have been. What was verified live on 0.16.19 was enabling and disabling 2FA, never signing in with a code. The contradiction was already in the codebase: turning 2FA *on* mints an app password and reseals the session onto it, precisely because a plain password stops working from that moment. The sign-in page was the one place still assuming otherwise. Three changes, no new capability: - A 401 on a sign-in that carried a code now says what is happening and where to go instead, and says the password is probably fine. A sign-in without a code is untouched, so an ordinary typo still reads as an ordinary typo. - The field stays, and is honest about itself. Removing it would leave someone with 2FA finding nothing at all, which is worse than finding a field that explains the situation and points at app passwords. - The README's claim is corrected rather than quietly dropped, and real 2FA support is written into the roadmap as what it is: an OAuth implementation, handing sign-in to Stalwart and holding a refresh token instead of a sealed password.
This commit is contained in:
+13
-1
@@ -79,7 +79,19 @@ export function LoginPage() {
|
||||
<div className="field">
|
||||
<label htmlFor="t">Two-factor code</label>
|
||||
<input id="t" className="input" inputMode="numeric" autoComplete="one-time-code" placeholder="123456" value={totp} onChange={(e) => setTotp(e.target.value)} autoFocus />
|
||||
<span className="hint">Enter the code from your authenticator app if your account uses 2FA.</span>
|
||||
{/*
|
||||
Kept, and honest about itself. Stalwart accepts a TOTP code only
|
||||
through an OAuth flow, and offers no password grant, so no client
|
||||
holding a username and password can pass one — the field cannot
|
||||
work here today. It stays because someone with 2FA will look for
|
||||
it, and finding nothing is worse than finding this; the hint sends
|
||||
them somewhere that does work, and the server explains it again if
|
||||
they try anyway.
|
||||
*/}
|
||||
<span className="hint">
|
||||
Most mail servers, Stalwart included, do not accept two-factor codes from webmail — use an app password instead, created in
|
||||
your mail server's own settings. This field is here for servers that do.
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<button type="button" className="btn btn-ghost btn-sm" style={{ marginBottom: 12, color: "var(--fg-muted)" }} onClick={() => setShowTotp(true)}>
|
||||
|
||||
Reference in New Issue
Block a user