validationRules(); } private function validationRules(?string $provider = null): array { $rules = OauthSetting::all()->reduce(function ($carry, $setting) use ($provider) { if ($provider !== null && $setting->provider !== $provider) { return $carry; } $carry["oauth_settings_map.$setting->provider.enabled"] = 'required|boolean'; $carry["oauth_settings_map.$setting->provider.client_id"] = 'nullable|string'; $carry["oauth_settings_map.$setting->provider.client_secret"] = 'nullable|string'; $carry["oauth_settings_map.$setting->provider.redirect_uri"] = 'nullable|string|max:2048|url:http,https'; $carry["oauth_settings_map.$setting->provider.tenant"] = 'nullable|string'; $carry["oauth_settings_map.$setting->provider.base_url"] = 'nullable|string|max:2048|url:http,https'; $carry["oauth_settings_map.$setting->provider.custom_label"] = 'nullable|string|max:255'; $carry["oauth_settings_map.$setting->provider.scopes"] = 'nullable|string|max:1000'; $carry["oauth_settings_map.$setting->provider.allow_registration"] = 'boolean'; $carry["oauth_settings_map.$setting->provider.require_email_verified"] = 'boolean'; $carry["oauth_settings_map.$setting->provider.use_pkce"] = 'boolean'; $carry["oauth_settings_map.$setting->provider.clock_skew_seconds"] = 'nullable|integer|min:0|max:600'; return $carry; }, []); if ($provider === null) { $rules['disable_registration_when_oauth_enabled'] = 'boolean'; } return $rules; } public function mount(?string $provider = null) { if (! isInstanceAdmin()) { return redirect()->route('home'); } $this->selectedProvider = $provider; $this->disable_registration_when_oauth_enabled = (bool) instanceSettings()->disable_registration_when_oauth_enabled; $this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function ($carry, $setting) { $carry[$setting->provider] = $this->oauthSettingToArray($setting); return $carry; }, []); if ($this->selectedProvider !== null && ! array_key_exists($this->selectedProvider, $this->oauth_settings_map)) { abort(404); } } private function updateOauthSettings(?string $provider = null): void { $this->validate($this->validationRules($provider)); if ($provider) { $oauthData = $this->oauth_settings_map[$provider]; $oauth = OauthSetting::find($oauthData['id']); if (! $oauth) { throw new \Exception('OAuth setting for '.$provider.' not found. It may have been deleted.'); } $this->fillOauthSetting($oauth, $oauthData); $this->ensureProviderCanBeEnabled($oauth); $oauth->save(); $this->oauth_settings_map[$provider] = $this->oauthSettingToArray($oauth); $this->dispatch('success', 'OAuth settings for '.$oauth->provider.' updated successfully!'); return; } $errors = []; foreach (array_values($this->oauth_settings_map) as $settingData) { $oauth = OauthSetting::find($settingData['id']); if (! $oauth) { $errors[] = "OAuth setting for provider '{$settingData['provider']}' not found. It may have been deleted."; continue; } $this->fillOauthSetting($oauth, $settingData); if ($oauth->enabled && ! $oauth->couldBeEnabled()) { $oauth->enabled = false; $errors[] = "OAuth settings are incomplete for '{$oauth->provider}'. Required fields are missing. The provider has been disabled."; } if ($oauth->enabled && $oauth->isOidc() && ! in_array('openid', $oauth->scopeList(), true)) { $oauth->enabled = false; $errors[] = "OIDC scopes must include 'openid'. The provider has been disabled."; } $oauth->save(); $this->oauth_settings_map[$oauth->provider] = $this->oauthSettingToArray($oauth); } instanceSettings()->update([ 'disable_registration_when_oauth_enabled' => $this->disable_registration_when_oauth_enabled, ]); if (! empty($errors)) { $this->dispatch('error', implode('
', $errors)); } } private function fillOauthSetting(OauthSetting $oauth, array $data): void { $oauth->fill([ 'enabled' => (bool) ($data['enabled'] ?? false), 'client_id' => $data['client_id'] ?? null, 'client_secret' => $data['client_secret'] ?? null, 'redirect_uri' => $this->nullableString($data['redirect_uri'] ?? null), 'tenant' => $data['tenant'] ?? null, 'base_url' => $this->nullableString($data['base_url'] ?? null), 'custom_label' => $data['custom_label'] ?? null, 'scopes' => $data['scopes'] ?? null, 'allow_registration' => (bool) ($data['allow_registration'] ?? false), 'require_email_verified' => (bool) ($data['require_email_verified'] ?? true), 'use_pkce' => (bool) ($data['use_pkce'] ?? true), 'clock_skew_seconds' => (int) ($data['clock_skew_seconds'] ?? 60), ]); } private function nullableString(mixed $value): ?string { if ($value === null) { return null; } $value = trim((string) $value); return $value === '' ? null : $value; } private function ensureProviderCanBeEnabled(OauthSetting $oauth): void { if (! $oauth->enabled) { return; } if (! $oauth->couldBeEnabled()) { $oauth->update(['enabled' => false]); throw new \Exception('OAuth settings are not complete for '.$oauth->provider.'.
Please fill in all required fields.'); } if ($oauth->isOidc() && ! in_array('openid', $oauth->scopeList(), true)) { $oauth->update(['enabled' => false]); throw new \Exception("OIDC scopes must include 'openid'."); } } private function oauthSettingToArray(OauthSetting $setting): array { return [ 'id' => $setting->id, 'provider' => $setting->provider, 'enabled' => $setting->enabled, 'client_id' => $setting->client_id, 'client_secret' => $setting->client_secret, 'redirect_uri' => $setting->redirect_uri, 'tenant' => $setting->tenant, 'base_url' => $setting->base_url, 'custom_label' => $setting->custom_label, 'scopes' => $setting->scopes ?: 'openid email profile', 'allow_registration' => $setting->allow_registration, 'require_email_verified' => $setting->require_email_verified ?? true, 'use_pkce' => $setting->use_pkce ?? true, 'clock_skew_seconds' => $setting->clock_skew_seconds ?? 60, 'label' => $this->providerLabel($setting->provider), ]; } public function providerLabel(string $provider): string { return match ($provider) { 'oidc' => 'OpenID Connect', 'gitlab' => 'GitLab', default => str($provider)->headline()->toString(), }; } public function instantSave(string $provider) { try { $this->updateOauthSettings($provider); } catch (\Exception $e) { return handleError($e, $this); } } public function toggleProvider(string $provider) { try { if (! array_key_exists($provider, $this->oauth_settings_map)) { abort(404); } if (! (bool) $this->oauth_settings_map[$provider]['enabled']) { $this->validateProviderCanBeEnabled($provider); } $this->oauth_settings_map[$provider]['enabled'] = ! (bool) $this->oauth_settings_map[$provider]['enabled']; $this->updateOauthSettings($provider); } catch (\Exception $e) { $oauth = OauthSetting::where('provider', $provider)->first(); if ($oauth) { $this->oauth_settings_map[$provider] = $this->oauthSettingToArray($oauth); } return handleError($e, $this); } } private function validateProviderCanBeEnabled(string $provider): void { $this->validate($this->validationRules($provider)); $oauth = OauthSetting::find($this->oauth_settings_map[$provider]['id']); if (! $oauth) { throw new \Exception('OAuth setting for '.$provider.' not found. It may have been deleted.'); } $this->fillOauthSetting($oauth, [ ...$this->oauth_settings_map[$provider], 'enabled' => true, ]); if (! $oauth->couldBeEnabled()) { throw new \Exception('OAuth settings are not complete for '.$oauth->provider.'.
Please fill in all required fields.'); } if ($oauth->isOidc() && ! in_array('openid', $oauth->scopeList(), true)) { throw new \Exception("OIDC scopes must include 'openid'."); } } public function saveRegistrationPolicy(): void { $this->validate([ 'disable_registration_when_oauth_enabled' => 'boolean', ]); instanceSettings()->update([ 'disable_registration_when_oauth_enabled' => $this->disable_registration_when_oauth_enabled, ]); $this->dispatch('success', 'Authentication settings updated successfully!'); } public function submit(): void { $this->updateOauthSettings($this->selectedProvider); if ($this->selectedProvider === null) { $this->dispatch('success', 'Instance settings updated successfully!'); } } }