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

Socialment Laravel Package

chrisreedio/socialment

Socialment adds Socialite OAuth login buttons to your Filament admin panel. Supports all Laravel Socialite providers, customizable buttons with icons, per-panel provider configuration, custom login hooks, and experimental SPA authentication.

View on GitHub
Deep Wiki
Context7

Getting Started

Socialment is a thin, opinionated bridge between Laravel Socialite and Filament's login page. It does one job — render provider buttons, run the OAuth dance, and log the user into your panel — and it's worth adopting on any Filament admin where you'd otherwise hand-roll a controller, a route, and a "connected accounts" table.

Three commands and one CSS line get you to a working button:

composer require chrisreedio/socialment
php artisan socialment:install

Then, in your panel's theme CSS (do this before you debug anything else — see Gotchas):

@source '../../../../vendor/chrisreedio/socialment/resources';

And in app/Providers/Filament/AdminPanelProvider.php:

$panel->plugins([
    \ChrisReedIO\Socialment\SocialmentPlugin::make()
        ->registerProvider('github', 'fab-github', 'GitHub'),
]);

The credentials themselves stay where Socialite expects them — config/services.php:

'github' => [
    'client_id' => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect' => env('GITHUB_REDIRECT_URI'),
],

Register https://yourdomain.com/login/github/callback (or /admin/login/github/callback if the panel is mounted under a path) in the provider's app console. Confirm the exact URL with php artisan route:list | grep callback rather than trusting the README — the route is generated from the panel's path, and a mismatch is the single most common reason a first integration fails.

Where to look first: php artisan vendor:publish --tag="socialment-config" and read the published config before touching the panel provider. The two things you'll almost certainly change are view.prompt ("Or Login Via") and models.user. Then skim the demo repo linked in the README — it's the fastest way to see the intended shape, though note it lags behind v4.

First real use case: internal admin panel where staff already have Google Workspace or Azure AD accounts. You want SSO on the login screen, no password management, and role assignment driven off directory groups. Socialment gives you the login; the pre-login hook gives you the authorization.

Implementation Patterns

Per-panel provider sets. The plugin is registered per panel, so a StaffPanelProvider and a CustomerPanelProvider can expose completely different buttons. This is the feature that justifies the package over a hand-rolled route: you don't need conditional Blade or middleware, just two ->registerProvider() chains.

// AdminPanelProvider
SocialmentPlugin::make()
    ->registerProvider('azure', 'fab-microsoft', 'Microsoft')
    ->registerProvider('github', 'fab-github', 'GitHub')

// PartnerPanelProvider
SocialmentPlugin::make()
    ->registerProvider('google', 'fab-google', 'Google Workspace')
    ->visible(fn () => app()->environment('production'))

->visible() takes a closure and is evaluated per render — use it to hide buttons in local/staging where you don't want stray OAuth accounts created, or to gate a provider behind a feature flag.

Community providers need two registrations, not one. Stock Socialite providers (GitHub, Google, Facebook, LinkedIn, Twitter, GitLab, Bitbucket) work out of the box. Anything from socialiteproviders.com — Azure, Okta, Keycloak, Discord, Slack — needs the SocialiteWasCalled listener wired up in addition to the Socialment registration. People forget the listener and get a Driver [azure] is not supported exception at the callback step. In Laravel 11+ the listener goes in a service provider's boot():

Event::listen(function (SocialiteWasCalled $event) {
    $event->extendSocialite('azure', \SocialiteProviders\Azure\Provider::class);
});

Pre-login hook is your authorization layer, not your login layer. This is the most valuable extension point in the package and the one I'd standardize on across a project. Everything that decides "should this person exist in my system" belongs here, throwing AbortedLoginException to stop the flow with a flash message:

Socialment::preLogin(function (ConnectedAccount $connectedAccount) {
    $user = $connectedAccount->user;

    // Domain allowlist — cheap, catches most mistakes
    if (! str_ends_with($user->email, '@yourcompany.com')) {
        throw new AbortedLoginException('Use your company account.');
    }

    // Directory group -> local role sync
    $groups = Http::withToken($connectedAccount->token)
        ->get('https://graph.microsoft.com/v1.0/me/memberOf')
        ->collect('value.displayName');

    $roles = Role::all()->filter(fn ($r) => $groups->contains($r->sso_group));
    $user->roles()->sync($roles);

    if ($user->roles->isEmpty()) {
        throw new AbortedLoginException('You are not authorized to access this application.');
    }
});

Note the pattern: provider groups are the source of truth, local roles are a projection. Re-sync on every login so revoking someone in Azure actually removes their access next time they hit the panel.

Post-login hook for audit and first-run setup. Keep this cheap and non-blocking — logging, last_login_at, maybe dispatching a job. Don't put authorization here; it runs after the session is already established.

Socialment::postLogin(function (ConnectedAccount $account) {
    $account->user->forceFill(['last_login_at' => now()])->save();
    activity()->performedOn($account->user)->log("SSO login via {$account->provider}");
});

Custom provider_name keys for multi-tenant. Because registerProvider()'s first argument is just a key into config/services.php, you can define two entries pointing at the same driver and route each panel to a different tenant:

'azure_staff' => ['client_id' => env('AZ_STAFF_ID'), ..., 'tenant' => env('AZ_STAFF_TENANT')],
'azure_clients' => ['client_id' => env('AZ_CLIENT_ID'), ..., 'tenant' => env('AZ_CLIENT_TENANT')],

Register azure_staff on the staff panel — the OAuth app registration is separate, so consent and redirect URL scoping stay clean.

Custom login route for non-default panels. If your panel login lives at filament.staff.auth.login rather than the default, pass it explicitly; otherwise a failed OAuth attempt redirects to a route that doesn't exist:

SocialmentPlugin::make()
    ->loginRoute('filament.staff.auth.login')
    ->registerProvider(...);

Views and branding. Publish --tag="socialment-views" to resources/views/vendor/socialment/ only when you need to change button markup; otherwise view.prompt plus Font Awesome icons (prefix fab- for brands) covers 95% of branding requests. Icons come from blade-fontawesome — if buttons render with missing glyphs, that's the dependency to check, not Socialment.

SPA mode: treat it as a spike, not a plan. Route::spaAuth('dashboard') plus Sanctum stateful domains plus SESSION_DOMAIN=.localhost does work for same-root-domain setups, but the README says "highly experimental" and means it. If you need this, isolate it behind a config flag so you can drop it wholesale when it changes shape.

Gotchas and Tips

The missing @source line is a silent failure. Without it in your theme.css, the buttons render unstyled — not invisible, just wrong — and you'll spend an hour in the wrong file. If you don't have a custom theme yet, create one first (php artisan make:filament-theme), then add the source and rebuild assets (npm run build / vite build). Editing CSS without rebuilding changes nothing.

Redirect URI mismatch is the #1 support ticket generator. Three separate places must agree: the provider console, services.php redirect, and the generated route. Provider consoles often reject http://localhost — use 127.0.0.1 or a tunnel with HTTPS. And clear config:cache after editing services.php; a cached config is a stale redirect.

Provider error page? Check the panel path. The documented pattern https://yourdomain.com/login/{provider}/callback assumes the panel is at the domain root. A panel on /admin needs /admin/login/github/callback. Always trust php artisan route:list over the docs.

Existing-user matching is implicit and worth understanding. Socialment stores connections in a ConnectedAccount model and resolves/creates the local user — the changelog calls out "handling existing user accounts" and there's a $createUserClosure on the plugin (with a v3.9.1 fix for a null-initialization bug in exactly that property). If your matching rule needs to be "match on email but only if the provider verified it," verify it in preLogin rather than assuming. Trusting an unverified provider email to link into an existing admin account is an account-takeover vector. Also: if you use a non-default user model, set models.user in the published config and make sure it's the model Filament's panel is configured with — a mismatch produces confusing auth failures.

AbortedLoginException is the only clean way to stop a login. Throwing anything else from a hook leaves a half-authenticated session or a 500. Wrap external service calls in the hook in try/catch and decide explicitly whether a directory outage should fail open or closed — for an admin panel, fail closed and rethrow as AbortedLoginException('Directory unavailable, try again shortly.').

Hook registration belongs in boot(), in one place. Register preLogin/postLogin in a dedicated SocialmentServiceProvider rather than scattered across panel providers. Closures registered twice execute twice, and the hook silently compounds.

Keep a local fallback login. Socialment adds buttons; it doesn't remove the password form. In production, keep at least one break-glass local account with a strong password, because an expired client secret or a misconfigured tenant will lock every SSO user out of the panel simultaneously. Consider ->visible(fn () => ! config('services.azure.emergency')) or similar.

Per-panel callbacks must be registered per panel. Each plugin instance generates its own callback route. Adding a third panel means a third redirect URL in the provider console — miss it and that panel's button dies at the callback while the others are fine.

Icon names are Font Awesome brand names. fab-github, fab-microsoft, fab-google, fab-linkedin — not fab-brands-github or fa-github. Search at fontawesome.com with the Brands filter and copy the fab- class. A wrong name renders nothing, silently.

It's a small package with limited blast radius — audit it yourself. Stars are modest, tests are explicitly "on the TODO list," and it's only been exercised at length against Azure AD. That's fine: the surface area is a Blade component, a couple of routes, and a model. Read SocialmentPlugin.php and the controller once before shipping to production — an afternoon of reading, and you'll be able to debug anything it throws.

SPA mode wants a root domain, not subdomains-on-different-roots. SESSION_DOMAIN must start with a dot (.localhost, .yourdomain.com) and both apps must share that root. If your frontend lives on a genuinely different domain, this feature will not work — use a token-based flow instead and skip the package's SPA layer entirely.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
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