Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Socialite Laravel Package

jeffersongoncalves/laravel-socialite

A Laravel Socialite extension by Jefferson Goncalves that simplifies OAuth social login integration. Add social providers to your Laravel app and let users sign in with existing accounts using clean, framework-native code and minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require <vendor>/<package>   # requires laravel/socialite as well
php artisan vendor:publish --tag=social-accounts-config
php artisan vendor:publish --tag=social-accounts-migrations
php artisan migrate

If the tags don't resolve, run php artisan vendor:publish interactively and pick the package provider — the migration is only published once, so check database/migrations before assuming it ran.

Configure the driver the way you already do. This package wraps Socialite rather than replacing it, so credentials still live in config/services.php:

'github' => [
    'client_id'     => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect'      => env('APP_URL').'/auth/github/callback',
],

First use case: link-then-login for an existing app. Two routes and one controller method each:

Route::get('/auth/{provider}/redirect', [SocialAuthController::class, 'redirect']);
Route::get('/auth/{provider}/callback', [SocialAuthController::class, 'callback']);
public function redirect(string $provider)
{
    return Socialite::provider($provider)
        ->scopes(['read:user', 'user:email'])
        ->redirect();
}

public function callback(string $provider, SocialiteUserResolver $resolver)
{
    $socialiteUser = Socialite::provider($provider)->user();

    Auth::login($resolver->resolve($provider, $socialiteUser), remember: true);

    return redirect()->intended('/dashboard');
}

Where to look first, in order: the published config (it drives the resolver's behaviour more than the code does), the Provider class for the fluent surface (scopes(), with(), stateless(), driver()), then SocialiteUserResolver::resolve() to see exactly what it does on a miss, then the SocialAccount model and migration for the schema you're now committed to.

Implementation Patterns

Controller-per-concern, not per-provider. Key everything off the {provider} route segment and validate it against an allowlist before it reaches Socialite — passing user input straight into driver() is a driver-instantiation vector.

Let the resolver own the three-way branch. The resolver distinguishes (a) an existing SocialAccount link, (b) an email match against users, and (c) registration. Decide the policy in config, not in the controller:

  • Existing app with a user table → enable email matching, disable registration.
  • Greenfield / social-first app → disable email matching, enable registration with a callback that fills required columns (name, password => null).

Custom registration callback is the right seam for anything domain-specific — assigning a tenant, seeding a trial, dispatching a UserRegistered event, or generating a username from the provider nickname:

// config or fluent, depending on the package's surface
'register' => fn (SocialiteUser $su, string $provider) => User::create([
    'name'              => $su->getName() ?? $su->getNickname(),
    'email'             => $su->getEmail(),
    'email_verified_at' => now(),
]),

One user, many accounts. Treat social_accounts as a pivot with payload: user_id, provider, provider_id, plus token columns. Attach additional providers to an already-authenticated user with a separate "connect account" route that reuses the same redirect/callback but calls $request->user()->socialAccounts()->create(...) instead of logging in. That link-intent distinction needs to survive the OAuth round trip — stash it in the session before redirecting.

Stateless mode for APIs and mobile. When a native app drives the OAuth flow, use ->stateless() and have the client pass the code/redirect back to your API. Note this disables Socialite's state check, so the client is responsible for CSRF protection on that leg.

Token handling. Tokens are encrypted at rest via the model's cast. Read them with $account->access_token; never ->getRawOriginal() into logs or JSON responses. Add access_token/refresh_token to $hidden if the model doesn't already. If you need to call the provider's API later, decrypt on demand rather than eager-loading accounts on every request.

Testing. Fake Socialite in feature tests (Socialite::shouldReceive('provider->user')->andReturn($fakeUser), or the Socialite::fake() helper on newer Socialite) and assert against real DB state — the resolver's branch logic is exactly what you want covered, and it's cheap to test because it's DB-driven, not HTTP-driven.

Gotchas and Tips

The email-match branch is an account-takeover surface. If the provider returns an unverified or absent email_verified flag, matching it against a local user silently hands over an existing account. Prefer disabling email matching in production, or gate it on $socialiteUser->getRaw()['email_verified'] ?? false. This is the single highest-risk default in the package and worth an explicit decision recorded in the config file.

APP_KEY rotation invalidates every stored token. Encrypted casts are not portable across keys. Plan a re-auth prompt for users whose token fails to decrypt — wrap reads in a try/catch for DecryptException rather than letting it 500 the account settings page.

Race on first login. Two concurrent callback requests for a new user can both miss the lookup and both insert. Put a unique index on (provider, provider_id) — the published migration should have one; verify it does — and catch QueryException on the create path, then re-query. Same for the users.email unique constraint during registration.

Concurrent provider_id uniqueness is per-provider, not global. Don't write a global unique index on provider_id; GitHub and GitLab IDs overlap.

Session state needs a session. The stateful flow requires the same session store on redirect and callback. Behind a load balancer without sticky sessions, or with a mismatched SESSION_DOMAIN between APP_URL and the callback host, you'll get "Invalid state" errors that look like credential problems. stateless() sidesteps it, at the cost of the CSRF check.

Fluent calls are order-sensitive. Set the driver and scopes() before redirect(); scopes passed after the authorization URL is built are ignored. Same for with() extra parameters. If a scope appears to have no effect, this is almost always why.

Registration leaves password null. Anything that assumes a password — the default Auth::attempt, password reset emails, password validation rules on profile updates — needs a null check. The users migration's password column must be nullable; if you're on an older app, alter it.

PHP floor vs Laravel floor. The package advertises PHP 8.1+, but Laravel 11 and 12 require 8.2+, and 13 will be higher. Your effective floor is whichever Laravel version you're on — don't read the package constraint as runtime compatibility in CI.

Extension points to confirm before you build on them: whether the SocialAccount model is swappable via config (rather than hardcoded), and whether the resolver is a singleton with config baked in at boot. If it's a singleton, you can't vary behaviour per-request (e.g. registration on a marketing flow, login-only on the app) without rebuilding the container binding — check the service provider before designing around per-route policy.

No routes or controllers ship with it. That's deliberate and good, but it means redirect URI registration with each provider is entirely your responsibility. Keep a single redirect config value per provider derived from APP_URL so local, staging and production don't drift.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log