mmanos/laravel-social
mmanos/laravel-social adds OAuth social login to Laravel apps, letting users sign in with providers like Facebook, Twitter, Google, and GitHub. It handles provider configuration, authentication flows, and user account integration through a simple, configurable API.
Install the package with Composer ("mmanos/laravel-social": "dev-master" in Laravel 4's require block, then composer update), register Mmanos\Social\SocialServiceProvider in app/config/app.php's providers array, and add the Social alias pointing at Mmanos\Social\Facades\Social. Publish the config with php artisan config:publish mmanos/laravel-social; everything you will touch day-to-day lives in app/config/packages/mmanos/laravel-social/config.php.
Before writing any code, skim two things in the repo: the published config file (to see the exact provider keys and the client_id / client_secret / redirect / scope shape) and the driver directory under src/Mmanos/Social/Drivers. The drivers tell you which providers are actually supported and what fields each returns — that is faster than guessing from the README.
The first real use case is the classic two-route flow: a "connect" route that redirects the browser to the provider and a "callback" route that exchanges the returned code for a user. The package is built around a driver manager, so the entry points are Social::driver('facebook')->login() (returns a redirect response) and Social::driver('facebook')->getUser() inside the callback. Wire those into routes, var_dump the returned user object once to see its actual shape, and only then start persisting.
You need provider credentials issued before you start: create an app in each provider's developer console and paste the redirect URI into both the console and the package config. Until those two strings match exactly, every login attempt fails at the provider, not in Laravel.
Driver-per-request routing. The idiomatic pattern is a single pair of routes with a {provider} parameter, resolving the driver from the route segment. This keeps one controller method serving Facebook, Google, GitHub, etc., and means adding a provider is a config-only change:
Route::get('auth/{provider}', 'AuthController@redirect');
Route::get('auth/{provider}/callback', 'AuthController@callback');
Persist, don't just authenticate. getUser() hands you an identity, not a session. The useful pattern is: look up a social_accounts row by (provider, provider_uid); if found, log in the owning user; if not, try to match on verified email and link the account; otherwise create a user. Store provider, provider_uid, access_token, refresh_token, and expires_at in a dedicated table rather than columns on users — most apps end up with users linked to more than one provider.
Separate the OAuth dance from your domain. Keep the callback controller thin: resolve the driver, fetch the user, hand a normalized array to a service or repository that does the find-or-create. The package's user object is provider-specific and will change shape across drivers, so normalize early (uid, name, email, avatar) and let the rest of the app depend only on your own DTO.
Account linking for logged-in users. The same callback route should check Auth::check() first. If a user is already authenticated, treat the callback as "attach this provider to my account" and redirect to a settings page instead of logging them in. This is where users recover from having accidentally created a second account.
Avatars and profiles as a cache, not a truth. Pull the avatar URL and display name out of getUser() on login, but store them locally (or re-fetch on a schedule) — provider CDN URLs rotate and get rate-limited. Refreshing on each login is usually enough.
Testing. Because the driver is resolved through a manager, you can bind a fake driver into the container in tests and assert your callback controller's behavior without touching the network. Do this early: OAuth callbacks are the single hardest thing to test once a controller has grown.
Redirect URI mismatches are the number one failure. http vs https, www vs bare domain, and a trailing slash are all distinct registered URIs. Configure the callback as an absolute URL, not a relative one, and copy it verbatim into the provider console. When a provider returns "invalid redirect_uri," check this before anything else.
Twitter has no email. OAuth 1.1 providers commonly omit the email field, so any find-or-create logic that assumes a unique email will silently create duplicates or crash. Always have a fallback path — either prompt for email after the callback or match on (provider, uid) only.
State and session fragility. The flow depends on the session surviving the round trip. If your callback route is behind a different domain, a load balancer without sticky sessions, or a cookie with SameSite=Strict, the state check fails and you get cryptic "invalid state" errors. Keep the connect and callback routes on the same host and cookie scope.
Don't put CSRF tokens in the way. In Laravel 4, the callback is a GET request from the provider, so it is not CSRF-protected anyway — but if you later change it, or if your connect route is POST, exclude it from Csrf filtering deliberately rather than debugging 419s.
Tokens are secrets. Encrypt access_token/refresh_token at rest or at minimum keep them out of logs, exception reports, and var_dump output. The raw provider payload returned alongside the user often contains the token — scrub it before storing the "raw" blob.
Config caching trap. Laravel 4 doesn't have config:cache, but the package's namespaced config (Config::get('laravel-social::...')) is easy to override in the wrong place. If your edits to app/config/packages/... don't take effect, confirm you are editing the published file, not a copy in app/config root, and that the environment-specific folder (config/packages/mmanos/laravel-social/local/) isn't shadowing it.
Per-environment credentials. Publish environment subfolders for local and production and keep separate provider apps. Sharing a production client secret with local dev is how test accounts end up in your real user table.
Error paths are silent. Providers return ?error=access_denied when a user cancels; getUser() has nothing to exchange and either throws or returns null. Handle the error query parameter in your callback explicitly and redirect to a friendly page — otherwise a cancelled login shows a stack trace.
Scope changes require re-consent. Adding a scope after users have logged in won't retroactively grant it; those users need to go through the connect flow again. Design the callback to be safely re-runnable for an already-linked account so a re-connect is a click, not a support ticket.
Modernization note. This package targets Laravel 4 (EOL since 2015) and last shipped in 2014. If you are maintaining it day-to-day, budget for periodic breakage as providers deprecate OAuth endpoints — Facebook's Graph API versioning and Twitter's OAuth 1.1→2.0 migration are the usual breakers. The extension point is the driver: subclass or copy an existing driver under Mmanos\Social\Drivers, register it as a custom driver if the manager supports it, and swap the provider endpoint there rather than patching vendor code. For greenfield work on any supported Laravel version, the equivalent functionality lives in Laravel Socialite; treat this package as legacy-only.
How can I help you explore Laravel packages today?