From 3dc89cfe05eb0a57deabcf8f01352f1c01e7dc03 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 3 Nov 2026 21:22:30 +0100 Subject: [PATCH] fix(oauth): require redirect URI to point at the provider callback route Validate that the OAuth redirect URI path matches the auth.callback route for the provider. Any other path falls through to the catch-all route and silently sends the user back to the login page, so reject it with a clear validation error instead. --- app/Livewire/SettingsOauth.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/Livewire/SettingsOauth.php b/app/Livewire/SettingsOauth.php index d70dd6421e..8864d1d0ef 100644 --- a/app/Livewire/SettingsOauth.php +++ b/app/Livewire/SettingsOauth.php @@ -36,7 +36,7 @@ class SettingsOauth extends Component $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.redirect_uri"] = ['nullable', 'string', 'max:2048', 'url:http,https', $this->callbackPathRule($setting->provider)]; $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'; @@ -57,6 +57,23 @@ class SettingsOauth extends Component return $rules; } + /** + * The provider sends the browser back to the redirect URI. Only the callback route can + * finish the login; any other path falls through to the catch-all route, which sends + * the user to the login page without an error. + */ + private function callbackPathRule(string $provider): \Closure + { + return function (string $attribute, mixed $value, \Closure $fail) use ($provider): void { + $expectedPath = route('auth.callback', $provider, false); + $path = rtrim((string) parse_url((string) $value, PHP_URL_PATH), '/'); + + if ($path !== $expectedPath) { + $fail("The redirect URI must end with {$expectedPath}."); + } + }; + } + public function mount(?string $provider = null): ?Redirector { if (! isInstanceAdmin()) {