Say a reset link has expired before asking for the work

The page rendered the form without looking at the token, so somebody
opening a link an hour late typed a password, typed it again to confirm,
and was then told "this password reset token is invalid" — a word nobody
outside the code knows, at the end rather than the start. Links last an
hour and people open them late. That is ordinary, not an error to be
scolded for.

store() still validates and is still the rule; there is a test that a
spent token is refused there whatever the page drew. This is only the
screen being honest a minute earlier.

An address that is missing, or belongs to nobody, is drawn as the form was
before. Partly because an unanswerable question is not an expired link,
but mostly because a page that said "expired" for a real address and
something else for an unknown one would answer whether an account exists
here to anybody typing guesses — the exact property /forgot-password
protects by saying "a link will be sent if the account exists". Two tests
pin that.

Worth having now rather than later: the advisories publishing with this
release will send more people than usual through this screen, in a hurry
and some of them frightened.

Found by the portal session's user, who opened a real link an hour and
forty minutes after it was sent.
This commit is contained in:
ignacionelson
2026-09-08 20:08:28 -03:00
parent a1f59e133b
commit df44c46a12
20 changed files with 218 additions and 18 deletions
+4
View File
@@ -38,6 +38,10 @@ installation from starting.
**Fixed**
- **An expired password reset link now says so before asking for a new password.** The page took a
password, took it a second time to confirm, and only then answered "this password reset token is
invalid" — a word nobody outside the code knows, at the end of the work rather than the start.
Links last an hour and people open them late; the screen now says that, and offers to send another.
- **Erasing a staff account no longer hands their files to a client.** The account that inherits
erased content is one setting for the whole installation, and it can name a client — which is
right when a client is erased and their files go to another client. Applied to a staff account it
@@ -30,9 +30,50 @@ class NewPasswordController extends Controller
return Inertia::render('auth/reset-password', [
'email' => $request->email,
'token' => $request->route('token'),
'expired' => $this->linkIsSpent(
(string) $request->string('email'),
(string) $request->route('token'),
),
]);
}
/**
* Whether this link is one store() is certain to refuse.
*
* The scaffolding renders the form without looking at the token, so an
* expired link asked for a new password, asked for it a second time to
* confirm, and only then answered "this password reset token is
* invalid" — naming a word nobody outside the code knows, after the
* work rather than before it. Reset links last an hour and people open
* them late; that is ordinary, not an error to be scolded for.
*
* store() still validates and remains the rule. This is the screen
* being honest a minute earlier.
*
* **An address that is missing or belongs to nobody is not an expired
* link, and is drawn as the form was before.** Two reasons, and the
* second is the one that matters: a page that said "expired" for a
* real address and something else for an unknown one would answer
* whether an account exists here, to anybody who typed a guess the
* property /forgot-password already protects by saying "a link will be
* sent if the account exists".
*/
private function linkIsSpent(string $email, string $token): bool
{
if ($email === '' || $token === '') {
return false;
}
$broker = Password::broker();
$user = $broker->getUser(['email' => $email]);
if ($user === null) {
return false;
}
return ! $broker->tokenExists($user, $token);
}
/**
* Handle an incoming new password request.
*
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Encara no s'ha descarregat",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Afegeix també la reclamació opcional \"xms_edov\" al registre de l'aplicació, a Configuració de testimoni. Indicar el tenant diu quin directori avala l'inici de sessió; aquesta reclamació diu que el directori ha comprovat que la persona és realment propietària de l'adreça. Sense ella, algú altre dins del teu tenant podria iniciar sessió amb l'adreça d'un company, de manera que ProjectSend crearà comptes nous però mai no vincularà un inici de sessió de Microsoft a un compte que ja existeix.",
"A password reset goes to this address, so changing it needs your password.": "El restabliment de contrasenya s'envia a aquesta adreça, així que canviar-la requereix la teva contrasenya.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "La teva adreça de correu prové del directori o del proveïdor d'identitat amb què inicies sessió, i no es pot canviar aquí."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "La teva adreça de correu prové del directori o del proveïdor d'identitat amb què inicies sessió, i no es pot canviar aquí.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Els enllaços de restabliment duren una hora. Demana'n un de nou i arribarà de seguida.",
"Send me a new link": "Envia'm un enllaç nou",
"This link has expired": "Aquest enllaç ha caducat"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Zatím nestaženo",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Přidejte také volitelný nárok \"xms_edov\" do registrace aplikace v části Konfigurace tokenu. Uvedení tenanta říká, který adresář se za přihlášení zaručil; tento nárok říká, že adresář ověřil, že adresa opravdu patří dané osobě. Bez něj by se někdo jiný ve vašem tenantovi mohl přihlásit adresou kolegy, takže ProjectSend bude vytvářet nové účty, ale nikdy nepřipojí přihlášení přes Microsoft k účtu, který už existuje.",
"A password reset goes to this address, so changing it needs your password.": "Obnovení hesla se posílá na tuto adresu, takže její změna vyžaduje vaše heslo.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Vaše e-mailová adresa pochází z adresáře nebo poskytovatele identity, přes kterého se přihlašujete, a nelze ji zde změnit."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Vaše e-mailová adresa pochází z adresáře nebo poskytovatele identity, přes kterého se přihlašujete, a nelze ji zde změnit.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Odkazy pro obnovení platí jednu hodinu. Požádejte o nový a za chvíli dorazí.",
"Send me a new link": "Pošlete mi nový odkaz",
"This link has expired": "Platnost tohoto odkazu vypršela"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Noch nicht heruntergeladen",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Fügen Sie außerdem den optionalen Anspruch \"xms_edov\" unter Tokenkonfiguration zu Ihrer App-Registrierung hinzu. Die Angabe des Mandanten sagt, welches Verzeichnis für die Anmeldung bürgt; dieser Anspruch sagt, dass das Verzeichnis geprüft hat, dass die Person die Adresse wirklich besitzt. Ohne ihn könnte sich jemand anderes in Ihrem Mandanten mit der Adresse einer Kollegin anmelden, daher legt ProjectSend zwar neue Konten an, verknüpft eine Microsoft-Anmeldung aber nie mit einem bereits bestehenden Konto.",
"A password reset goes to this address, so changing it needs your password.": "Eine Kennwortzurücksetzung geht an diese Adresse, daher ist für eine Änderung Ihr Kennwort erforderlich.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Ihre E-Mail-Adresse stammt aus dem Verzeichnis oder Identitätsanbieter, mit dem Sie sich anmelden, und kann hier nicht geändert werden."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Ihre E-Mail-Adresse stammt aus dem Verzeichnis oder Identitätsanbieter, mit dem Sie sich anmelden, und kann hier nicht geändert werden.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Zurücksetzungslinks gelten eine Stunde. Fordern Sie einen neuen an, er trifft gleich ein.",
"Send me a new link": "Neuen Link senden",
"This link has expired": "Dieser Link ist abgelaufen"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Todavía no se descargó",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Añade también la notificación opcional \"xms_edov\" al registro de tu aplicación, en Configuración de token. Indicar el tenant dice qué directorio avaló el inicio de sesión; esa notificación dice que el directorio comprobó que la persona es realmente dueña de la dirección. Sin ella, alguien más dentro de tu tenant podría entrar con la dirección de un compañero, así que ProjectSend creará cuentas nuevas pero nunca enlazará un inicio de sesión de Microsoft con una cuenta que ya existe.",
"A password reset goes to this address, so changing it needs your password.": "El restablecimiento de contraseña se envía a esta dirección, así que cambiarla necesita tu contraseña.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Tu dirección de correo viene del directorio o proveedor de identidad con el que inicias sesión, y no se puede cambiar aquí."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Tu dirección de correo viene del directorio o proveedor de identidad con el que inicias sesión, y no se puede cambiar aquí.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Los enlaces de restablecimiento duran una hora. Pedí uno nuevo y llegará en un momento.",
"Send me a new link": "Enviame un enlace nuevo",
"This link has expired": "Este enlace ha caducado"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Pas encore téléchargé",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Ajoutez également la revendication facultative « xms_edov » à votre inscription d'application, sous Configuration des jetons. Indiquer le locataire dit quel annuaire s'est porté garant de la connexion ; cette revendication dit que l'annuaire a vérifié que la personne possède réellement l'adresse. Sans elle, quelqu'un d'autre dans votre locataire pourrait se connecter avec l'adresse d'un collègue, aussi ProjectSend créera de nouveaux comptes mais ne rattachera jamais une connexion Microsoft à un compte existant.",
"A password reset goes to this address, so changing it needs your password.": "Une réinitialisation de mot de passe part vers cette adresse, la modifier demande donc votre mot de passe.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Votre adresse e-mail provient de l'annuaire ou du fournisseur d'identité avec lequel vous vous connectez, et ne peut pas être modifiée ici."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Votre adresse e-mail provient de l'annuaire ou du fournisseur d'identité avec lequel vous vous connectez, et ne peut pas être modifiée ici.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Les liens de réinitialisation durent une heure. Demandez-en un nouveau, il arrivera dans un instant.",
"Send me a new link": "Envoyez-moi un nouveau lien",
"This link has expired": "Ce lien a expiré"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Belum pernah diunduh",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Tambahkan juga klaim opsional \"xms_edov\" ke pendaftaran aplikasi Anda, di bagian Token configuration. Menyebutkan tenant memberi tahu direktori mana yang menjamin proses masuk; klaim itu menyatakan bahwa direktori telah memeriksa bahwa orang tersebut benar-benar memiliki alamat itu. Tanpanya, orang lain di dalam tenant Anda dapat masuk dengan alamat rekan kerja, sehingga ProjectSend akan membuat akun baru tetapi tidak pernah menautkan proses masuk Microsoft ke akun yang sudah ada.",
"A password reset goes to this address, so changing it needs your password.": "Pengaturan ulang kata sandi dikirim ke alamat ini, jadi mengubahnya memerlukan kata sandi Anda.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Alamat email Anda berasal dari direktori atau penyedia identitas tempat Anda masuk, dan tidak dapat diubah di sini."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Alamat email Anda berasal dari direktori atau penyedia identitas tempat Anda masuk, dan tidak dapat diubah di sini.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Tautan atur ulang berlaku satu jam. Minta yang baru dan akan tiba sebentar lagi.",
"Send me a new link": "Kirimi saya tautan baru",
"This link has expired": "Tautan ini sudah kedaluwarsa"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Non ancora scaricato",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Aggiungi anche l'attestazione facoltativa \"xms_edov\" alla registrazione dell'app, in Configurazione token. Indicare il tenant dice quale directory ha garantito per l'accesso; quell'attestazione dice che la directory ha verificato che la persona possieda davvero l'indirizzo. Senza di essa, qualcun altro nel tuo tenant potrebbe accedere con l'indirizzo di un collega, quindi ProjectSend creerà nuovi account ma non collegherà mai un accesso Microsoft a un account già esistente.",
"A password reset goes to this address, so changing it needs your password.": "La reimpostazione della password viene inviata a questo indirizzo, quindi cambiarlo richiede la tua password.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Il tuo indirizzo email proviene dalla directory o dal provider di identità con cui accedi e non può essere modificato qui."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Il tuo indirizzo email proviene dalla directory o dal provider di identità con cui accedi e non può essere modificato qui.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "I link di reimpostazione durano un'ora. Chiedine uno nuovo e arriverà tra un momento.",
"Send me a new link": "Inviami un nuovo link",
"This link has expired": "Questo link è scaduto"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "まだダウンロードされていません",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "アプリ登録の「トークン構成」で、オプションの要求「xms_edov」も追加してください。テナントの指定は、どのディレクトリがサインインを保証したかを示します。この要求は、その人が本当にそのアドレスの持ち主であることをディレクトリが確認したことを示します。これがないと、テナント内の別の人が同僚のアドレスでサインインできてしまうため、ProjectSend は新しいアカウントは作成しますが、既存のアカウントに Microsoft サインインを結び付けることはありません。",
"A password reset goes to this address, so changing it needs your password.": "パスワードの再設定はこのアドレスに届くため、変更にはパスワードが必要です。",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "メールアドレスはサインインに使用しているディレクトリまたは ID プロバイダーのもので、ここでは変更できません。"
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "メールアドレスはサインインに使用しているディレクトリまたは ID プロバイダーのもので、ここでは変更できません。",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "再設定リンクの有効期間は 1 時間です。新しいリンクを申し込めば、すぐに届きます。",
"Send me a new link": "新しいリンクを送る",
"This link has expired": "このリンクは有効期限が切れています"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Nog niet gedownload",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Voeg ook de optionele claim \"xms_edov\" toe aan je app-registratie, onder Tokenconfiguratie. De tenant noemen zegt welke directory voor de aanmelding instaat; die claim zegt dat de directory heeft gecontroleerd dat de persoon het adres echt bezit. Zonder die claim kan iemand anders binnen je tenant zich aanmelden met het adres van een collega, dus ProjectSend maakt wel nieuwe accounts aan maar koppelt een Microsoft-aanmelding nooit aan een account dat al bestaat.",
"A password reset goes to this address, so changing it needs your password.": "Een wachtwoordherstel gaat naar dit adres, dus het wijzigen ervan vraagt om je wachtwoord.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Je e-mailadres komt van de directory of identiteitsprovider waarmee je je aanmeldt en kan hier niet worden gewijzigd."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Je e-mailadres komt van de directory of identiteitsprovider waarmee je je aanmeldt en kan hier niet worden gewijzigd.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Herstellinks zijn een uur geldig. Vraag een nieuwe aan, die komt zo binnen.",
"Send me a new link": "Stuur me een nieuwe link",
"This link has expired": "Deze link is verlopen"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Jeszcze nie pobrano",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Dodaj też opcjonalne oświadczenie \"xms_edov\" do rejestracji aplikacji, w sekcji Konfiguracja tokenu. Wskazanie dzierżawy mówi, który katalog poręczył za logowanie; to oświadczenie mówi, że katalog sprawdził, iż dana osoba naprawdę jest właścicielem adresu. Bez niego ktoś inny w Twojej dzierżawie mógłby zalogować się adresem współpracownika, więc ProjectSend będzie tworzyć nowe konta, ale nigdy nie powiąże logowania Microsoft z kontem, które już istnieje.",
"A password reset goes to this address, so changing it needs your password.": "Resetowanie hasła trafia na ten adres, więc jego zmiana wymaga Twojego hasła.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Twój adres e-mail pochodzi z katalogu lub dostawcy tożsamości, przez którego się logujesz, i nie można go tu zmienić."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Twój adres e-mail pochodzi z katalogu lub dostawcy tożsamości, przez którego się logujesz, i nie można go tu zmienić.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Linki do resetowania są ważne godzinę. Poproś o nowy, dotrze za chwilę.",
"Send me a new link": "Wyślij mi nowy link",
"This link has expired": "Ten link wygasł"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Ainda não foi baixado",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Adicione também a declaração opcional \"xms_edov\" ao registro do aplicativo, em Configuração de token. Informar o locatário diz qual diretório respondeu pelo login; essa declaração diz que o diretório verificou que a pessoa realmente é dona do endereço. Sem ela, outra pessoa dentro do seu locatário poderia entrar com o endereço de um colega, então o ProjectSend criará contas novas mas nunca vinculará um login da Microsoft a uma conta que já existe.",
"A password reset goes to this address, so changing it needs your password.": "A redefinição de senha vai para este endereço, então alterá-lo exige a sua senha.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Seu endereço de e-mail vem do diretório ou provedor de identidade com que você entra, e não pode ser alterado aqui."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Seu endereço de e-mail vem do diretório ou provedor de identidade com que você entra, e não pode ser alterado aqui.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Os links de redefinição duram uma hora. Peça um novo e ele chega em instantes.",
"Send me a new link": "Envie-me um novo link",
"This link has expired": "Este link expirou"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Ещё не скачивался",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Также добавьте необязательное утверждение «xms_edov» в регистрацию приложения, в разделе «Конфигурация токена». Указание клиента говорит, какой каталог поручился за вход; это утверждение говорит, что каталог проверил, что адрес действительно принадлежит человеку. Без него кто-то другой внутри вашего клиента смог бы войти с адресом коллеги, поэтому ProjectSend будет создавать новые учётные записи, но никогда не привяжет вход через Microsoft к уже существующей.",
"A password reset goes to this address, so changing it needs your password.": "Сброс пароля отправляется на этот адрес, поэтому его изменение требует вашего пароля.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Ваш адрес электронной почты берётся из каталога или поставщика удостоверений, через который вы входите, и здесь его изменить нельзя."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Ваш адрес электронной почты берётся из каталога или поставщика удостоверений, через который вы входите, и здесь его изменить нельзя.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Ссылки для сброса действуют один час. Запросите новую — она придёт через мгновение.",
"Send me a new link": "Прислать новую ссылку",
"This link has expired": "Срок действия ссылки истёк"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Bado haijapakuliwa",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Ongeza pia dai la hiari \"xms_edov\" kwenye usajili wa programu yako, chini ya Token configuration. Kutaja mpangaji kunaeleza ni saraka gani iliyodhamini kuingia; dai hilo linaeleza kuwa saraka imethibitisha kuwa mtu huyo ndiye mmiliki halisi wa anwani. Bila hilo, mtu mwingine ndani ya mpangaji wako anaweza kuingia kwa anwani ya mwenzake, hivyo ProjectSend itaunda akaunti mpya lakini haitaunganisha kamwe kuingia kwa Microsoft na akaunti iliyopo.",
"A password reset goes to this address, so changing it needs your password.": "Kuweka upya nenosiri hutumwa kwa anwani hii, hivyo kuibadilisha kunahitaji nenosiri lako.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Anwani yako ya barua pepe inatoka kwenye saraka au mtoa utambulisho unaotumia kuingia, na haiwezi kubadilishwa hapa."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Anwani yako ya barua pepe inatoka kwenye saraka au mtoa utambulisho unaotumia kuingia, na haiwezi kubadilishwa hapa.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Viungo vya kuweka upya hudumu saa moja. Omba kipya na kitafika punde.",
"Send me a new link": "Nitumie kiungo kipya",
"This link has expired": "Kiungo hiki kimeisha muda"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Henüz indirilmedi",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Uygulama kaydınıza, Belirteç yapılandırması altında \"xms_edov\" isteğe bağlı talebini de ekleyin. Kiracıyı belirtmek, oturum açma için hangi dizinin kefil olduğunu söyler; bu talep ise dizinin, kişinin adresin gerçekten sahibi olduğunu doğruladığını söyler. Bu olmadan, kiracınızdaki başka biri bir iş arkadaşının adresiyle oturum açabilir; bu nedenle ProjectSend yeni hesaplar oluşturur ancak bir Microsoft oturum açma işlemini var olan bir hesaba asla bağlamaz.",
"A password reset goes to this address, so changing it needs your password.": "Parola sıfırlama bu adrese gönderilir, bu yüzden değiştirmek parolanızı gerektirir.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "E-posta adresiniz, oturum açtığınız dizinden veya kimlik sağlayıcısından gelir ve burada değiştirilemez."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "E-posta adresiniz, oturum açtığınız dizinden veya kimlik sağlayıcısından gelir ve burada değiştirilemez.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Sıfırlama bağlantıları bir saat geçerlidir. Yenisini isteyin, birazdan ulaşır.",
"Send me a new link": "Bana yeni bir bağlantı gönder",
"This link has expired": "Bu bağlantının süresi doldu"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "Chưa được tải xuống",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "Đồng thời hãy thêm xác nhận quyền tùy chọn \"xms_edov\" vào đăng ký ứng dụng của bạn, trong phần Cấu hình mã thông báo. Việc nêu tên tenant cho biết thư mục nào đã bảo đảm cho lần đăng nhập; xác nhận quyền đó cho biết thư mục đã kiểm tra rằng người này thực sự sở hữu địa chỉ. Không có nó, một người khác trong tenant của bạn có thể đăng nhập bằng địa chỉ của đồng nghiệp, nên ProjectSend sẽ tạo tài khoản mới nhưng không bao giờ gắn một lần đăng nhập Microsoft vào tài khoản đã tồn tại.",
"A password reset goes to this address, so changing it needs your password.": "Việc đặt lại mật khẩu sẽ gửi tới địa chỉ này, nên thay đổi nó cần mật khẩu của bạn.",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Địa chỉ email của bạn đến từ thư mục hoặc nhà cung cấp danh tính mà bạn dùng để đăng nhập, và không thể thay đổi tại đây."
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "Địa chỉ email của bạn đến từ thư mục hoặc nhà cung cấp danh tính mà bạn dùng để đăng nhập, và không thể thay đổi tại đây.",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "Liên kết đặt lại có hiệu lực một giờ. Hãy yêu cầu liên kết mới, nó sẽ đến ngay.",
"Send me a new link": "Gửi cho tôi liên kết mới",
"This link has expired": "Liên kết này đã hết hạn"
}
+4 -1
View File
@@ -2024,5 +2024,8 @@
"Not downloaded yet": "尚未下载",
"Also add the \"xms_edov\" optional claim to your app registration, under Token configuration. Naming the tenant says which directory vouched for the sign-in; that claim says the directory checked the person really owns the address. Without it, someone else inside your tenant could sign in with a colleague's address, so ProjectSend will create new accounts but never attach a Microsoft sign-in to an account that already exists.": "还请在应用注册的“令牌配置”中添加可选声明“xms_edov”。指定租户说明是哪个目录为此次登录背书;该声明则说明目录已核实此人确实拥有该地址。没有它,你租户内的其他人就能用同事的地址登录,因此 ProjectSend 会创建新账户,但绝不会把 Microsoft 登录关联到已存在的账户。",
"A password reset goes to this address, so changing it needs your password.": "密码重置会发送到这个地址,所以修改它需要你的密码。",
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "你的邮箱地址来自你用于登录的目录或身份提供方,无法在此处修改。"
"Your email address comes from the directory or identity provider you sign in with, and cannot be changed here.": "你的邮箱地址来自你用于登录的目录或身份提供方,无法在此处修改。",
"Reset links last one hour. Ask for a new one and it will arrive in a moment.": "重置链接有效期为一小时。申请一个新的,稍后即可收到。",
"Send me a new link": "给我发送新链接",
"This link has expired": "此链接已过期"
}
+26 -2
View File
@@ -1,4 +1,4 @@
import { Head, useForm } from '@inertiajs/react';
import { Head, Link, useForm } from '@inertiajs/react';
import { LoaderCircle } from 'lucide-react';
import { FormEventHandler } from 'react';
@@ -13,6 +13,12 @@ import AuthLayout from '@/layouts/auth-layout';
interface ResetPasswordProps {
token: string;
email: string;
/**
* Whether the server already knows this link will be refused. False
* for an address it cannot place, which is not the same thing see
* NewPasswordController::linkIsSpent().
*/
expired: boolean;
}
interface ResetPasswordForm {
@@ -23,7 +29,7 @@ interface ResetPasswordForm {
password_confirmation: string;
}
export default function ResetPassword({ token, email }: ResetPasswordProps) {
export default function ResetPassword({ token, email, expired }: ResetPasswordProps) {
const { t } = useTranslation();
const { data, setData, post, processing, errors, reset } = useForm<ResetPasswordForm>({
@@ -40,6 +46,24 @@ export default function ResetPassword({ token, email }: ResetPasswordProps) {
});
};
// Said before the work rather than after it. Reset links last an hour
// and people open them late; asking for a password twice and then
// refusing it is a bad minute for somebody who is already worried.
if (expired) {
return (
<AuthLayout
title={t('This link has expired')}
description={t('Reset links last one hour. Ask for a new one and it will arrive in a moment.')}
>
<Head title={t('This link has expired')} />
<Button className="w-full" asChild>
<Link href={route('password.request')}>{t('Send me a new link')}</Link>
</Button>
</AuthLayout>
);
}
return (
<AuthLayout title={t('Reset password')} description={t('Please enter your new password below')}>
<Head title={t('Reset password')} />
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
use App\Models\User;
use Illuminate\Support\Facades\Password;
use Inertia\Testing\AssertableInertia;
/**
* An expired reset link should say so before asking for the work, not
* after it.
*
* The scaffolding renders the form without looking at the token, so
* somebody opening a link an hour late typed a password, typed it again to
* confirm, and was then told "this password reset token is invalid" a
* word nobody outside the code knows, at the end rather than the start.
*
* store() still validates and is still the rule. This is only the screen
* being honest a minute earlier.
*/
beforeEach(function () {
$this->user = User::factory()->create(['email' => 'owner@example.com']);
});
test('a live link still shows the form', function () {
$token = Password::broker()->createToken($this->user);
$this->get("/reset-password/{$token}?email=owner@example.com")
->assertOk()
->assertInertia(fn (AssertableInertia $page) => $page
->component('auth/reset-password')
->where('expired', false));
});
test('a spent link says so instead of asking for a password', function () {
$this->get('/reset-password/not-a-real-token?email=owner@example.com')
->assertOk()
->assertInertia(fn (AssertableInertia $page) => $page->where('expired', true));
});
test('a link whose token has been used is spent', function () {
$token = Password::broker()->createToken($this->user);
Password::broker()->deleteToken($this->user);
$this->get("/reset-password/{$token}?email=owner@example.com")
->assertOk()
->assertInertia(fn (AssertableInertia $page) => $page->where('expired', true));
});
/*
|--------------------------------------------------------------------------
| It must not answer whether an account exists
|--------------------------------------------------------------------------
|
| /forgot-password deliberately says "a link will be sent if the account
| exists". A screen that said "expired" for a real address and something
| else for an unknown one would give that away to anybody typing guesses.
*/
test('an address nobody has is not called expired', function () {
$this->get('/reset-password/not-a-real-token?email=nobody@example.com')
->assertOk()
->assertInertia(fn (AssertableInertia $page) => $page->where('expired', false));
});
test('a missing address is not called expired either', function () {
$this->get('/reset-password/not-a-real-token')
->assertOk()
->assertInertia(fn (AssertableInertia $page) => $page->where('expired', false));
});
test('the real check still happens on the write', function () {
// The screen is a courtesy; store() is the rule. A spent token is
// refused there whatever the page decided to draw.
$this->post('/reset-password', [
'token' => 'not-a-real-token',
'email' => 'owner@example.com',
'password' => 'a-brand-new-password',
'password_confirmation' => 'a-brand-new-password',
])->assertSessionHasErrors('email');
expect(Hash::check('a-brand-new-password', $this->user->fresh()->password))->toBeFalse();
});