ensureFeatureEnabled($provider); // Save provider to session for the unified callback session(['social_auth_provider' => $provider]); return Socialite::driver($provider)->redirect(); } /** * Handle Provider Callback */ public function callback() { $provider = session('social_auth_provider'); if (! $provider) { return redirect('/login')->with('error', __('Authentication provider not found in session.')); } $this->ensureFeatureEnabled($provider); try { $socialUser = Socialite::driver($provider)->user(); } catch (Exception $e) { return redirect('/login')->with('error', __(':provider authentication failed.', ['provider' => ucfirst($provider)])); } $idColumn = $provider.'_id'; // google_id, facebook_id, github_id // Reject if the OAuth provider signals the email is not verified $emailVerified = $socialUser->user['email_verified'] ?? null; if ($emailVerified === false) { return redirect('/login')->with('error', __('Your :provider email address is not verified. Please verify it and try again.', ['provider' => ucfirst($provider)])); } // Primary lookup: by provider-specific ID (not spoofable) $user = User::where($idColumn, $socialUser->id)->first(); // Secondary lookup: by email only if no provider-ID match exists yet // (covers first-time OAuth login for users who registered via email) if (! $user && $socialUser->email) { $byEmail = User::where('email', $socialUser->email)->first(); if ($byEmail) { // Only link if the existing account does NOT already belong to a different OAuth identity if (empty($byEmail->{$idColumn})) { $user = $byEmail; } else { // Email already linked to a different identity on this provider — refuse silently // to avoid leaking that the account exists or letting an attacker take it over. return redirect('/login')->with('error', __('This email is already linked to a different account. Please sign in with your original method.')); } } } if (! $user) { // Register new user $user = User::create([ 'name' => $socialUser->name ?? $socialUser->nickname ?? $socialUser->email, 'email' => $socialUser->email, $idColumn => $socialUser->id, 'avatar' => $socialUser->avatar, 'password' => bcrypt(Str::random(32)), ]); // Assign default role $user->assignRole('User'); } else { // Sync Social ID and Avatar $user->update([ $idColumn => $socialUser->id, 'avatar' => $socialUser->avatar, ]); } Auth::login($user, true); session()->forget('social_auth_provider'); session()->regenerate(); return redirect()->intended('/dashboard'); } /** * Ensure the requested provider is enabled in settings */ protected function ensureFeatureEnabled(string $provider): void { $settingKey = 'feature_'.$provider.'_oauth'; if ($provider === 'facebook' || $provider === 'github') { // GitHub and Facebook keys follow the 'feature_{provider}_oauth' pattern } abort_unless($this->systemConfig->get($settingKey, false), 404, __('Provider not enabled.')); } }