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 Social Auth Laravel Package

mad-web/laravel-social-auth

A Laravel package that adds social authentication to your app, letting users sign in with providers like Google, Facebook, and GitHub through a simple, configurable OAuth flow that integrates with Laravel's built-in auth system.

View on GitHub
Deep Wiki
Context7

Getting Started

This package sits on top of Laravel Socialite and adds a driver manager, a normalized user object, and persistence for linked social accounts. Before writing any code, read four things in the repo: config/social-auth.php, the published migration, src/SocialAuth.php (the manager/facade), and src/SocialUser.php (the DTO returned from callbacks). Those four files are the entire contract you will code against.

1. Install and publish

composer require mad-web/laravel-social-auth
php artisan vendor:publish --provider="MadWeb\SocialAuth\SocialAuthServiceProvider"
php artisan migrate

The publish step should give you a config file and a social_accounts migration (columns roughly: id, user_id, provider, provider_user_id, nickname, name, email, avatar, timestamps). If a tag is required, use --tag=social-auth-config and --tag=social-auth-migrations; if that fails, publish by provider as above and check vendor/mad-web/laravel-social-auth/ for the stubs.

2. Wire up credentials

Populate the per-provider block in config/social-auth.php (client id, secret, redirect URI, scopes). Depending on version this may delegate to Socialite's usual config/services.php entries — confirm by reading the driver's __construct before you spend an hour debugging "client_id is null". Add the env vars to .env and .env.example.

3. Add the trait to your User model

use MadWeb\SocialAuth\Traits\HasSocialAccounts;

class User extends Authenticatable
{
    use HasSocialAccounts;
}

This gives you $user->socialAccounts() (a hasMany).

4. Two routes and one controller

Route::get('auth/{provider}', [SocialController::class, 'redirect'])->name('social.redirect');
Route::get('auth/{provider}/callback', [SocialController::class, 'callback'])->name('social.callback');
public function redirect(string $provider)
{
    return SocialAuth::driver($provider)->redirect();
}

public function callback(string $provider)
{
    $socialUser = SocialAuth::driver($provider)->user();

    // find-or-create local user, link SocialAccount, Auth::login($user)
}

First use case to build: "Sign in with GitHub." Render a button pointing at route('social.redirect', 'github'), handle the callback, look up social_accounts by (provider, provider_user_id), fall back to matching on email, otherwise create a user, then log in. Ship that one flow end to end before adding a second provider — it exercises every part of the package (manager, redirect, DTO, persistence) and the second provider is then a config change plus a button.

Implementation Patterns

Allowlist providers at the edge of your app. Never pass {provider} straight into the manager without validation. A tiny middleware or a Rule::in(array_keys(config('social-auth.providers'))) guard prevents 500s from typos and makes the failure mode a clean 404.

Treat SocialUser as the seam. Everything downstream should consume the DTO, not the raw provider response:

$socialUser->getId();      // provider user id — your stable key
$socialUser->getNickname();
$socialUser->getName();
$socialUser->getEmail();   // may be null
$socialUser->getAvatar();
$socialUser->getRaw();     // dump this when debugging an unfamiliar provider

Because the DTO is normalized, your "find or create user" service works identically for GitHub, Google, Facebook, and whatever else is in the driver map.

Centralize linking in an action, not a controller. A LinkSocialAccount action with handle(SocialUser $su, string $provider): User keeps controllers four lines long and makes the tricky logic (email match, account takeover checks, race conditions) unit-testable:

$account = SocialAccount::firstOrCreate(
    ['provider' => $provider, 'provider_user_id' => $su->getId()],
    ['nickname' => $su->getNickname(), 'avatar' => $su->getAvatar(), 'email' => $su->getEmail()]
);

if (! $account->wasRecentlyCreated) {
    return $account->user;
}
// else resolve-or-create user, then $user->socialAccounts()->save($account);

Link-on-login for existing users. For an authenticated user connecting a second provider, reuse the same action but force the user_id instead of resolving a user. Keep two entry points (login vs link) that share the persistence code — this avoids the classic bug where linking silently logs the user into a different account.

Stateless flows for APIs. If you are authenticating a mobile/SPA client, use ->stateless() so Socialite skips session state validation, and drive the redirect from the app rather than a server session. Do not mix stateless and stateful requests for the same provider in the same flow; the Invalid state error is almost always this.

Per-request scopes. Set sensible defaults in config, then widen per-flow:

SocialAuth::driver('github')->scopes(['user:email', 'read:user'])->redirect();

Ask for email scope explicitly — some providers only return an email when it is in scope.

Store tokens only if you have a reason. If you need to call the provider's API later, add token/refresh_token/expires_at columns and cast them encrypted on the model. If you only need login, don't store them at all — it's a liability.

Testing. You are testing your linking logic, not OAuth. Bind a fake driver or mock the facade so ->user() returns a hand-built SocialUser; then assert the user creation, the social_accounts row, and the redirect. Keep one slow, real integration test per provider against a staging app if you can.

Custom providers. Providers outside Socialite core are the package's selling point — check the driver map in config for a name-to-class mapping you can extend. Register your class and it gets the same DTO contract as the built-ins.

Gotchas and Tips

  • The redirect URI is exact-match. Trailing slash, http vs https, www vs apex, port. Register both local and production URIs in the provider console; a mismatch returns redirect_uri_mismatch before your code runs.
  • getEmail() can be null. Twitter/X never returns one; GitHub only with user:email scope and even then only if the user has a public or verified email. Your find-or-create path must have a fallback (prompt for email, or refuse to auto-create).
  • Do not auto-link by email blindly. If provider A returns [email protected] unverified, linking it to the existing local account is an account-takeover vector. Prefer explicit email verification, or only auto-link when the provider asserts a verified email.
  • Race conditions on the callback. A double-click or a provider retry can produce two parallel callbacks. Put a unique index on (provider, provider_user_id) and use firstOrCreate — otherwise you get duplicate rows that later break ->socialAccounts lookups.
  • config:cache will bite you. Env vars added after a cached config was built are invisible. Run php artisan config:clear whenever a provider "works locally but not on staging."
  • Facade caching between requests in tests. The manager may memoize drivers; call SocialAuth::forgetDrivers() (or re-instantiate) between test cases if you swap mocks per test.
  • Debugging unfamiliar providers: dump $socialUser->getRaw() first and confirm the email/nickname/id keys actually exist. Normalization is a best-effort mapping, not a guarantee, and some providers nest the payload differently than you expect.
  • Catch provider exceptions. Wrap ->user() in a try/catch for League\OAuth2\Client\Provider\Exception\IdentityProviderException and InvalidArgumentException; a denied consent screen throws, and an uncaught exception here is a scary stack trace for end users. Redirect back to login with a flash message.
  • Low adoption — pin the version and read the source. With near-zero dependents there is little community pressure on the maintainers, and the README will lag the code. composer require mad-web/laravel-social-auth:^x.y with an explicit constraint, and treat Laravel/Socialite major upgrades as a deliberate migration task rather than a blind composer update. If you hit a wall, dropping to Socialite directly (Socialite::driver($p)->user()) and keeping only the social_accounts model is a viable escape hatch — the persistence layer is the part worth keeping.
  • Extension point worth knowing: the driver map in config is the cleanest place to add a provider. Overriding the manager binding is possible but fragile; prefer registering a driver class over rebinding the facade.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib