feat(auth): sign in with an email address, keeping only a keyed hash of it
People without GitHub can now keep an anonymous account, and sign in on another device, with an email address and a code mailed to it each time; no password. The instance keeps only HMAC-SHA256 of the address, keyed with its emailSignInPepper (ADR 0010): from what someone types it finds the person and tells whether an address is free, it mails only an address someone has just typed, and it can't write to anybody otherwise or show anybody their address. The device remembers the address it signed in with, in the person's folder, to show it. It works as GitHub does. A code for a new address makes it the signed-in person's, who becomes identified and keeps everything. A code for another person's address gives a switch token, which signs in as them without a session; a device holding studysets asks first. Asking for a code answers alike for every address. Codes are eight Crockford characters, valid 15 minutes and three tries (Serverpod's code challenges); three codes an hour per address, ten per network. In development the code goes to the server's log; elsewhere there is no email sign-in until a mail service is set up (instance.info().emailSignIn). Someone identified may add the other way in: the Profile tab lists how they sign in and adds GitHub or an address. They are never switched to another account: an address or GitHub account that is another's is said to be so. Switching now deletes the account left behind only when it was anonymous; it deleted any, which only anonymous people could reach so far. The migration adds email_identity and email_code_request with their indexes and foreign keys to Serverpod's challenges; nothing else changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
58e13521b1
commit
1cfd391dbc
1 changed files with 21720 additions and 103 deletions
|
|
@ -1778,6 +1778,113 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get profileSwitchConfirm => 'Wechseln';
|
||||
|
||||
@override
|
||||
String get profileContinueWithEmail => 'Mit E-Mail fortfahren';
|
||||
|
||||
@override
|
||||
String get profileSignInWays => 'Anmeldewege';
|
||||
|
||||
@override
|
||||
String get profileWayGitHub => 'GitHub';
|
||||
|
||||
@override
|
||||
String get profileWayEmail => 'E-Mail-Adresse';
|
||||
|
||||
@override
|
||||
String get profileWayLinked => 'Meldet dich an';
|
||||
|
||||
@override
|
||||
String get profileAddGitHub => 'GitHub hinzufügen';
|
||||
|
||||
@override
|
||||
String get profileAddEmail => 'E-Mail-Adresse hinzufügen';
|
||||
|
||||
@override
|
||||
String get profileGitHubTaken =>
|
||||
'Mit diesem GitHub-Konto meldet sich schon ein anderes Konto an.';
|
||||
|
||||
@override
|
||||
String get emailSignInTitle => 'Mit E-Mail fortfahren';
|
||||
|
||||
@override
|
||||
String get emailSignInIntro =>
|
||||
'Wir schicken dir einen Code zum Anmelden. Diese Instanz behält nur einen Fingerabdruck deiner Adresse, nie die Adresse selbst, und schreibt ihr nur, wenn du einen Code anforderst.';
|
||||
|
||||
@override
|
||||
String get emailSignInAddress => 'E-Mail-Adresse';
|
||||
|
||||
@override
|
||||
String get emailSignInSend => 'Code senden';
|
||||
|
||||
@override
|
||||
String emailSignInCodeSent(String address) {
|
||||
return 'Wir haben einen Code an $address geschickt. Er gilt 15 Minuten.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get emailSignInResent => 'Ein neuer Code ist unterwegs.';
|
||||
|
||||
@override
|
||||
String get emailSignInCode => 'Code';
|
||||
|
||||
@override
|
||||
String get emailSignInContinue => 'Weiter';
|
||||
|
||||
@override
|
||||
String get emailSignInResend => 'Neuen Code senden';
|
||||
|
||||
@override
|
||||
String get emailSignInOtherAddress => 'Andere Adresse verwenden';
|
||||
|
||||
@override
|
||||
String emailSignInSwitchTitle(String address) {
|
||||
return 'Zum Konto von $address wechseln?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get emailSignInSwitchMessage =>
|
||||
'Diese Adresse hat bereits Fortschritt von einem anderen Gerät. Die Lernsets auf diesem Gerät lassen sich noch nicht damit zusammenführen: Wenn du wechselst, werden sie gelöscht.';
|
||||
|
||||
@override
|
||||
String emailSignInTaken(String address) {
|
||||
return 'Mit $address meldet sich schon ein anderes Konto an. Um zu ihm zu wechseln, melde dich zuerst ab.';
|
||||
}
|
||||
|
||||
@override
|
||||
String emailSignInDone(String address) {
|
||||
return 'Mit $address angemeldet.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get emailSignInUnavailable =>
|
||||
'Diese Instanz bietet keine Anmeldung mit E-Mail an.';
|
||||
|
||||
@override
|
||||
String get emailSignInNotAnAddress => 'Das ist keine E-Mail-Adresse.';
|
||||
|
||||
@override
|
||||
String get emailSignInTooManyCodes =>
|
||||
'Zu viele Codes in der letzten Stunde. Versuche es später noch einmal.';
|
||||
|
||||
@override
|
||||
String get emailSignInWrongCode => 'Dieser Code stimmt nicht.';
|
||||
|
||||
@override
|
||||
String get emailSignInTooManyTries =>
|
||||
'Zu viele falsche Codes. Fordere einen neuen an.';
|
||||
|
||||
@override
|
||||
String get emailSignInCodeExpired =>
|
||||
'Dieser Code gilt nicht mehr. Fordere einen neuen an.';
|
||||
|
||||
@override
|
||||
String get emailSignInOtherAddressLinked =>
|
||||
'Du meldest dich schon mit einer anderen Adresse an.';
|
||||
|
||||
@override
|
||||
String get emailSignInFailed =>
|
||||
'Die Instanz war nicht erreichbar. Versuche es noch einmal.';
|
||||
|
||||
@override
|
||||
String get publicProfileTitle => 'Öffentliches Profil';
|
||||
|
||||
|
|
@ -1798,7 +1905,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get publicProfileAnonymousHint =>
|
||||
'Melde dich mit GitHub an, um ein öffentliches Profil zu haben und zu veröffentlichen.';
|
||||
'Melde dich an, um ein öffentliches Profil zu haben und zu veröffentlichen.';
|
||||
|
||||
@override
|
||||
String get publicProfileDisplayName => 'Anzeigename';
|
||||
|
|
@ -2014,7 +2121,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String accountRemoveMessage(String name) {
|
||||
return 'Entfernt das Konto auf $name von diesem Gerät, mit allem, was das Gerät davon hat. Was es synchronisiert hat, bleibt auf $name: Melde dich dort mit GitHub an, um es wiederzuhaben. In ein anonymes Konto kann man sich nicht wieder anmelden.';
|
||||
return 'Entfernt das Konto auf $name von diesem Gerät, mit allem, was das Gerät davon hat. Was es synchronisiert hat, bleibt auf $name: Melde dich dort wieder an, um es wiederzuhaben. In ein anonymes Konto kann man sich nicht wieder anmelden.';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -5899,7 +6006,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get publishAnonymous =>
|
||||
'Melde dich mit GitHub an, um ein öffentliches Profil zu haben und zu veröffentlichen.';
|
||||
'Melde dich an, um ein öffentliches Profil zu haben und zu veröffentlichen.';
|
||||
|
||||
@override
|
||||
String get publishName => 'Name';
|
||||
|
|
@ -6411,7 +6518,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get feedbackNeedsIdentity =>
|
||||
'Melde dich mit GitHub an, um im Original zu melden und vorzuschlagen.';
|
||||
'Melde dich an, um im Original zu melden und vorzuschlagen.';
|
||||
|
||||
@override
|
||||
String get kanjiExampleWords => 'Wörter damit';
|
||||
|
|
|
|||
Loading…
Reference in a new issue