fix(auth): a session's wrong codes end the session, not the account's codes
Wrong codes for removing an authenticator, new recovery codes or confirming
one counted toward the account's waits (the re-review's third finding): a
stolen session alone, without the password, could keep the owner's codes
waiting for up to a day at a time, renewed without end, holding them out
of signing in and of changing the password, the way to end that session,
and nobody was mailed. Those codes now count per session, where the session
is (the register's own, or a member's, which forwards the code): each try
counted before its code is checked (one statement, so tries sent at once
count each), a code accepted clearing the count, and the tenth wrong in a
row ending the session (sessionEnded; the device says it was signed out).
A try that checked no code (a refusal of something else) is taken back.
The account's waits now count only codes after a right password, and the
per-account ten tries an hour, which a session could fill as well, go.
Migration (module, Learn's and Todo's servers, Learn's device schema):
CREATE TABLE "session_code_failures" ("id" bigserial PRIMARY KEY,
"sessionId" uuid NOT NULL, "authUserId" uuid NOT NULL, "failures"
bigint NOT NULL, "updatedAt" timestamp NOT NULL) with a unique index on
"sessionId": a new table, nothing touched. Its rows go with a code
accepted, with the person, and hourly once their session is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
24d46a8503
commit
e8c4a3d472
8 changed files with 42634 additions and 88 deletions
|
|
@ -210,5 +210,6 @@
|
|||
"passwordMinLength": "Mindestens {count} Zeichen",
|
||||
"passwordTooShort": "Ein neues Passwort braucht mindestens {count} Zeichen: Sollte die Datenbank der Instanz je nach außen gelangen, wird ein kurzes zuerst erraten.",
|
||||
"passwordCodesWait": "Zu viele falsche Codes hintereinander: Bis {date} wird kein Code angenommen. Waren es nicht deine, kennt jemand dein Passwort: Ändere es dann.",
|
||||
"passwordDeleteIntro": "Dein Konto wird mit allem darin gelöscht, in jedem Dienst dieser Instanz. Gib zur Bestätigung dein Passwort und einen Code deiner Authenticator-App ein."
|
||||
"passwordDeleteIntro": "Dein Konto wird mit allem darin gelöscht, in jedem Dienst dieser Instanz. Gib zur Bestätigung dein Passwort und einen Code deiner Authenticator-App ein.",
|
||||
"passwordSessionEnded": "Zu viele falsche Codes hintereinander von diesem Gerät: Es wurde abgemeldet. Melde dich wieder mit deinem Passwort und einem Code an."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -457,5 +457,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"passwordDeleteIntro": "Your account goes with everything in it, in every service of this instance. To confirm, type your password and a code from your authenticator app."
|
||||
"passwordDeleteIntro": "Your account goes with everything in it, in every service of this instance. To confirm, type your password and a code from your authenticator app.",
|
||||
"passwordSessionEnded": "Too many wrong codes in a row from this device: it was signed out. Sign in again with your password and a code."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,12 @@ abstract class HalkyonAppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Your account goes with everything in it, in every service of this instance. To confirm, type your password and a code from your authenticator app.'**
|
||||
String get passwordDeleteIntro;
|
||||
|
||||
/// No description provided for @passwordSessionEnded.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Too many wrong codes in a row from this device: it was signed out. Sign in again with your password and a code.'**
|
||||
String get passwordSessionEnded;
|
||||
}
|
||||
|
||||
class _HalkyonAppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -792,4 +792,8 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
|||
@override
|
||||
String get passwordDeleteIntro =>
|
||||
'Dein Konto wird mit allem darin gelöscht, in jedem Dienst dieser Instanz. Gib zur Bestätigung dein Passwort und einen Code deiner Authenticator-App ein.';
|
||||
|
||||
@override
|
||||
String get passwordSessionEnded =>
|
||||
'Zu viele falsche Codes hintereinander von diesem Gerät: Es wurde abgemeldet. Melde dich wieder mit deinem Passwort und einem Code an.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -775,6 +775,10 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
|||
@override
|
||||
String get passwordDeleteIntro =>
|
||||
'Your account goes with everything in it, in every service of this instance. To confirm, type your password and a code from your authenticator app.';
|
||||
|
||||
@override
|
||||
String get passwordSessionEnded =>
|
||||
'Too many wrong codes in a row from this device: it was signed out. Sign in again with your password and a code.';
|
||||
}
|
||||
|
||||
/// The translations for English, as used in the United Kingdom (`en_GB`).
|
||||
|
|
|
|||
|
|
@ -433,6 +433,7 @@ PasswordProblem _problemOf(protocol.PasswordRefusal reason) => switch (reason) {
|
|||
// be taken as the instance's.
|
||||
protocol.PasswordRefusal.otherInstance => PasswordProblem.instanceUnproven,
|
||||
protocol.PasswordRefusal.codesWait => PasswordProblem.codesWait,
|
||||
protocol.PasswordRefusal.sessionEnded => PasswordProblem.sessionEnded,
|
||||
};
|
||||
|
||||
/// Keeps one session in memory, for a client used once.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ enum PasswordProblem() {
|
|||
/// [PasswordFailed.takesEffectAt].
|
||||
codesWait,
|
||||
|
||||
/// Ten wrong codes in a row from this device's session: it ended, and the
|
||||
/// device signs in again.
|
||||
sessionEnded,
|
||||
|
||||
/// No reset waits for the address.
|
||||
noReset,
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ String passwordProblemText(BuildContext context, PasswordFailed failed) {
|
|||
final at? => l10n.passwordCodesWait(passwordMoment(context, at)),
|
||||
null => l10n.passwordTooManyAttempts,
|
||||
},
|
||||
PasswordProblem.sessionEnded => l10n.passwordSessionEnded,
|
||||
PasswordProblem.noReset => l10n.passwordNoReset,
|
||||
PasswordProblem.passwordExists => l10n.passwordExists,
|
||||
PasswordProblem.addressTaken => l10n.passwordAddressTaken,
|
||||
|
|
|
|||
Loading…
Reference in a new issue