From 1c1bfb0ce8988febf9cedb40f2e654ef942e69da Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:32:15 +0200 Subject: [PATCH] fix: explain a refused token exchange instead of asking for Basic auth on a TOTP login When Stalwart accepted the password and code but refused to issue tokens (in Lite: `invalid_client` for an OAuth client with a secret), login retried `password$totp` over Basic. Stalwart 0.16+ rejects that, so the browser showed its own Basic-auth dialog and the login page then reported a wrong code. The login now stops with a message pointing at the OAuth client settings. Password-only logins keep the Basic fallback. --- locales/ar/common.json | 3 ++- locales/ca/common.json | 3 ++- locales/cs/common.json | 3 ++- locales/da/common.json | 3 ++- locales/de/common.json | 3 ++- locales/en/common.json | 3 ++- locales/es/common.json | 3 ++- locales/fa/common.json | 3 ++- locales/fr/common.json | 3 ++- locales/he/common.json | 3 ++- locales/hu/common.json | 3 ++- locales/it/common.json | 3 ++- locales/ja/common.json | 3 ++- locales/ko/common.json | 3 ++- locales/lv/common.json | 1 + locales/mn/common.json | 3 ++- locales/nb/common.json | 3 ++- locales/nl/common.json | 3 ++- locales/pl/common.json | 3 ++- locales/pt/common.json | 3 ++- locales/ro/common.json | 3 ++- locales/ru/common.json | 3 ++- locales/sk/common.json | 3 ++- locales/tr/common.json | 3 ++- locales/uk/common.json | 3 ++- locales/zh-TW/common.json | 3 ++- locales/zh/common.json | 3 ++- stores/__tests__/auth-store-lite.test.ts | 27 ++++++++++++++++++++++++ stores/auth-store.ts | 12 ++++++++++- 29 files changed, 91 insertions(+), 27 deletions(-) diff --git a/locales/ar/common.json b/locales/ar/common.json index 1f39cc695..8d719aa4c 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -24,7 +24,8 @@ "generic": "حدث خطأ غير متوقع. إذا استمرت المشكلة، تواصل مع المسؤول.", "totp_required": "مطلوب رمز مصادقة ثنائية. يرجى إدخال الرمز أدناه.", "totp_invalid": "رمز المصادقة غير صحيح. تحقق من تطبيق المصادقة وحاول مرة أخرى.", - "oauth_discovery_failed": "تسجيل الدخول الموحد (SSO) مفعّل، لكن تعذّر الوصول إلى مزوّد الهوية. تحقق من إعدادات OAuth." + "oauth_discovery_failed": "تسجيل الدخول الموحد (SSO) مفعّل، لكن تعذّر الوصول إلى مزوّد الهوية. تحقق من إعدادات OAuth.", + "token_exchange_failed": "تم قبول كلمة المرور والرمز، لكن خادم البريد رفض بدء جلسة لهذا التطبيق. اطلب من المسؤول التحقق من إعدادات عميل OAuth." }, "show_password": "إظهار كلمة المرور", "hide_password": "إخفاء كلمة المرور", diff --git a/locales/ca/common.json b/locales/ca/common.json index 24b353f79..0c48ee741 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -24,7 +24,8 @@ "generic": "S'ha produït un error inesperat. Si el problema persisteix, contacteu amb l'administrador.", "totp_required": "Cal un codi d'autenticació de dos factors. Introduïu-lo a continuació.", "totp_invalid": "El codi d'autenticació no és vàlid. Comproveu l'aplicació d'autenticació i torneu-ho a provar.", - "oauth_discovery_failed": "El SSO està activat, però no s'ha pogut contactar amb el proveïdor d'identitat. Comproveu la configuració d'OAuth." + "oauth_discovery_failed": "El SSO està activat, però no s'ha pogut contactar amb el proveïdor d'identitat. Comproveu la configuració d'OAuth.", + "token_exchange_failed": "La contrasenya i el codi s'han acceptat, però el servidor de correu s'ha negat a iniciar una sessió per a aquesta aplicació. Demaneu a l'administrador que revisi la configuració del client OAuth." }, "show_password": "Mostra la contrasenya", "hide_password": "Amaga la contrasenya", diff --git a/locales/cs/common.json b/locales/cs/common.json index 8057c0884..7ddd19968 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -24,7 +24,8 @@ "generic": "Došlo k neočekávané chybě. Pokud problém přetrvává, kontaktujte správce.", "totp_required": "Je vyžadován kód dvoufázového ověření. Zadejte kód níže.", "totp_invalid": "Neplatný ověřovací kód. Zkontrolujte svou ověřovací aplikaci a zkuste to znovu.", - "oauth_discovery_failed": "SSO je povoleno, ale nelze se připojit k poskytovateli identity. Zkontrolujte konfiguraci OAuth." + "oauth_discovery_failed": "SSO je povoleno, ale nelze se připojit k poskytovateli identity. Zkontrolujte konfiguraci OAuth.", + "token_exchange_failed": "Heslo a kód byly přijaty, ale poštovní server odmítl pro tuto aplikaci zahájit relaci. Požádejte správce o kontrolu nastavení klienta OAuth." }, "show_password": "Zobrazit heslo", "hide_password": "Skrýt heslo", diff --git a/locales/da/common.json b/locales/da/common.json index f6f65a0e8..addd4a317 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -24,7 +24,8 @@ "generic": "Der opstod en uventet fejl. Kontakt din administrator, hvis problemet fortsætter.", "totp_required": "En to-faktor-godkendelseskode er påkrævet. Indtast din kode nedenfor.", "totp_invalid": "Ugyldig godkendelseskode. Kontrollér din autentificeringsapp og prøv igen.", - "oauth_discovery_failed": "SSO er aktiveret, men identitetsudbyderen kunne ikke nås. Kontrollér din OAuth-konfiguration." + "oauth_discovery_failed": "SSO er aktiveret, men identitetsudbyderen kunne ikke nås. Kontrollér din OAuth-konfiguration.", + "token_exchange_failed": "Din adgangskode og kode blev accepteret, men mailserveren nægtede at starte en session for denne app. Bed din administrator om at kontrollere OAuth-klientindstillingerne." }, "show_password": "Vis adgangskode", "hide_password": "Skjul adgangskode", diff --git a/locales/de/common.json b/locales/de/common.json index 8b515b333..aadb47176 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -24,7 +24,8 @@ "generic": "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", "totp_required": "Ein Zwei-Faktor-Authentifizierungscode ist erforderlich. Bitte geben Sie Ihren Code unten ein.", "totp_invalid": "Ungültiger Authentifizierungscode. Überprüfen Sie Ihre Authenticator-App.", - "oauth_discovery_failed": "SSO ist aktiviert, aber der Identitätsanbieter ist nicht erreichbar. Überprüfen Sie Ihre OAuth-Konfiguration." + "oauth_discovery_failed": "SSO ist aktiviert, aber der Identitätsanbieter ist nicht erreichbar. Überprüfen Sie Ihre OAuth-Konfiguration.", + "token_exchange_failed": "Passwort und Code wurden akzeptiert, aber der Mailserver hat für diese App keine Sitzung gestartet. Bitten Sie Ihre Administration, die OAuth-Client-Einstellungen zu prüfen." }, "show_password": "Passwort anzeigen", "hide_password": "Passwort verbergen", diff --git a/locales/en/common.json b/locales/en/common.json index 0a18fd029..a13e735ce 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -24,7 +24,8 @@ "generic": "An unexpected error occurred. If this persists, contact your administrator.", "totp_required": "A two-factor authentication code is required. Please enter your code below.", "totp_invalid": "Invalid authentication code. Please check your authenticator app and try again.", - "oauth_discovery_failed": "SSO is enabled but the identity provider could not be reached. Check your OAuth configuration." + "oauth_discovery_failed": "SSO is enabled but the identity provider could not be reached. Check your OAuth configuration.", + "token_exchange_failed": "Your password and code were accepted, but the mail server refused to start a session for this app. Ask your administrator to check its OAuth client settings." }, "show_password": "Show password", "hide_password": "Hide password", diff --git a/locales/es/common.json b/locales/es/common.json index f9a5631e8..4a6fca1a9 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -24,7 +24,8 @@ "generic": "Ocurrió un error. Por favor, inténtelo de nuevo.", "totp_required": "Se requiere un código de autenticación de dos factores. Introduce tu código a continuación.", "totp_invalid": "Código de autenticación inválido. Verifica tu aplicación de autenticación.", - "oauth_discovery_failed": "SSO está habilitado pero no se pudo contactar al proveedor de identidad. Verifica tu configuración OAuth." + "oauth_discovery_failed": "SSO está habilitado pero no se pudo contactar al proveedor de identidad. Verifica tu configuración OAuth.", + "token_exchange_failed": "Tu contraseña y tu código fueron aceptados, pero el servidor de correo se negó a iniciar una sesión para esta aplicación. Pide a tu administrador que revise la configuración de su cliente OAuth." }, "show_password": "Mostrar contraseña", "hide_password": "Ocultar contraseña", diff --git a/locales/fa/common.json b/locales/fa/common.json index b36514e90..007a02616 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -24,7 +24,8 @@ "generic": "خطای غیرمنتظره‌ای رخ داد. اگر ادامه داشت با مدیر سیستم تماس بگیرید.", "totp_required": "کد احراز هویت دو مرحله‌ای لازم است.", "totp_invalid": "کد احراز هویت نامعتبر است. برنامه authenticator خود را بررسی کنید.", - "oauth_discovery_failed": "ورود یکپارچه فعال است اما ارائه‌دهنده هویت در دسترس نیست." + "oauth_discovery_failed": "ورود یکپارچه فعال است اما ارائه‌دهنده هویت در دسترس نیست.", + "token_exchange_failed": "رمز عبور و کد پذیرفته شد، اما سرور ایمیل از شروع نشست برای این برنامه خودداری کرد. از مدیر سیستم بخواهید تنظیمات کلاینت OAuth را بررسی کند." }, "show_password": "نمایش رمز عبور", "hide_password": "مخفی کردن رمز عبور", diff --git a/locales/fr/common.json b/locales/fr/common.json index 8a3c36efa..9b24ea339 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -24,7 +24,8 @@ "generic": "Une erreur s'est produite. Veuillez réessayer.", "totp_required": "Un code d'authentification à deux facteurs est requis. Veuillez saisir votre code ci-dessous.", "totp_invalid": "Code d'authentification invalide. Vérifiez votre application d'authentification.", - "oauth_discovery_failed": "Le SSO est activé mais le fournisseur d'identité est injoignable. Vérifiez votre configuration OAuth." + "oauth_discovery_failed": "Le SSO est activé mais le fournisseur d'identité est injoignable. Vérifiez votre configuration OAuth.", + "token_exchange_failed": "Votre mot de passe et votre code ont été acceptés, mais le serveur de messagerie a refusé d'ouvrir une session pour cette application. Demandez à votre administrateur de vérifier les paramètres de son client OAuth." }, "show_password": "Afficher le mot de passe", "hide_password": "Masquer le mot de passe", diff --git a/locales/he/common.json b/locales/he/common.json index 13f034d84..ea21b5482 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -21,7 +21,8 @@ "generic": "אירעה שגיאה בלתי צפויה. אם זה נמשך, פנה למנהל המערכת שלך.", "totp_required": "נדרש קוד אימות דו-גורמי. אנא הזן את הקוד שלך למטה.", "totp_invalid": "קוד אימות לא חוקי. בדוק את אפליקציית המאמת שלך ונסה שוב.", - "oauth_discovery_failed": "SSO מופעל אך לא ניתן היה להשיג את ספק הזהות. בדוק את תצורת ה-OAuth שלך." + "oauth_discovery_failed": "SSO מופעל אך לא ניתן היה להשיג את ספק הזהות. בדוק את תצורת ה-OAuth שלך.", + "token_exchange_failed": "הסיסמה והקוד התקבלו, אך שרת הדואר סירב לפתוח הפעלה עבור האפליקציה הזו. בקשו ממנהל המערכת לבדוק את הגדרות לקוח ה-OAuth." }, "show_password": "הצג סיסמה", "hide_password": "הסתר את הסיסמה", diff --git a/locales/hu/common.json b/locales/hu/common.json index 6fc1998a9..0f6833a8e 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -24,7 +24,8 @@ "generic": "Váratlan hiba történt. Ha a probléma továbbra is fennáll, vedd fel a kapcsolatot a rendszergazdával.", "totp_required": "Kétfaktoros hitelesítési kód szükséges. Kérjük, add meg a kódot lent.", "totp_invalid": "Érvénytelen hitelesítési kód. Ellenőrizd a hitelesítő alkalmazásod és próbáld újra.", - "oauth_discovery_failed": "SSO engedélyezve van, de az identitásszolgáltató nem érhető el. Ellenőrizd az OAuth konfigurációt." + "oauth_discovery_failed": "SSO engedélyezve van, de az identitásszolgáltató nem érhető el. Ellenőrizd az OAuth konfigurációt.", + "token_exchange_failed": "A jelszót és a kódot elfogadtuk, de a levelezőszerver nem indított munkamenetet ehhez az alkalmazáshoz. Kérje meg a rendszergazdát, hogy ellenőrizze az OAuth-kliens beállításait." }, "show_password": "Jelszó megjelenítése", "hide_password": "Jelszó elrejtése", diff --git a/locales/it/common.json b/locales/it/common.json index 452932d8e..a0f54bde7 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -24,7 +24,8 @@ "generic": "Si è verificato un errore. Riprova.", "totp_required": "È richiesto un codice di autenticazione a due fattori. Inserisci il codice qui sotto.", "totp_invalid": "Codice di autenticazione non valido. Controlla la tua app di autenticazione.", - "oauth_discovery_failed": "SSO è abilitato ma il provider di identità non è raggiungibile. Controlla la configurazione OAuth." + "oauth_discovery_failed": "SSO è abilitato ma il provider di identità non è raggiungibile. Controlla la configurazione OAuth.", + "token_exchange_failed": "Password e codice sono stati accettati, ma il server di posta ha rifiutato di avviare una sessione per questa app. Chiedi all'amministratore di controllare le impostazioni del client OAuth." }, "show_password": "Mostra password", "hide_password": "Nascondi password", diff --git a/locales/ja/common.json b/locales/ja/common.json index 200a70360..3bfab1019 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -24,7 +24,8 @@ "generic": "エラーが発生しました。もう一度お試しください。", "totp_required": "二要素認証コードが必要です。以下にコードを入力してください。", "totp_invalid": "認証コードが無効です。認証アプリを確認してください。", - "oauth_discovery_failed": "SSOは有効ですが、IDプロバイダーに接続できません。OAuth設定を確認してください。" + "oauth_discovery_failed": "SSOは有効ですが、IDプロバイダーに接続できません。OAuth設定を確認してください。", + "token_exchange_failed": "パスワードとコードは受け付けられましたが、メールサーバーがこのアプリのセッション開始を拒否しました。管理者に OAuth クライアントの設定を確認してもらってください。" }, "show_password": "パスワードを表示", "hide_password": "パスワードを隠す", diff --git a/locales/ko/common.json b/locales/ko/common.json index 5cb17ed48..641add449 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -24,7 +24,8 @@ "generic": "예상치 못한 오류가 발생했어요. 계속되면 관리자에게 문의해 주세요.", "totp_required": "2단계 인증 코드가 필요해요. 아래에 코드를 입력해 주세요.", "totp_invalid": "인증 코드가 올바르지 않아요. 인증 앱을 확인하고 다시 시도해 주세요.", - "oauth_discovery_failed": "SSO가 활성화되어 있지만 ID 공급자에 연결할 수 없어요. OAuth 설정을 확인해 주세요." + "oauth_discovery_failed": "SSO가 활성화되어 있지만 ID 공급자에 연결할 수 없어요. OAuth 설정을 확인해 주세요.", + "token_exchange_failed": "비밀번호와 코드는 확인되었지만 메일 서버가 이 앱의 세션 시작을 거부했습니다. 관리자에게 OAuth 클라이언트 설정을 확인해 달라고 요청하세요." }, "show_password": "비밀번호 표시", "hide_password": "비밀번호 숨기기", diff --git a/locales/lv/common.json b/locales/lv/common.json index 4ea8ef650..5e228f6e9 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -24,6 +24,7 @@ "generic": "Notikusi neparedzēta kļūda. Ja tā atkārtojas, sazinieties ar administratoru.", "totp_invalid": "Nepareizs autentifikācijas kods. Pārbaudiet lietotni un mēģiniet vēlreiz.", "oauth_discovery_failed": "SSO ir iespējots, bet autentifikācijas pakalpojums nav pieejams. Pārbaudiet OAuth iestatījumus.", + "token_exchange_failed": "Parole un kods tika pieņemti, taču pasta serveris atteicās sākt sesiju šai lietotnei. Lūdziet administratoram pārbaudīt OAuth klienta iestatījumus.", "totp_required": "Nepieciešams divu faktoru autentifikācijas kods. Lūdzu, ievadiet kodu zemāk." }, "show_password": "Rādīt paroli", diff --git a/locales/mn/common.json b/locales/mn/common.json index 64d84078f..a517c81cd 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -24,7 +24,8 @@ "generic": "Санамсаргүй алдаа гарлаа. Хэрэв давтагдсаар байвал админтайгаа холбогдоно уу.", "totp_required": "Хоёр шаттай баталгаажуулалтын код шаардлагатай. Доор кодоо оруулна уу.", "totp_invalid": "Баталгаажуулалтын код буруу байна. Authenticator апп-аа шалгаад дахин оролдоно уу.", - "oauth_discovery_failed": "SSO идэвхтэй боловч identity provider-д хүрч чадсангүй. OAuth тохиргоогоо шалгана уу." + "oauth_discovery_failed": "SSO идэвхтэй боловч identity provider-д хүрч чадсангүй. OAuth тохиргоогоо шалгана уу.", + "token_exchange_failed": "Нууц үг болон код зөв байсан ч шуудангийн сервер энэ аппад сешн эхлүүлэхээс татгалзлаа. Админаасаа OAuth клиентийн тохиргоог шалгахыг хүснэ үү." }, "show_password": "Нууц үг харуулах", "hide_password": "Нууц үг нуух", diff --git a/locales/nb/common.json b/locales/nb/common.json index efe424d03..565ecae5f 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -24,7 +24,8 @@ "generic": "Det oppstod en uventet feil. Kontakt administratoren din hvis feilen vedvarer.", "totp_required": "Du må skrive inn en tofaktorautentiseringskode nedenfor.", "totp_invalid": "Ugyldig autentiseringskode. Sjekk autentiseringsappen din og prøv igjen.", - "oauth_discovery_failed": "SSO er aktivert, men identitetsleverandøren kunne ikke nås. Sjekk OAuth-konfigurasjonen." + "oauth_discovery_failed": "SSO er aktivert, men identitetsleverandøren kunne ikke nås. Sjekk OAuth-konfigurasjonen.", + "token_exchange_failed": "Passordet og koden ble godtatt, men e-postserveren nektet å starte en økt for denne appen. Be administratoren din om å sjekke OAuth-klientinnstillingene." }, "show_password": "Vis passord", "hide_password": "Skjul passord", diff --git a/locales/nl/common.json b/locales/nl/common.json index 1591de8e2..5dceb596d 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -24,7 +24,8 @@ "generic": "Er is een fout opgetreden. Probeer het opnieuw.", "totp_required": "Een tweefactorauthenticatiecode is vereist. Voer uw code hieronder in.", "totp_invalid": "Ongeldige authenticatiecode. Controleer uw authenticator-app.", - "oauth_discovery_failed": "SSO is ingeschakeld maar de identiteitsprovider is niet bereikbaar. Controleer uw OAuth-configuratie." + "oauth_discovery_failed": "SSO is ingeschakeld maar de identiteitsprovider is niet bereikbaar. Controleer uw OAuth-configuratie.", + "token_exchange_failed": "Uw wachtwoord en code zijn geaccepteerd, maar de mailserver weigerde een sessie voor deze app te starten. Vraag uw beheerder de OAuth-clientinstellingen te controleren." }, "show_password": "Wachtwoord tonen", "hide_password": "Wachtwoord verbergen", diff --git a/locales/pl/common.json b/locales/pl/common.json index c4e62bdff..f83b1018c 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -24,7 +24,8 @@ "generic": "Wystąpił nieoczekiwany błąd. Jeśli problem się powtarza, skontaktuj się z administratorem.", "totp_required": "Wymagany jest kod uwierzytelniania dwuskładnikowego. Wprowadź kod poniżej.", "totp_invalid": "Nieprawidłowy kod uwierzytelniania. Sprawdź aplikację uwierzytelniającą i spróbuj ponownie.", - "oauth_discovery_failed": "SSO jest włączone, ale nie można połączyć się z dostawcą tożsamości. Sprawdź konfigurację OAuth." + "oauth_discovery_failed": "SSO jest włączone, ale nie można połączyć się z dostawcą tożsamości. Sprawdź konfigurację OAuth.", + "token_exchange_failed": "Hasło i kod zostały zaakceptowane, ale serwer poczty odmówił rozpoczęcia sesji dla tej aplikacji. Poproś administratora o sprawdzenie ustawień klienta OAuth." }, "show_password": "Pokaż hasło", "hide_password": "Ukryj hasło", diff --git a/locales/pt/common.json b/locales/pt/common.json index 714bedf57..8af09f2db 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -24,7 +24,8 @@ "generic": "Ocorreu um erro. Por favor, tente novamente.", "totp_required": "É necessário um código de autenticação de dois fatores. Insira seu código abaixo.", "totp_invalid": "Código de autenticação inválido. Verifique seu aplicativo de autenticação.", - "oauth_discovery_failed": "SSO está ativado mas o provedor de identidade não pôde ser contatado. Verifique sua configuração OAuth." + "oauth_discovery_failed": "SSO está ativado mas o provedor de identidade não pôde ser contatado. Verifique sua configuração OAuth.", + "token_exchange_failed": "Sua senha e o código foram aceitos, mas o servidor de e-mail se recusou a iniciar uma sessão para este aplicativo. Peça ao administrador que verifique as configurações do cliente OAuth." }, "show_password": "Mostrar senha", "hide_password": "Ocultar senha", diff --git a/locales/ro/common.json b/locales/ro/common.json index 3524683e6..5b72a115e 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -24,7 +24,8 @@ "generic": "A apărut o eroare neașteptată. Dacă problema persistă, contactați administratorul.", "totp_required": "Este necesar un cod de autentificare în doi pași. Vă rugăm să introduceți codul mai jos.", "totp_invalid": "Cod de autentificare invalid. Vă rugăm să verificați aplicația de autentificare și să încercați din nou.", - "oauth_discovery_failed": "SSO este activată, dar furnizorul de identitate nu a putut fi contactat. Verificați configurația OAuth." + "oauth_discovery_failed": "SSO este activată, dar furnizorul de identitate nu a putut fi contactat. Verificați configurația OAuth.", + "token_exchange_failed": "Parola și codul au fost acceptate, dar serverul de e-mail a refuzat să pornească o sesiune pentru această aplicație. Cereți administratorului să verifice setările clientului OAuth." }, "show_password": "Afișează parola", "hide_password": "Ascunde parola", diff --git a/locales/ru/common.json b/locales/ru/common.json index 33e259245..43f88167e 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -24,7 +24,8 @@ "generic": "Произошла непредвиденная ошибка. Если она повторяется, обратитесь к администратору.", "totp_required": "Требуется код двухфакторной аутентификации. Пожалуйста, введите его ниже.", "totp_invalid": "Неверный код аутентификации. Проверьте приложение аутентификатора и повторите попытку.", - "oauth_discovery_failed": "SSO включён, но поставщик удостоверений недоступен. Проверьте настройки OAuth." + "oauth_discovery_failed": "SSO включён, но поставщик удостоверений недоступен. Проверьте настройки OAuth.", + "token_exchange_failed": "Пароль и код приняты, но почтовый сервер отказался открыть сеанс для этого приложения. Попросите администратора проверить настройки OAuth-клиента." }, "show_password": "Показать пароль", "hide_password": "Скрыть пароль", diff --git a/locales/sk/common.json b/locales/sk/common.json index 433a233df..98128d621 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -24,7 +24,8 @@ "generic": "Vyskytla sa neočakávaná chyba. Ak problém pretrváva, kontaktujte správcu.", "totp_required": "Je vyžadovaný kód dvojfaktorového overenia. Zadajte kód nižšie.", "totp_invalid": "Neplatný overovací kód. Skontrolujte svoju overovaciu aplikáciu a skúste znova.", - "oauth_discovery_failed": "SSO je povolené, ale poskytovateľa identity sa nepodarilo dosiahnuť. Skontrolujte konfiguráciu OAuth." + "oauth_discovery_failed": "SSO je povolené, ale poskytovateľa identity sa nepodarilo dosiahnuť. Skontrolujte konfiguráciu OAuth.", + "token_exchange_failed": "Heslo a kód boli prijaté, ale poštový server odmietol pre túto aplikáciu spustiť reláciu. Požiadajte správcu o kontrolu nastavení klienta OAuth." }, "show_password": "Zobraziť heslo", "hide_password": "Skryť heslo", diff --git a/locales/tr/common.json b/locales/tr/common.json index 1048d6fb8..930639832 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -24,7 +24,8 @@ "generic": "Beklenmedik bir hata oluştu. Bu sorun devam ederse yöneticinizle iletişime geçin.", "totp_required": "İki faktörlü doğrulama kodu gereklidir. Lütfen kodunuzu aşağıya girin.", "totp_invalid": "Geçersiz doğrulama kodu. Lütfen doğrulama uygulamanızı kontrol edip tekrar deneyin.", - "oauth_discovery_failed": "SSO etkin ancak kimlik sağlayıcısına ulaşılamıyor. OAuth yapılandırmanızı kontrol edin." + "oauth_discovery_failed": "SSO etkin ancak kimlik sağlayıcısına ulaşılamıyor. OAuth yapılandırmanızı kontrol edin.", + "token_exchange_failed": "Parolanız ve kodunuz kabul edildi ancak posta sunucusu bu uygulama için oturum başlatmayı reddetti. Yöneticinizden OAuth istemci ayarlarını kontrol etmesini isteyin." }, "show_password": "Şifreyi göster", "hide_password": "Şifreyi gizle", diff --git a/locales/uk/common.json b/locales/uk/common.json index 828e5c95e..0c9c75322 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -24,7 +24,8 @@ "generic": "Сталася неочікувана помилка. Якщо це не зникне, зверніться до адміністратора.", "totp_required": "Потрібен код двофакторної автентифікації. Будь ласка, введіть свій код нижче.", "totp_invalid": "Недійсний код автентифікації. Перевірте свою програму автентифікації та повторіть спробу.", - "oauth_discovery_failed": "Система єдиного входу ввімкнена, але зв’язатися з постачальником ідентифікаційної інформації не вдалося. Перевірте конфігурацію OAuth." + "oauth_discovery_failed": "Система єдиного входу ввімкнена, але зв’язатися з постачальником ідентифікаційної інформації не вдалося. Перевірте конфігурацію OAuth.", + "token_exchange_failed": "Пароль і код прийнято, але поштовий сервер відмовився відкрити сеанс для цього застосунку. Попросіть адміністратора перевірити налаштування OAuth-клієнта." }, "show_password": "Показати пароль", "hide_password": "Приховати пароль", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 2708d0616..c43188116 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -24,7 +24,8 @@ "generic": "發生意外錯誤。如果問題持續存在,請聯絡管理員。", "totp_required": "需要雙因素驗證碼。請在下方輸入驗證碼。", "totp_invalid": "驗證碼無效。請檢查驗證器應用程式後再試一次。", - "oauth_discovery_failed": "SSO 已啟用,但無法連線至身分提供者。請檢查 OAuth 設定。" + "oauth_discovery_failed": "SSO 已啟用,但無法連線至身分提供者。請檢查 OAuth 設定。", + "token_exchange_failed": "密碼與驗證碼已通過,但郵件伺服器拒絕為此應用程式建立工作階段。請聯絡管理員檢查其 OAuth 用戶端設定。" }, "show_password": "顯示密碼", "hide_password": "隱藏密碼", diff --git a/locales/zh/common.json b/locales/zh/common.json index 7a3f224cf..c5c1f0f48 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -24,7 +24,8 @@ "generic": "发生意外错误。如果问题持续存在,请联系管理员。", "totp_required": "需要双重验证代码。请在下方输入验证码。", "totp_invalid": "验证码无效。请检查您的身份验证器应用,然后重试。", - "oauth_discovery_failed": "SSO 已启用,但无法访问身份提供者。检查您的 OAuth 配置。" + "oauth_discovery_failed": "SSO 已启用,但无法访问身份提供者。检查您的 OAuth 配置。", + "token_exchange_failed": "密码和验证码已通过,但邮件服务器拒绝为此应用建立会话。请联系管理员检查其 OAuth 客户端设置。" }, "show_password": "显示密码", "hide_password": "隐藏密码", diff --git a/stores/__tests__/auth-store-lite.test.ts b/stores/__tests__/auth-store-lite.test.ts index f3edff9ce..93019bfb0 100644 --- a/stores/__tests__/auth-store-lite.test.ts +++ b/stores/__tests__/auth-store-lite.test.ts @@ -46,6 +46,8 @@ function stalwartFetch(options: { loginAnswer?: unknown; refreshAnswer?: () => Response; loginStatus?: number; + /** Stalwart's answer to the authorization_code grant (default: tokens). */ + tokenAnswer?: () => Response; /** Advertise an RFC 7009 revocation endpoint in the metadata document. */ revocation?: boolean; } = {}) { @@ -78,6 +80,7 @@ function stalwartFetch(options: { if (params.get('grant_type') === 'refresh_token') { return options.refreshAnswer ? options.refreshAnswer() : jsonResponse({ access_token: 'AT-refreshed', expires_in: 3600, refresh_token: 'RT-2' }); } + if (options.tokenAnswer) return options.tokenAnswer(); return jsonResponse({ access_token: 'AT-1', expires_in: 3600, refresh_token: 'RT-1' }); } throw new Error(`unexpected fetch ${url}`); @@ -259,6 +262,30 @@ describe('auth-store in the static Lite build', () => { expect(connectSpy).not.toHaveBeenCalled(); }); + // Stalwart answers `invalid_client` at /auth/token when the OAuth client has + // a secret, which Lite cannot send. Retrying `password$totp` over Basic + // only popped the browser's Basic-auth dialog and then blamed the code. + it('reports a refused token exchange after a TOTP login instead of retrying over Basic', async () => { + const { calls } = stalwartFetch({ tokenAnswer: () => jsonResponse({ error: 'invalid_client' }, 400) }); + + const ok = await useAuthStore.getState().login(SERVER, 'alice', 'pw', '123456', true); + + expect(ok).toBe(false); + expect(useAuthStore.getState().error).toBe('token_exchange_failed'); + expect(calls).toEqual([`POST ${SERVER}/api/auth`, `POST ${SERVER}/auth/token`]); + expect(connectSpy).not.toHaveBeenCalled(); + expect(liteStorageKeys()).toEqual([]); + }); + + it('still falls back to Basic for a password-only login when the token exchange is refused', async () => { + stalwartFetch({ tokenAnswer: () => jsonResponse({ error: 'invalid_client' }, 400) }); + + const ok = await useAuthStore.getState().login(SERVER, 'alice', 'pw', undefined, true); + + expect(ok).toBe(true); + expect(useAuthStore.getState().client?.getAuthHeader()).toBe(`Basic ${btoa('alice:pw')}`); + }); + it('keeps no password in web storage when token login exists but failed', async () => { stalwartFetch({ loginStatus: 500 }); diff --git a/stores/auth-store.ts b/stores/auth-store.ts index b1c2eb4c3..3d9fde2e6 100644 --- a/stores/auth-store.ts +++ b/stores/auth-store.ts @@ -138,6 +138,7 @@ export interface LogoutOptions { const ERROR_PATTERNS: Array<{ key: string; matches: string[] }> = [ { key: 'cors_blocked', matches: ['CORS_ERROR'] }, { key: 'totp_required', matches: ['TOTP_REQUIRED'] }, + { key: 'token_exchange_failed', matches: ['TOKEN_EXCHANGE_FAILED'] }, { key: 'invalid_credentials', matches: ['Invalid username or password', '401', 'Unauthorized'] }, { key: 'connection_failed', matches: ['network', 'Failed to fetch', 'NetworkError', 'ECONNREFUSED', 'Load failed', 'cancelled'] }, { key: 'server_error', matches: ['500', '502', '503', '504', 'Internal Server Error', 'Service Unavailable'] }, @@ -1321,10 +1322,19 @@ export const useAuthStore = create()( if (errorBody?.error === 'totp_required') { throw new Error('TOTP_REQUIRED'); } + // The server took the password and code but would not issue + // tokens (in Lite: an OAuth client with a secret, which a + // browser cannot send). A server with the structured login + // endpoint is 0.16+ and refuses `password$totp` over Basic, + // so the legacy fallback would only pop the browser's + // Basic-auth dialog and then report a wrong code. + if (totp && errorBody?.error === 'token_exchange_failed') { + throw new Error('TOKEN_EXCHANGE_FAILED'); + } debug.warn('auth', 'TOTP login exchange failed, trying legacy basic auth:', tokenRes.status, errorBody); } } catch (err) { - if (err instanceof Error && err.message === 'TOTP_REQUIRED') throw err; + if (err instanceof Error && (err.message === 'TOTP_REQUIRED' || err.message === 'TOKEN_EXCHANGE_FAILED')) throw err; // Unreachable (a server without /api/auth rarely answers CORS). tokenLoginUnavailable = true; debug.warn('auth', 'TOTP login exchange error, trying legacy basic auth:', err);