diff --git a/app/Actions/Fortify/ResetUserPassword.php b/app/Actions/Fortify/ResetUserPassword.php index 158996c90..a5d9e56c2 100644 --- a/app/Actions/Fortify/ResetUserPassword.php +++ b/app/Actions/Fortify/ResetUserPassword.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rules\Password; +use Illuminate\Validation\ValidationException; use Laravel\Fortify\Contracts\ResetsUserPasswords; class ResetUserPassword implements ResetsUserPasswords @@ -17,6 +18,14 @@ class ResetUserPassword implements ResetsUserPasswords */ public function reset(User $user, array $input): void { + $settings = instanceSettings(); + // Prevent OAuth-only users from resetting passwords + if ($settings->oauth_only || $user->oauth_only) { + throw ValidationException::withMessages([ + 'email' => __('Password reset is disabled for OAuth-only accounts.'), + ]); + } + Validator::make($input, [ 'password' => ['required', Password::defaults(), 'confirmed'], ])->validate(); diff --git a/app/Actions/Fortify/UpdateUserPassword.php b/app/Actions/Fortify/UpdateUserPassword.php index 0c51ec56d..b00dab5c0 100644 --- a/app/Actions/Fortify/UpdateUserPassword.php +++ b/app/Actions/Fortify/UpdateUserPassword.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rules\Password; +use Illuminate\Validation\ValidationException; use Laravel\Fortify\Contracts\UpdatesUserPasswords; class UpdateUserPassword implements UpdatesUserPasswords @@ -17,6 +18,14 @@ class UpdateUserPassword implements UpdatesUserPasswords */ public function update(User $user, array $input): void { + $settings = instanceSettings(); + // Prevent OAuth-only users from updating passwords + if ($settings->oauth_only || $user->oauth_only) { + throw ValidationException::withMessages([ + 'current_password' => __('Password update is disabled for OAuth-only accounts.'), + ]); + } + Validator::make($input, [ 'current_password' => ['required', 'string', 'current_password:web'], 'password' => ['required', Password::defaults(), 'confirmed'], diff --git a/app/Http/Controllers/OauthController.php b/app/Http/Controllers/OauthController.php index 3a3f18c9c..442a1a7e3 100644 --- a/app/Http/Controllers/OauthController.php +++ b/app/Http/Controllers/OauthController.php @@ -22,13 +22,15 @@ class OauthController extends Controller $user = User::whereEmail($oauthUser->email)->first(); if (! $user) { $settings = instanceSettings(); - if (! $settings->is_registration_enabled) { + // Allow OAuth registration if either general registration OR OAuth-specific registration is enabled + if (! $settings->is_registration_enabled && ! $settings->oauth_registration_enabled) { abort(403, 'Registration is disabled'); } $user = User::create([ 'name' => $oauthUser->name, 'email' => $oauthUser->email, + 'oauth_only' => $settings->oauth_only, ]); } Auth::login($user); diff --git a/app/Livewire/Settings/Advanced.php b/app/Livewire/Settings/Advanced.php index ad478273f..f6c125fad 100644 --- a/app/Livewire/Settings/Advanced.php +++ b/app/Livewire/Settings/Advanced.php @@ -14,6 +14,12 @@ class Advanced extends Component #[Validate('boolean')] public bool $is_registration_enabled; + #[Validate('boolean')] + public bool $oauth_registration_enabled; + + #[Validate('boolean')] + public bool $oauth_only; + #[Validate('boolean')] public bool $do_not_track; @@ -41,6 +47,8 @@ class Advanced extends Component { return [ 'is_registration_enabled' => 'boolean', + 'oauth_registration_enabled' => 'boolean', + 'oauth_only' => 'boolean', 'do_not_track' => 'boolean', 'is_dns_validation_enabled' => 'boolean', 'custom_dns_servers' => 'nullable|string', @@ -62,6 +70,8 @@ class Advanced extends Component $this->allowed_ips = $this->settings->allowed_ips; $this->do_not_track = $this->settings->do_not_track; $this->is_registration_enabled = $this->settings->is_registration_enabled; + $this->oauth_registration_enabled = $this->settings->oauth_registration_enabled ?? false; + $this->oauth_only = $this->settings->oauth_only ?? false; $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled; $this->is_api_enabled = $this->settings->is_api_enabled; $this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation; @@ -142,6 +152,8 @@ class Advanced extends Component { try { $this->settings->is_registration_enabled = $this->is_registration_enabled; + $this->settings->oauth_registration_enabled = $this->oauth_registration_enabled; + $this->settings->oauth_only = $this->oauth_only; $this->settings->do_not_track = $this->do_not_track; $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; $this->settings->custom_dns_servers = $this->custom_dns_servers; diff --git a/app/Models/User.php b/app/Models/User.php index 4561cddb2..7e7100a91 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -55,6 +55,7 @@ class User extends Authenticatable implements SendsEmail 'force_password_reset' => 'boolean', 'show_boarding' => 'boolean', 'email_change_code_expires_at' => 'datetime', + 'oauth_only' => 'boolean', ]; /** diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index 85f38b967..a63e69503 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -78,6 +78,16 @@ class FortifyServiceProvider extends ServiceProvider $user && Hash::check($request->password, $user->password) ) { + $settings = instanceSettings(); + // Prevent password login when global OAuth-only is enabled + if ($settings->oauth_only) { + return null; + } + // Prevent OAuth-only users from logging in with password + if ($user->oauth_only) { + return null; + } + $user->updated_at = now(); $user->save(); diff --git a/database/migrations/2026_03_10_080000_add_oauth_registration_settings.php b/database/migrations/2026_03_10_080000_add_oauth_registration_settings.php new file mode 100644 index 000000000..bac43778f --- /dev/null +++ b/database/migrations/2026_03_10_080000_add_oauth_registration_settings.php @@ -0,0 +1,29 @@ +boolean('oauth_registration_enabled')->default(false)->after('is_registration_enabled'); + $table->boolean('oauth_only')->default(false)->after('oauth_registration_enabled'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('instance_settings', function (Blueprint $table) { + $table->dropColumn(['oauth_registration_enabled', 'oauth_only']); + }); + } +}; diff --git a/database/migrations/2026_03_10_080001_add_oauth_only_to_users.php b/database/migrations/2026_03_10_080001_add_oauth_only_to_users.php new file mode 100644 index 000000000..524e7b096 --- /dev/null +++ b/database/migrations/2026_03_10_080001_add_oauth_only_to_users.php @@ -0,0 +1,28 @@ +boolean('oauth_only')->default(false)->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('oauth_only'); + }); + } +}; diff --git a/resources/views/livewire/settings/advanced.blade.php b/resources/views/livewire/settings/advanced.blade.php index 3069c8479..5e83606cf 100644 --- a/resources/views/livewire/settings/advanced.blade.php +++ b/resources/views/livewire/settings/advanced.blade.php @@ -21,6 +21,17 @@ helper="Allow users to self-register. If disabled, only administrators can create accounts." label="Registration Allowed" /> +

OAuth Settings

+
+ +
+
+ +
- \ No newline at end of file +