diff --git a/app/Exceptions/OauthLoginException.php b/app/Exceptions/OauthLoginException.php new file mode 100644 index 0000000000..6120ba3ef4 --- /dev/null +++ b/app/Exceptions/OauthLoginException.php @@ -0,0 +1,19 @@ +logCallbackFailure($provider, $e); - $errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback'; + $errorCode = match (true) { + $e instanceof OauthLoginException => $e->userMessageKey, + $e instanceof HttpException => 'auth.failed.oauth', + default => 'auth.failed.callback', + }; return redirect()->route('login')->withErrors([__($errorCode)]); } diff --git a/app/Services/Auth/OauthLoginService.php b/app/Services/Auth/OauthLoginService.php index f3b5b166e3..8a79ed157f 100644 --- a/app/Services/Auth/OauthLoginService.php +++ b/app/Services/Auth/OauthLoginService.php @@ -3,6 +3,7 @@ namespace App\Services\Auth; use App\Auth\Oidc\OidcUser; +use App\Exceptions\OauthLoginException; use App\Models\OauthIdentity; use App\Models\OauthSetting; use App\Models\Team; @@ -130,20 +131,22 @@ class OauthLoginService } $user = User::whereEmail($email)->first(); - if ($user?->oauthIdentities()->exists()) { - throw new HttpException(403, 'OAuth identity cannot be linked to this account'); - } // Before OAuth identities existed, OAuth sign-in matched users by email only. // Users from that time keep signing in without email delivery, so their - // first identity links without a provider verification claim. + // first identity links without a provider verification claim. This check + // runs first, so an unverified email cannot reveal a linked account. if ($user?->created_before_oauth_identities !== true && ! $this->hasVerifiedEmail($provider, $rawClaims, $email)) { - throw new HttpException(403, 'OAuth provider did not verify the email address'); + throw new OauthLoginException('OAuth provider did not verify the email address', 'auth.failed.oauth_email_unverified'); + } + + if ($user?->oauthIdentities()->exists()) { + throw new OauthLoginException('OAuth identity cannot be linked to this account', 'auth.failed.oauth_already_linked'); } if (! $user) { if (! $this->canCreateUser($oauthSetting)) { - throw new HttpException(403, 'Registration is disabled'); + throw new OauthLoginException('Registration is disabled', 'auth.registration_disabled'); } $user = $this->createUser($oauthUser->name ?: $email, $email, $oauthSetting); @@ -202,7 +205,7 @@ class OauthLoginService } if ($oauthSetting->require_email_verified && ! $emailVerified) { - throw new HttpException(403, 'OIDC provider did not verify the email address'); + throw new OauthLoginException('OIDC provider did not verify the email address', 'auth.failed.oauth_email_unverified'); } $rawClaims = is_array($oauthUser->user ?? null) ? $oauthUser->user : []; @@ -234,16 +237,16 @@ class OauthLoginService // guard is independent of the require_email_verified toggle, which // only governs the broader login flow. if ($user && ! $emailVerified) { - throw new HttpException(403, 'OIDC provider must verify the email address before linking to an existing account'); + throw new OauthLoginException('OIDC provider must verify the email address before linking to an existing account', 'auth.failed.oauth_email_unverified'); } if ($user?->oauthIdentities()->exists()) { - throw new HttpException(403, 'OAuth identity cannot be linked to this account'); + throw new OauthLoginException('OAuth identity cannot be linked to this account', 'auth.failed.oauth_already_linked'); } if (! $user) { if (! $this->canCreateUser($oauthSetting)) { - throw new HttpException(403, 'Registration is disabled'); + throw new OauthLoginException('Registration is disabled', 'auth.registration_disabled'); } $user = $this->createUser($oauthUser->name ?: $email, $email, $oauthSetting); diff --git a/lang/ar.json b/lang/ar.json index c966cc6869..c3aa8aa18d 100644 --- a/lang/ar.json +++ b/lang/ar.json @@ -21,6 +21,9 @@ "auth.reset_password": "إعادة تعيين كلمة المرور", "auth.failed": "هذه البيانات لا تتطابق مع سجلاتنا.", "auth.failed.callback": "فشل في معالجة استدعاء من مزود تسجيل الدخول.", + "auth.failed.oauth": "فشل تسجيل الدخول باستخدام هذا المزود. تواصل مع المسؤول إذا استمرت المشكلة.", + "auth.failed.oauth_already_linked": "هذا الحساب يستخدم بالفعل طريقة تسجيل دخول أخرى. سجّل الدخول باستخدام تلك الطريقة.", + "auth.failed.oauth_email_unverified": "لم يتحقق مزود تسجيل الدخول من بريدك الإلكتروني. تحقق منه لدى المزود ثم حاول مرة أخرى.", "auth.failed.password": "كلمة المرور المقدمة غير صحيحة.", "auth.failed.email": "لا يمكننا العثور على مستخدم بهذا البريد الإلكتروني.", "auth.throttle": "عدد محاولات تسجيل الدخول كثيرة جدًا. يرجى المحاولة مرة أخرى في :seconds ثانية.", diff --git a/lang/az.json b/lang/az.json index 85cee75899..9ac7a03b0e 100644 --- a/lang/az.json +++ b/lang/az.json @@ -21,6 +21,9 @@ "auth.reset_password": "Şifrənin bərpası", "auth.failed": "Bu məlumatlar bizim qeydlərimizlə uyğun gəlmir.", "auth.failed.callback": "Giriş təminatçısından geri çağırma işlənə bilmədi.", + "auth.failed.oauth": "Bu təminatçı ilə giriş uğursuz oldu. Problem davam edərsə, administratorla əlaqə saxlayın.", + "auth.failed.oauth_already_linked": "Bu hesab artıq başqa giriş üsulundan istifadə edir. Həmin üsulla daxil olun.", + "auth.failed.oauth_email_unverified": "Giriş təminatçınız e-poçt ünvanınızı təsdiqləməyib. Onu təminatçıda təsdiqləyin və yenidən cəhd edin.", "auth.failed.password": "Daxil etdiyiniz şifrə yanlışdır.", "auth.failed.email": "Bu e-poçt ünvanı ilə istifadəçi tapılmadı.", "auth.throttle": "Çox sayda uğursuz giriş cəhdi. Zəhmət olmasa :seconds saniyə sonra yenidən cəhd edin.", diff --git a/lang/cs.json b/lang/cs.json index 9e5d2c44e5..b4bddfba4f 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -20,6 +20,9 @@ "auth.reset_password": "Obnovit heslo", "auth.failed": "Tyto údaje neodpovídají našim záznamům.", "auth.failed.callback": "Nepodařilo se zpracovat zpětné volání od poskytovatele přihlášení.", + "auth.failed.oauth": "Přihlášení přes tohoto poskytovatele se nezdařilo. Pokud problém přetrvává, kontaktujte správce.", + "auth.failed.oauth_already_linked": "Tento účet již používá jinou metodu přihlášení. Přihlaste se touto metodou.", + "auth.failed.oauth_email_unverified": "Váš poskytovatel přihlášení neověřil vaši e-mailovou adresu. Ověřte ji u poskytovatele a zkuste to znovu.", "auth.failed.password": "Zadané heslo je nesprávné.", "auth.failed.email": "Nemůžeme najít uživatele s touto e-mailovou adresou.", "auth.throttle": "Příliš mnoho pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.", diff --git a/lang/de.json b/lang/de.json index cbc2237a75..6e354c7cc5 100644 --- a/lang/de.json +++ b/lang/de.json @@ -22,6 +22,9 @@ "auth.reset_password": "Passwort zurücksetzen", "auth.failed": "Diese Anmeldedaten wurden nicht gefunden.", "auth.failed.callback": "Fehlerhafte Verarbeitung der Antwort des Anmeldeanbieters.", + "auth.failed.oauth": "Die Anmeldung über diesen Anbieter ist fehlgeschlagen. Wende dich an deinen Administrator, wenn das Problem weiter besteht.", + "auth.failed.oauth_already_linked": "Dieses Konto verwendet bereits eine andere Anmeldemethode. Melde dich mit dieser Methode an.", + "auth.failed.oauth_email_unverified": "Dein Anmeldeanbieter hat deine E-Mail-Adresse nicht bestätigt. Bestätige sie beim Anbieter und versuche es erneut.", "auth.failed.password": "Das angegebene Passwort ist inkorrekt.", "auth.failed.email": "Wir können keinen Benutzer mit dieser E-Mail Adresse finden.", "auth.throttle": "Zu viele Anmeldeversuche. Bitte versuche es in :seconds Sekunden erneut.", diff --git a/lang/en.json b/lang/en.json index b97a10d629..f9d1bfc9ec 100644 --- a/lang/en.json +++ b/lang/en.json @@ -24,6 +24,9 @@ "auth.reset_password": "Reset password", "auth.failed": "These credentials do not match our records.", "auth.failed.callback": "Failed to process callback from login provider.", + "auth.failed.oauth": "Sign-in with this provider failed. Contact your administrator if the problem continues.", + "auth.failed.oauth_already_linked": "This account already uses another sign-in method. Sign in with that method.", + "auth.failed.oauth_email_unverified": "Your sign-in provider did not verify your email address. Verify it with the provider and try again.", "auth.failed.password": "The provided password is incorrect.", "auth.failed.email": "If an account exists with this email address, you will receive a password reset link shortly.", "auth.throttle": "Too many login attempts. Please try again in :seconds seconds.", diff --git a/lang/es.json b/lang/es.json index f56387f051..732e1bf794 100644 --- a/lang/es.json +++ b/lang/es.json @@ -20,6 +20,9 @@ "auth.reset_password": "Cambiar contraseña", "auth.failed": "Las credenciales no coinciden con nuestro registro..", "auth.failed.callback": "Falló el proceso de inicio de sesión con el proveedor.", + "auth.failed.oauth": "El inicio de sesión con este proveedor falló. Contacta a tu administrador si el problema continúa.", + "auth.failed.oauth_already_linked": "Esta cuenta ya usa otro método de inicio de sesión. Inicia sesión con ese método.", + "auth.failed.oauth_email_unverified": "Tu proveedor de inicio de sesión no verificó tu dirección de correo. Verifícala con el proveedor e inténtalo de nuevo.", "auth.failed.password": "La contraseña es incorrecta.", "auth.failed.email": "No encontramos un usuario con ese correo.", "auth.throttle": "Demasiados intentos. Por favor inténtalo en :seconds segundos.", diff --git a/lang/fa.json b/lang/fa.json index ae22ee9462..943dbb7c61 100644 --- a/lang/fa.json +++ b/lang/fa.json @@ -20,6 +20,9 @@ "auth.reset_password": "بازیابی رمز عبور", "auth.failed": "این اطلاعات با سوابق ما مطابقت ندارد.", "auth.failed.callback": "پردازش بازگشت از ارائه‌دهنده ورود با شکست مواجه شد.", + "auth.failed.oauth": "ورود با این ارائه‌دهنده ناموفق بود. اگر مشکل ادامه داشت با مدیر تماس بگیرید.", + "auth.failed.oauth_already_linked": "این حساب از قبل از روش ورود دیگری استفاده می‌کند. با همان روش وارد شوید.", + "auth.failed.oauth_email_unverified": "ارائه‌دهنده ورود شما آدرس ایمیل شما را تأیید نکرده است. آن را نزد ارائه‌دهنده تأیید کنید و دوباره تلاش کنید.", "auth.failed.password": "رمز عبور ارائه شده نادرست است.", "auth.failed.email": "ما نمی توانیم کاربر با آدرس ایمیل مورد نظر را پیدا کنیم.", "auth.throttle": "تعداد تلاش‌های ورود بیش از حد است. لطفاً در :seconds ثانیه دوباره تلاش کنید.", diff --git a/lang/fr.json b/lang/fr.json index d98a1ebc8c..07ccebd68c 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -21,6 +21,9 @@ "auth.reset_password": "Réinitialiser le mot de passe", "auth.failed": "Aucune correspondance n'a été trouvée pour les informations d'identification renseignées.", "auth.failed.callback": "Erreur lors du processus de retour de la plateforme de connexion.", + "auth.failed.oauth": "La connexion avec ce fournisseur a échoué. Contactez votre administrateur si le problème persiste.", + "auth.failed.oauth_already_linked": "Ce compte utilise déjà une autre méthode de connexion. Connectez-vous avec cette méthode.", + "auth.failed.oauth_email_unverified": "Votre fournisseur de connexion n'a pas vérifié votre adresse e-mail. Vérifiez-la auprès du fournisseur et réessayez.", "auth.failed.password": "Le mot de passe renseigné est incorrect.", "auth.failed.email": "Aucun utilisateur avec cette adresse email n'a été trouvé.", "auth.throttle": "Trop de tentatives de connexion. Merci de réessayer dans :seconds secondes.", diff --git a/lang/id.json b/lang/id.json index d85176cdab..0ff9b13508 100644 --- a/lang/id.json +++ b/lang/id.json @@ -21,6 +21,9 @@ "auth.reset_password": "Reset kata sandi", "auth.failed": "Kredensial ini tidak cocok dengan catatan kami.", "auth.failed.callback": "Gagal memproses callback dari penyedia login.", + "auth.failed.oauth": "Login dengan penyedia ini gagal. Hubungi administrator jika masalah berlanjut.", + "auth.failed.oauth_already_linked": "Akun ini sudah menggunakan metode login lain. Login dengan metode tersebut.", + "auth.failed.oauth_email_unverified": "Penyedia login Anda belum memverifikasi alamat email Anda. Verifikasi di penyedia, lalu coba lagi.", "auth.failed.password": "Kata sandi yang diberikan salah.", "auth.failed.email": "Kami tidak dapat menemukan pengguna dengan alamat e-mail tersebut.", "auth.throttle": "Terlalu banyak percobaan login. Silakan coba lagi dalam :seconds detik.", diff --git a/lang/it.json b/lang/it.json index e4c1a9c05c..1904fdcada 100644 --- a/lang/it.json +++ b/lang/it.json @@ -21,6 +21,9 @@ "auth.reset_password": "Reimposta password", "auth.failed": "Queste credenziali non corrispondono ai nostri record.", "auth.failed.callback": "Errore durante l'elaborazione del callback dal provider di accesso.", + "auth.failed.oauth": "L'accesso con questo provider non è riuscito. Contatta l'amministratore se il problema persiste.", + "auth.failed.oauth_already_linked": "Questo account usa già un altro metodo di accesso. Accedi con quel metodo.", + "auth.failed.oauth_email_unverified": "Il tuo provider di accesso non ha verificato il tuo indirizzo email. Verificalo presso il provider e riprova.", "auth.failed.password": "La password fornita non è corretta.", "auth.failed.email": "Non possiamo trovare un utente con questo indirizzo email.", "auth.throttle": "Troppi tentativi di accesso. Per favore riprova tra :seconds secondi.", diff --git a/lang/ja.json b/lang/ja.json index 05987e7ce1..67b0395066 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -20,6 +20,9 @@ "auth.reset_password": "パスワードをリセット", "auth.failed": "これらの資格情報は記録と一致しません。", "auth.failed.callback": "ログインプロバイダーからのコールバックの処理に失敗しました。", + "auth.failed.oauth": "このプロバイダーでのログインに失敗しました。問題が続く場合は管理者に連絡してください。", + "auth.failed.oauth_already_linked": "このアカウントは既に別のログイン方法を使用しています。その方法でログインしてください。", + "auth.failed.oauth_email_unverified": "ログインプロバイダーがメールアドレスを確認していません。プロバイダーで確認してから再度お試しください。", "auth.failed.password": "提供されたパスワードが正しくありません。", "auth.failed.email": "そのメールアドレスのユーザーが見つかりません。", "auth.throttle": "ログイン試行回数が多すぎます。:seconds秒後にもう一度お試しください。", diff --git a/lang/no.json b/lang/no.json index 967bdf606f..707229c9cf 100644 --- a/lang/no.json +++ b/lang/no.json @@ -21,6 +21,9 @@ "auth.reset_password": "Tilbakestill passord", "auth.failed": "Disse legitimasjonene samsvarer ikke med våre registre.", "auth.failed.callback": "Klarte ikke å behandle tilbakekall fra innloggingsleverandør.", + "auth.failed.oauth": "Innlogging med denne leverandøren mislyktes. Kontakt administratoren hvis problemet vedvarer.", + "auth.failed.oauth_already_linked": "Denne kontoen bruker allerede en annen innloggingsmetode. Logg inn med den metoden.", + "auth.failed.oauth_email_unverified": "Innloggingsleverandøren din har ikke bekreftet e-postadressen din. Bekreft den hos leverandøren og prøv igjen.", "auth.failed.password": "Det oppgitte passordet er feil.", "auth.failed.email": "Vi finner ingen bruker med den e-postadressen.", "auth.throttle": "For mange innloggingsforsøk. Vennligst prøv igjen om :seconds sekunder.", diff --git a/lang/pl.json b/lang/pl.json index b05437ac4e..d5b55cd671 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -23,6 +23,9 @@ "auth.reset_password": "Zresetuj hasło", "auth.failed": "Podane dane nie zgadzają się z naszymi rekordami.", "auth.failed.callback": "Nie udało się przeprocesować callbacku od dostawcy logowania.", + "auth.failed.oauth": "Logowanie przez tego dostawcę nie powiodło się. Skontaktuj się z administratorem, jeśli problem będzie się powtarzał.", + "auth.failed.oauth_already_linked": "To konto używa już innej metody logowania. Zaloguj się tą metodą.", + "auth.failed.oauth_email_unverified": "Twój dostawca logowania nie zweryfikował Twojego adresu e-mail. Zweryfikuj go u dostawcy i spróbuj ponownie.", "auth.failed.password": "Podane hasło jest nieprawidłowe.", "auth.failed.email": "Nie znaleziono użytkownika z takim adresem email.", "auth.throttle": "Zbyt wiele prób logowania. Spróbuj ponownie za :seconds sekund.", diff --git a/lang/pt-br.json b/lang/pt-br.json index 83e49fa657..aaa01c536e 100644 --- a/lang/pt-br.json +++ b/lang/pt-br.json @@ -22,6 +22,9 @@ "auth.reset_password": "Redefinir senha", "auth.failed": "Essas credenciais não correspondem aos nossos registros.", "auth.failed.callback": "Falha ao processar o callback do provedor de login.", + "auth.failed.oauth": "O login com este provedor falhou. Entre em contato com o administrador se o problema continuar.", + "auth.failed.oauth_already_linked": "Esta conta já usa outro método de login. Entre com esse método.", + "auth.failed.oauth_email_unverified": "Seu provedor de login não verificou seu endereço de e-mail. Verifique-o no provedor e tente novamente.", "auth.failed.password": "A senha fornecida está incorreta.", "auth.failed.email": "Não encontramos nenhum usuário com esse endereço de e-mail.", "auth.throttle": "Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.", diff --git a/lang/pt.json b/lang/pt.json index 1a48884100..a5dc8b820f 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -22,6 +22,9 @@ "auth.reset_password": "Redefinir senha", "auth.failed": "Essas credenciais não correspondem aos nossos registros.", "auth.failed.callback": "Falha ao processar o callback do provedor de login.", + "auth.failed.oauth": "O início de sessão com este fornecedor falhou. Contacte o administrador se o problema continuar.", + "auth.failed.oauth_already_linked": "Esta conta já usa outro método de início de sessão. Inicie sessão com esse método.", + "auth.failed.oauth_email_unverified": "O seu fornecedor de início de sessão não verificou o seu endereço de email. Verifique-o no fornecedor e tente novamente.", "auth.failed.password": "A senha fornecida está incorreta.", "auth.failed.email": "Não encontramos um usuário com esse endereço de e-mail.", "auth.throttle": "Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.", diff --git a/lang/ro.json b/lang/ro.json index 18028d0870..addce9b712 100644 --- a/lang/ro.json +++ b/lang/ro.json @@ -20,6 +20,9 @@ "auth.reset_password": "Resetare parolă", "auth.failed": "Autentificare nereușită. Vă rugăm să verificați datele introduse.", "auth.failed.callback": "A apărut o eroare în timpul autentificării cu furnizorul extern.", + "auth.failed.oauth": "Autentificarea cu acest furnizor a eșuat. Contactați administratorul dacă problema persistă.", + "auth.failed.oauth_already_linked": "Acest cont folosește deja o altă metodă de autentificare. Autentificați-vă cu acea metodă.", + "auth.failed.oauth_email_unverified": "Furnizorul de autentificare nu v-a verificat adresa de e-mail. Verificați-o la furnizor și încercați din nou.", "auth.failed.password": "Parola furnizată este incorectă.", "auth.failed.email": "Nu putem găsi un utilizator cu această adresă de e-mail.", "auth.throttle": "Prea multe încercări de autentificare. Vă rugăm să încercați din nou în :seconds secunde.", diff --git a/lang/tr.json b/lang/tr.json index 8218f66e0f..ba138d6745 100644 --- a/lang/tr.json +++ b/lang/tr.json @@ -22,6 +22,9 @@ "auth.reset_password": "Şifreyi Sıfırla", "auth.failed": "Bu kimlik bilgileri kayıtlarımızla eşleşmiyor.", "auth.failed.callback": "Giriş sağlayıcıdan gelen istek işlenemedi.", + "auth.failed.oauth": "Bu sağlayıcı ile giriş başarısız oldu. Sorun devam ederse yöneticinize başvurun.", + "auth.failed.oauth_already_linked": "Bu hesap zaten başka bir giriş yöntemi kullanıyor. O yöntemle giriş yapın.", + "auth.failed.oauth_email_unverified": "Giriş sağlayıcınız e-posta adresinizi doğrulamadı. Sağlayıcıda doğrulayın ve tekrar deneyin.", "auth.failed.password": "Sağlanan şifre yanlış.", "auth.failed.email": "Bu e-posta adresiyle bir kullanıcı bulamıyoruz.", "auth.throttle": "Çok fazla giriş denemesi. Lütfen :seconds saniye sonra tekrar deneyin.", diff --git a/lang/vi.json b/lang/vi.json index 76e380477c..a5bde607d8 100644 --- a/lang/vi.json +++ b/lang/vi.json @@ -20,6 +20,9 @@ "auth.reset_password": "Đặt lại mật khẩu", "auth.failed": "Thông tin đăng nhập không khớp với bất kỳ tài khoản nào.", "auth.failed.callback": "Xử lý thông tin từ nhà cung cấp đăng nhập thất bại.", + "auth.failed.oauth": "Đăng nhập bằng nhà cung cấp này thất bại. Hãy liên hệ quản trị viên nếu sự cố vẫn tiếp diễn.", + "auth.failed.oauth_already_linked": "Tài khoản này đã dùng một phương thức đăng nhập khác. Hãy đăng nhập bằng phương thức đó.", + "auth.failed.oauth_email_unverified": "Nhà cung cấp đăng nhập chưa xác minh địa chỉ email của bạn. Hãy xác minh với nhà cung cấp và thử lại.", "auth.failed.password": "Mật khẩu bạn cung cấp không chính xác.", "auth.failed.email": "Không có người dùng nào đã đăng ký với email đó.", "auth.throttle": "Quá nhiều lần đăng nhập thất bại. Vui lòng thử lại sau :seconds giây.", diff --git a/lang/zh-cn.json b/lang/zh-cn.json index 84e22a79af..6b531aaaa8 100644 --- a/lang/zh-cn.json +++ b/lang/zh-cn.json @@ -22,6 +22,9 @@ "auth.reset_password": "重置密码", "auth.failed": "这些凭据与我们的记录不符", "auth.failed.callback": "处理第三方登录的回调时出错", + "auth.failed.oauth": "使用此提供商登录失败。如果问题持续存在,请联系管理员。", + "auth.failed.oauth_already_linked": "此账户已使用其他登录方式。请使用该方式登录。", + "auth.failed.oauth_email_unverified": "你的登录提供商未验证你的邮箱地址。请在提供商处验证后重试。", "auth.failed.password": "密码错误", "auth.failed.email": "该账户未注册", "auth.throttle": "登录次数过多,请在 :seconds 秒后重试", diff --git a/lang/zh-tw.json b/lang/zh-tw.json index aa078104b8..3d0606753c 100644 --- a/lang/zh-tw.json +++ b/lang/zh-tw.json @@ -20,6 +20,9 @@ "auth.reset_password": "重設密碼", "auth.failed": "這些憑證與我們的記錄不符。", "auth.failed.callback": "無法處理來自登入提供者的回呼。", + "auth.failed.oauth": "使用此提供者登入失敗。如果問題持續,請聯絡管理員。", + "auth.failed.oauth_already_linked": "此帳戶已使用其他登入方式。請使用該方式登入。", + "auth.failed.oauth_email_unverified": "您的登入提供者未驗證您的電子郵件地址。請在提供者處驗證後再試一次。", "auth.failed.password": "密碼錯誤。", "auth.failed.email": "找不到該電子郵件地址的使用者。", "auth.throttle": "登入嘗試次數太多。請在 :seconds 秒後重試。", diff --git a/tests/Feature/OauthControllerTest.php b/tests/Feature/OauthControllerTest.php index 5568407662..5a5a8febb4 100644 --- a/tests/Feature/OauthControllerTest.php +++ b/tests/Feature/OauthControllerTest.php @@ -718,3 +718,91 @@ it('sends the instance callback url without saving it when a forged host starts expect($query['redirect_uri'])->toBe('https://coolify.example.com/auth/github/callback') ->and(OauthSetting::where('provider', 'github')->value('redirect_uri'))->toBeNull(); }); + +function mockGoogleCallbackUser(array $oauthUser): void +{ + $provider = Mockery::mock(); + $provider->shouldReceive('setConfig')->andReturnSelf(); + $provider->shouldReceive('with')->andReturnSelf(); + $provider->shouldReceive('user')->andReturn((object) $oauthUser); + Socialite::shouldReceive('driver')->with('google')->andReturn($provider); +} + +describe('callback error messages', function () { + beforeEach(function () { + config()->set('app.maintenance.driver', 'file'); + }); + + it('tells a user that the account already uses another sign-in method', function () { + $user = User::factory()->create(['email' => 'linked@example.com']); + OauthIdentity::create([ + 'user_id' => $user->id, + 'provider' => 'github', + 'issuer' => 'github', + 'provider_user_id' => 'github-user-id', + 'email' => 'linked@example.com', + ]); + mockGoogleCallbackUser([ + 'email' => 'linked@example.com', + 'name' => 'Linked User', + 'id' => 'google-user-id', + 'user' => ['email_verified' => true, 'hd' => 'example.com'], + ]); + + $this->from('/login')->get(route('auth.callback', 'google')) + ->assertRedirect('/login'); + expect(session('errors')->first())->toBe(__('auth.failed.oauth_already_linked')); + $this->assertGuest(); + }); + + it('tells a user that the provider did not verify the email before it reveals a linked account', function () { + $user = User::factory()->create(['email' => 'linked@example.com']); + OauthIdentity::create([ + 'user_id' => $user->id, + 'provider' => 'github', + 'issuer' => 'github', + 'provider_user_id' => 'github-user-id', + 'email' => 'linked@example.com', + ]); + mockGoogleCallbackUser([ + 'email' => 'linked@example.com', + 'name' => 'Unverified User', + 'id' => 'google-user-id', + 'user' => ['email_verified' => false, 'hd' => 'example.com'], + ]); + + $this->from('/login')->get(route('auth.callback', 'google')) + ->assertRedirect('/login'); + expect(session('errors')->first())->toBe(__('auth.failed.oauth_email_unverified')); + $this->assertGuest(); + }); + + it('tells a new user that registration is disabled', function () { + mockGoogleCallbackUser([ + 'email' => 'new@example.com', + 'name' => 'New User', + 'id' => 'google-user-id', + 'user' => ['email_verified' => true, 'hd' => 'example.com'], + ]); + + $this->from('/login')->get(route('auth.callback', 'google')) + ->assertRedirect('/login'); + expect(session('errors')->first())->toBe(__('auth.registration_disabled')); + $this->assertGuest(); + }); + + it('shows a generic OAuth message instead of the password message for other denied logins', function () { + mockGoogleCallbackUser([ + 'email' => 'outside@example.org', + 'name' => 'Outside User', + 'id' => 'google-user-id', + 'user' => ['email_verified' => true, 'hd' => 'example.org'], + ]); + + $this->from('/login')->get(route('auth.callback', 'google')) + ->assertRedirect('/login'); + expect(session('errors')->first())->toBe(__('auth.failed.oauth')) + ->not->toBe(__('auth.failed')); + $this->assertGuest(); + }); +});