feat(auth): show OAuth-only registration notice on login page

Move the user creation check from OauthLoginService into
OauthSetting::allowsUserCreation(), so the login view can use it.
The login view now gets a can_register_with_oauth flag. The flag is
true when an enabled OAuth provider allows creating new users. The
view uses it to show the new auth.register_with_oauth message.
Add translations for the message in all locales, plus test coverage.
This commit is contained in:
Andras Bacsai committed 2026-10-02 16:45:33 +02:00
1 parent b1ddde2b64
commit 5b1fcd6e13
25 files changed
+55 -15

No files matched your search

+13
View File
@@ -37,6 +37,19 @@ class OauthSetting extends Model
);
}
/**
* Only OIDC exposes a provider-level user creation setting. Other providers
* follow the instance registration setting, as before OIDC support.
*/
public function allowsUserCreation(): bool
{
if (instanceSettings()->is_registration_enabled) {
return true;
}
return $this->provider === 'oidc' && $this->allow_registration;
}
public function couldBeEnabled(): bool
{
switch ($this->provider) {
+3
View File
@@ -65,6 +65,9 @@ class FortifyServiceProvider extends ServiceProvider
return view('auth.login', [
'is_registration_enabled' => $settings->isPasswordRegistrationAllowed(),
'can_register_with_oauth' => $enabled_oauth_providers->contains(
fn (OauthSetting $oauthSetting) => $oauthSetting->couldBeEnabled() && $oauthSetting->allowsUserCreation()
),
'enabled_oauth_providers' => $enabled_oauth_providers,
]);
});
+2 -15
View File
@@ -147,7 +147,7 @@ class OauthLoginService
}
if (! $user) {
if (! $this->canCreateUser($oauthSetting)) {
if (! $oauthSetting->allowsUserCreation()) {
throw new OauthLoginException('Registration is disabled', 'auth.registration_disabled');
}
@@ -266,7 +266,7 @@ class OauthLoginService
}
if (! $user) {
if (! $this->canCreateUser($oauthSetting)) {
if (! $oauthSetting->allowsUserCreation()) {
throw new OauthLoginException('Registration is disabled', 'auth.registration_disabled');
}
@@ -290,19 +290,6 @@ class OauthLoginService
}
}
/**
* Only OIDC exposes a provider-level user creation setting. Other providers
* follow the instance registration setting, as before OIDC support.
*/
private function canCreateUser(OauthSetting $oauthSetting): bool
{
if (instanceSettings()->is_registration_enabled) {
return true;
}
return $oauthSetting->provider === 'oidc' && $oauthSetting->allow_registration;
}
private function createUser(string $name, string $email, OauthSetting $oauthSetting): User
{
if (User::count() === 0) {
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "تسجيل الخروج",
"auth.register": "تسجيل",
"auth.registration_disabled": "تم تعطيل التسجيل. يرجى التواصل مع المسؤول.",
"auth.register_with_oauth": "التسجيل متاح فقط عبر OAuth.",
"auth.reset_password": "إعادة تعيين كلمة المرور",
"auth.failed": "هذه البيانات لا تتطابق مع سجلاتنا.",
"auth.failed.callback": "فشل في معالجة استدعاء من مزود تسجيل الدخول.",
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "Çıxış",
"auth.register": "Qeydiyyat",
"auth.registration_disabled": "Qeydiyyat bağlıdır. Administratorla əlaqə saxlayın.",
"auth.register_with_oauth": "Yalnız OAuth ilə qeydiyyat aktivdir.",
"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.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "Odhlásit se",
"auth.register": "Registrovat se",
"auth.registration_disabled": "Registrace je zakázána. Kontaktujte prosím administrátora.",
"auth.register_with_oauth": "Je povolena pouze registrace přes OAuth.",
"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í.",
+1
View File
@@ -19,6 +19,7 @@
"auth.logout": "Abmelden",
"auth.register": "Registrieren",
"auth.registration_disabled": "Registrierung ist deaktiviert. Bitte kontaktiere einen Administrator.",
"auth.register_with_oauth": "Nur die Registrierung über OAuth ist aktiviert.",
"auth.reset_password": "Passwort zurücksetzen",
"auth.failed": "Diese Anmeldedaten wurden nicht gefunden.",
"auth.failed.callback": "Fehlerhafte Verarbeitung der Antwort des Anmeldeanbieters.",
+1
View File
@@ -21,6 +21,7 @@
"auth.logout": "Logout",
"auth.register": "Register",
"auth.registration_disabled": "Registration is disabled. Please contact the administrator.",
"auth.register_with_oauth": "Only OAuth registration is enabled.",
"auth.reset_password": "Reset password",
"auth.failed": "These credentials do not match our records.",
"auth.failed.callback": "Failed to process callback from login provider.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "Cerrar sesión",
"auth.register": "Registrar",
"auth.registration_disabled": "El registro está desactivado. Por favor contacta con el administrador.",
"auth.register_with_oauth": "Solo está habilitado el registro mediante OAuth.",
"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.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "خروج",
"auth.register": "ثبت نام",
"auth.registration_disabled": "ثبت نام غیر فعال است. لطفا با ادمین تماس بگیرید.",
"auth.register_with_oauth": "فقط ثبت‌نام از طریق OAuth فعال است.",
"auth.reset_password": "بازیابی رمز عبور",
"auth.failed": "این اطلاعات با سوابق ما مطابقت ندارد.",
"auth.failed.callback": "پردازش بازگشت از ارائه‌دهنده ورود با شکست مواجه شد.",
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "Déconnexion",
"auth.register": "S'enregistrer",
"auth.registration_disabled": "L'enregistrement est désactivé. Merci de contacter l'administrateur.",
"auth.register_with_oauth": "Seule l'inscription via OAuth est activée.",
"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.",
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "Keluar",
"auth.register": "Daftar",
"auth.registration_disabled": "Pendaftaran dinonaktifkan. Harap hubungi administrator.",
"auth.register_with_oauth": "Hanya pendaftaran melalui OAuth yang diaktifkan.",
"auth.reset_password": "Reset kata sandi",
"auth.failed": "Kredensial ini tidak cocok dengan catatan kami.",
"auth.failed.callback": "Gagal memproses callback dari penyedia login.",
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "Esci",
"auth.register": "Registrati",
"auth.registration_disabled": "La registrazione è disabilitata. Si prega di contattare l'amministratore.",
"auth.register_with_oauth": "È abilitata solo la registrazione tramite OAuth.",
"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.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "ログアウト",
"auth.register": "登録",
"auth.registration_disabled": "登録は無効です。管理者に連絡してください。",
"auth.register_with_oauth": "OAuth による登録のみ有効です。",
"auth.reset_password": "パスワードをリセット",
"auth.failed": "これらの資格情報は記録と一致しません。",
"auth.failed.callback": "ログインプロバイダーからのコールバックの処理に失敗しました。",
+1
View File
@@ -18,6 +18,7 @@
"auth.logout": "Logg ut",
"auth.register": "Registrer",
"auth.registration_disabled": "Registrering er deaktivert. Vennligst kontakt administrator.",
"auth.register_with_oauth": "Bare registrering via OAuth er aktivert.",
"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.",
+1
View File
@@ -20,6 +20,7 @@
"auth.logout": "Wyloguj",
"auth.register": "Zarejestruj",
"auth.registration_disabled": "Rejestracja jest wyłączona. Skontaktuj się z administratorem.",
"auth.register_with_oauth": "Włączona jest tylko rejestracja przez OAuth.",
"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.",
+1
View File
@@ -19,6 +19,7 @@
"auth.logout": "Sair",
"auth.register": "Cadastrar",
"auth.registration_disabled": "O registro está desativado. Por favor, contate o administrador.",
"auth.register_with_oauth": "Apenas o cadastro via OAuth está habilitado.",
"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.",
+1
View File
@@ -19,6 +19,7 @@
"auth.logout": "Sair",
"auth.register": "Cadastrar",
"auth.registration_disabled": "Cadastro desativado. Por favor, entre em contato com o administrador.",
"auth.register_with_oauth": "Apenas o registo através de OAuth está ativado.",
"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.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "Deconectare",
"auth.register": "Înregistrare",
"auth.registration_disabled": "Înregistrarea este dezactivată. Vă rugăm să contactați administratorul site-ului.",
"auth.register_with_oauth": "Este activată doar înregistrarea prin OAuth.",
"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.",
+1
View File
@@ -19,6 +19,7 @@
"auth.logout": "Çıkış Yap",
"auth.register": "Kayıt Ol",
"auth.registration_disabled": "Kayıt devre dışı bırakıldı. Lütfen yöneticiyle iletişime geçin.",
"auth.register_with_oauth": "Yalnızca OAuth ile kayıt etkin.",
"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.",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "Đăng xuất",
"auth.register": "Đăng ký",
"auth.registration_disabled": "Đăng ký không khả dụng. Vui lòng liên hệ quản trị viên.",
"auth.register_with_oauth": "Chỉ cho phép đăng ký qua OAuth.",
"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.",
+1
View File
@@ -19,6 +19,7 @@
"auth.logout": "退出登录",
"auth.register": "注册",
"auth.registration_disabled": "注册已禁用,请联系管理员",
"auth.register_with_oauth": "仅启用了 OAuth 注册。",
"auth.reset_password": "重置密码",
"auth.failed": "这些凭据与我们的记录不符",
"auth.failed.callback": "处理第三方登录的回调时出错",
+1
View File
@@ -17,6 +17,7 @@
"auth.logout": "登出",
"auth.register": "註冊",
"auth.registration_disabled": "註冊已停用,請聯絡管理員。",
"auth.register_with_oauth": "僅啟用 OAuth 註冊。",
"auth.reset_password": "重設密碼",
"auth.failed": "這些憑證與我們的記錄不符。",
"auth.failed.callback": "無法處理來自登入提供者的回呼。",
+2
View File
@@ -99,6 +99,8 @@
@if ($is_registration_enabled)
<span>New to Coolify?</span>
<a href="/register" class="auth-text-link">{{ __('auth.register_now') }}</a>
@elseif ($can_register_with_oauth)
<span>{{ __('auth.register_with_oauth') }}</span>
@else
<span>{{ __('auth.registration_disabled') }}</span>
@endif
+15
View File
@@ -863,3 +863,18 @@ it('matches the Google hosted domain without case or spaces and accepts any Work
'other domain' => ['example.com', 'example.org', false],
'personal account' => ['example.com', null, false],
]);
it('allows OAuth user creation through global registration or the OIDC user creation setting', function (string $provider, bool $isRegistrationEnabled, bool $allowRegistration, bool $expected) {
InstanceSettings::query()->whereKey(0)->update(['is_registration_enabled' => $isRegistrationEnabled]);
Once::flush();
$setting = new OauthSetting(['provider' => $provider, 'allow_registration' => $allowRegistration]);
expect($setting->allowsUserCreation())->toBe($expected);
})->with([
'github, registration on' => ['github', true, false, true],
'github, registration off' => ['github', false, true, false],
'oidc, registration off, oidc user creation on' => ['oidc', false, true, true],
'oidc, registration off, oidc user creation off' => ['oidc', false, false, false],
'oidc, registration on, oidc user creation off' => ['oidc', true, false, true],
]);