diff --git a/app/Http/Controllers/Auth/SocialiteController.php b/app/Http/Controllers/Auth/SocialiteController.php index 9484ebd..673c2c2 100644 --- a/app/Http/Controllers/Auth/SocialiteController.php +++ b/app/Http/Controllers/Auth/SocialiteController.php @@ -3,10 +3,10 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; +use App\Models\SocialAccount; use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use Laravel\Socialite\Facades\Socialite; @@ -63,45 +63,78 @@ class SocialiteController extends Controller ]); } - // Find existing user by provider ID or email - $user = User::where('email', $socialUser->getEmail())->first(); + // First, check if this provider account is already linked to a user + $socialAccount = SocialAccount::where('provider', $provider) + ->where('provider_id', $socialUser->getId()) + ->first(); - if (! $user) { - // Create new user if registration is enabled - if (! config('auth-ui.features.registration')) { + if ($socialAccount) { + Auth::login($socialAccount->user, remember: true); + request()->session()->regenerate(); + + return redirect()->intended(config('auth-ui.redirects.login', '/')); + } + + // Check if a user with this email already exists + $existingUser = User::where('email', $socialUser->getEmail())->first(); + + if ($existingUser) { + if (! $existingUser->hasVerifiedEmail()) { return redirect()->route('login')->withErrors([ - 'email' => 'Registration is disabled. Please contact an administrator.', + 'email' => 'An account with this email already exists. Please verify your email first, then link your social account.', ]); } - $name = $socialUser->getName() ?? $socialUser->getNickname() ?? 'User'; - $nameParts = explode(' ', $name, 2); - $suggestedUsername = $this->suggestUsername($socialUser); + $existingUser->socialAccounts()->create([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + ]); - // Check if username is already taken - if (User::whereRaw('LOWER(username) = ?', [strtolower($suggestedUsername)])->exists()) { - // Store social data in session and redirect to complete profile - session()->put('socialite_user', [ - 'email' => $socialUser->getEmail(), - 'first_name' => $nameParts[0], - 'last_name' => $nameParts[1] ?? '', - 'suggested_username' => $suggestedUsername, - 'provider' => $provider, - ]); + Auth::login($existingUser, remember: true); + request()->session()->regenerate(); - return redirect()->route('auth.complete-profile'); - } + return redirect()->intended(config('auth-ui.redirects.login', '/')); + } - $user = User::create([ - 'username' => $suggestedUsername, - 'first_name' => $nameParts[0], - 'last_name' => $nameParts[1] ?? '', - 'email' => $socialUser->getEmail(), - 'password' => Hash::make(Str::random(24)), - 'email_verified_at' => now(), + // New user — check if registration is enabled + if (! config('auth-ui.features.registration')) { + return redirect()->route('login')->withErrors([ + 'email' => 'Registration is disabled. Please contact an administrator.', ]); } + $name = $socialUser->getName() ?? $socialUser->getNickname() ?? 'User'; + $nameParts = explode(' ', $name, 2); + $suggestedUsername = $this->suggestUsername($socialUser); + + // Check if username is already taken + if (User::whereRaw('LOWER(username) = ?', [strtolower($suggestedUsername)])->exists()) { + session()->put('socialite_user', [ + 'email' => $socialUser->getEmail(), + 'first_name' => $nameParts[0], + 'last_name' => $nameParts[1] ?? '', + 'suggested_username' => $suggestedUsername, + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + ]); + + return redirect()->route('auth.complete-profile'); + } + + $user = User::create([ + 'username' => $suggestedUsername, + 'first_name' => $nameParts[0], + 'last_name' => $nameParts[1] ?? '', + 'email' => $socialUser->getEmail(), + ]); + + $user->forceFill(['email_verified_at' => now()])->save(); + + $user->socialAccounts()->create([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + ]); + Auth::login($user, remember: true); request()->session()->regenerate(); diff --git a/app/Models/SocialAccount.php b/app/Models/SocialAccount.php new file mode 100644 index 0000000..3614ea1 --- /dev/null +++ b/app/Models/SocialAccount.php @@ -0,0 +1,28 @@ + + */ + protected $fillable = [ + 'user_id', + 'provider', + 'provider_id', + ]; + + /** + * Get the user that owns the social account. + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/database/migrations/2026_03_19_212955_create_social_accounts_table.php b/database/migrations/2026_03_19_212955_create_social_accounts_table.php new file mode 100644 index 0000000..257adf1 --- /dev/null +++ b/database/migrations/2026_03_19_212955_create_social_accounts_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->string('provider'); + $table->string('provider_id'); + $table->timestamps(); + + $table->unique(['provider', 'provider_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('social_accounts'); + } +};