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

Wn Sso Plugin Laravel Package

winter/wn-sso-plugin

Add single sign-on to Winter CMS with this plugin, letting users authenticate through an external identity provider instead of local credentials. Configure your provider and streamline secure access to the backend.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and migrate:

    composer require winter/wn-sso-plugin
    php artisan winter:up
    

    The plugin pulls in laravel/socialite as a dependency; if your app already requires Socialite, check for version conflicts before updating.

  2. Look here first: Backend → Settings → SSO. This is where providers live (stored in the database, not in config/services.php). Each record typically captures a code/name, a Socialite driver (e.g. google, github, gitlab), client ID, client secret, scopes, and an enabled flag.

  3. Configure the provider console. The settings form shows you the callback/redirect URL the plugin registers — it looks like:

    https://your-site.test/backend/sso/{code}/callback
    

    Paste that verbatim into Google/GitHub/etc. If your backend URI is customised (backendUri in config/cms.php), the path changes with it.

  4. First use case: internal team login. Create one provider (Google Workspace or GitHub org), leave the standard backend login form enabled as a break-glass admin path, and sign out/in through the "Sign in with …" button that appears on /backend.

  5. Verify end-to-end on staging with the real HTTPS hostname before touching production. SSO misconfiguration fails closed and can lock you out of the backend.

This is a small, low-adoption package (single-digit stars, no dependents). Read the source in plugins/winter/sso/ before relying on it — it is short, and you will need to know exactly which events and models it exposes.

Implementation Patterns

Keep providers in code, not just in the DB. The settings UI is convenient for discovery, but production config should be reproducible. Either seed providers from a migration/seeder, or write a small console command that upserts provider records from env vars:

SsoProvider::updateOrCreate(['code' => 'google'], [
    'driver'        => 'google',
    'client_id'     => env('SSO_GOOGLE_CLIENT_ID'),
    'client_secret' => env('SSO_GOOGLE_CLIENT_SECRET'),
    'is_enabled'    => true,
]);

Decide your user-matching policy explicitly. Winter backend users have both login and email columns. The two sane policies are:

  • Existing users only — look up by email, refuse to create; safest for a closed admin team.
  • Auto-provision — create a Backend\Models\User on first login, then assign a role and permissions in the same request:
Event::listen('backend.user.login', function ($user) {
    if ($user->roles->isEmpty()) {
        $user->addRole(Backend\Models\UserRole::where('code', 'publisher')->first());
    }
});

Never leave a freshly provisioned SSO user with default administrator rights.

Gate by identity, not just by provider. A provider being enabled only means "anyone with an account there can authenticate." Add an allowlist check (email domain, group claim, or an explicit table) in a backend.user.login listener or a small middleware on the backend routes. Reject by logging the user out and throwing a 403.

Custom / non-core drivers. Socialite core covers Google, GitHub, GitLab, Facebook, LinkedIn, Twitter, Bitbucket, Slack. For anything else (Azure AD, Okta, Keycloak), install the relevant socialiteproviders/* package and register it during plugin boot:

Socialite::extend('azure', fn ($app) => $app->make(AzureProvider::class));

The driver string in the provider record must match what you registered.

Layer SSO on top of password auth, not instead of it. Keep at least one local admin account with a password so a broken client secret or an expired IdP certificate doesn't take the site offline.

Test with mocks, not with real OAuth round-trips in CI. Socialite is mockable:

Socialite::shouldReceive('driver->user')->andReturn($fakeUser);

Drive the callback route directly and assert the resulting backend session, rather than trying to automate Google's consent screen.

Gotchas and Tips

  • Redirect URI mismatch is the #1 failure — and it's almost never a trailing slash. It's http vs https, www vs apex, port, or a backend URI prefix. Compare what the plugin displays against what the provider console stores, character by character.
  • Secrets are encrypted with APP_KEY. Rotating the key silently invalidates every stored client secret and every active session. Rotate the key and you must re-enter the secrets.
  • InvalidStateException / state mismatch usually means the session didn't survive the round-trip: cookie domain mismatch, SESSION_DOMAIN set to the wrong host, SameSite=Strict on the session cookie, a load balancer without sticky sessions, or SESSION_DRIVER=array. Fix the session, not the plugin.
  • Emails are not always present or verified. GitHub returns null for email unless you request the user:email scope, and Google Workspace accounts can differ from personal ones. Never match accounts on an unverified email — that's an account-takeover path if you allow auto-linking by email across providers.
  • Email case and login uniqueness. Winter enforces uniqueness on login and email. An IdP that changes its primary email, or two providers returning the same address, will throw a database exception mid-login, which surfaces as a useless 500. Catch and handle it.
  • The backend throttle doesn't cover the SSO path. Brute-forcing your callback is unlikely, but rate-limit it if the routes are public.
  • Settings are cached. After editing provider records out-of-band (seeder, tinker), run php artisan cache:clear. Socialite provider instances are resolved per request, so stale config in config/services.php also bites.
  • Debugging: Socialite swallows detail behind generic exceptions. Catch Laravel\Socialite\Two\InvalidStateException and GuzzleHttp\Exception\ClientException separately, log the IdP's error body (invalid_client, redirect_uri_mismatch, invalid_scope), and rethrow a clean message. Turn on APP_DEBUG only on staging.
  • Extension points are thin. You realistically have the plugin's models, the backend.auth.extendSignInView hook (to add your own login buttons or messaging), and the backend.user.login / backend.user.authenticate events. There's no formal driver contract beyond Socialite's, so if you need claim-based role mapping, plan to write it as a listener in your own plugin.
  • View overrides: you can override the plugin's partials by placing matching files in your own plugin's view path, but pin the version you override against — the plugin is small enough that a patch release can change markup.
  • Vendor/pin it. At this adoption level, a composer.lock pin plus a fork under your org is cheaper than discovering an abandoned dependency mid-incident. Do a five-minute read of the source before you ship it: it's a few hundred lines, and the audit cost is far lower than the debugging cost.
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.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics