Adds OAuth-based Single Sign-On (SSO) authentication to the Winter CMS backend using Laravel Socialite. Allow your backend users to authenticate using their existing accounts from providers like Google, GitHub, Microsoft 365, and more.
- 8 Built-in Providers: GitHub, Google, Facebook, GitLab, Bitbucket, LinkedIn, Twitter (OAuth 1.0 & 2.0)
- Extensible: Easy integration with 100+ community Socialite Providers
- Provider Plugins: Install additional providers as separate plugins (e.g., Winter.SSOProviderMicrosoft)
- Event System: Comprehensive hooks for customizing authentication flow
- Security: SSO ID verification, email normalization, IP logging
- User Registration: Optionally create new users via SSO
- Native Auth Control: Disable username/password login, enforce SSO-only
- Audit Logging: Track all SSO authentication attempts
- Flexible Configuration: Environment-based or file-based setup
Install via Composer:
composer require winter/wn-sso-plugin
php artisan migrateIf using a public folder, republish assets:
php artisan winter:mirrorLet's set up GitHub authentication as an example:
- Go to GitHub Developer Settings
- Click "New OAuth App"
- Fill in the details:
- Application name: Your Site Name
- Homepage URL:
https://example.com - Authorization callback URL:
https://example.com/backend/winter/sso/handle/callback/github
- Save and copy your Client ID and Client Secret
Add to your .env file:
GITHUB_CLIENT_ID=your_client_id_here
GITHUB_CLIENT_SECRET=your_client_secret_hereEdit config/winter/sso/config.php (create if it doesn't exist):
<?php
return [
'enabled_providers' => [
'github',
],
];Visit /backend/auth/signin - you'll see a "Sign in with GitHub" button!
Configuration file: config/winter/sso/config.php
<?php
return [
/**
* List of enabled providers
* Only providers in this array will appear on the login page
*/
'enabled_providers' => [
'google',
'github',
// Add more providers here
],
/**
* Disable native username/password authentication
* Forces users to authenticate via SSO only
* If only one provider is enabled, redirects directly to it
*/
'prevent_native_auth' => false,
/**
* Allow new user registration via SSO
* If false, only existing users can login via SSO
*/
'allow_registration' => false,
/**
* Require explicit permission for SSO connections
* Users must explicitly allow each provider (requires backend UI - TODO)
*/
'require_explicit_permission' => false,
/**
* Default role for SSO-registered users
* Role code to assign to new users (TODO: not yet implemented)
*/
'default_role' => null,
/**
* Provider-specific configuration
* Credentials are typically loaded from environment variables
*/
'providers' => [
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'guzzle' => [], // HTTP client options
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'guzzle' => [],
],
// Add more providers...
],
];All providers use this URL pattern:
https://example.com/backend/winter/sso/handle/callback/{provider}
Examples:
- GitHub:
/backend/winter/sso/handle/callback/github - Google:
/backend/winter/sso/handle/callback/google - Microsoft:
/backend/winter/sso/handle/callback/microsoft
These providers are supported out of the box by Laravel Socialite:
| Provider | Config Key | Setup Guide |
|---|---|---|
| Bitbucket | bitbucket |
Bitbucket OAuth |
facebook |
Facebook Login | |
| GitHub | github |
GitHub OAuth Apps |
| GitLab | gitlab |
GitLab OAuth |
google |
Setup Guide | |
| LinkedIn (OpenID) | linkedin-openid |
LinkedIn OAuth |
| Twitter (OAuth 1.0) | twitter |
Twitter OAuth |
| Twitter (OAuth 2.0) | twitter-oauth-2 |
Twitter OAuth 2.0 |
Provider plugins package everything needed for a specific provider:
# Example: Install Microsoft 365 provider
composer require winter/wn-ssoprovidermicrosoft-pluginProvider plugins automatically:
- Install the Socialite provider package
- Register with Laravel Socialite
- Add configuration options
- Include provider logos and assets
See Creating Provider Plugins for building your own.
Use any provider from SocialiteProviders.com:
-
Install the provider package:
composer require socialiteproviders/microsoft
-
Register the provider in your plugin's
boot()method:Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { $event->extendSocialite('microsoft', \SocialiteProviders\Microsoft\Provider::class); });
-
Add configuration:
// config/winter/sso/config.php 'enabled_providers' => ['microsoft'], 'providers' => [ 'microsoft' => [ 'client_id' => env('MICROSOFT_CLIENT_ID'), 'client_secret' => env('MICROSOFT_CLIENT_SECRET'), 'tenant' => env('MICROSOFT_TENANT', 'common'), ], ],
-
Add provider logo (optional): Place an SVG at
/plugins/winter/sso/assets/images/providers/microsoft.svg
The plugin fires events at every stage of the authentication flow, allowing you to customize behavior:
Each event is provider-specific. Replace {provider} with your provider name (e.g., google, github).
Fires before OAuth authentication begins. Return false to abort.
Event::listen('winter.sso.google.authenticating', function () {
// Check if authentication should proceed
if (!some_condition()) {
return false; // Aborts authentication
}
});Fires after successful OAuth, before user lookup.
Event::listen('winter.sso.google.authenticated', function ($ssoUser) {
// $ssoUser is Laravel\Socialite\AbstractUser
Log::info('User authenticated via Google', [
'email' => $ssoUser->getEmail(),
'id' => $ssoUser->getId(),
]);
});Fires before creating a new user account. Throw exception to prevent registration.
Event::listen('winter.sso.google.beforeRegister', function ($ssoUser) {
// Only allow company email addresses
if (!str_ends_with($ssoUser->getEmail(), '@mycompany.com')) {
throw new AuthenticationException('Only company emails allowed');
}
});Fires after new user is created. Populate additional fields here.
Event::listen('winter.sso.google.registered', function ($user, $ssoUser) {
// $user is Backend\Models\User
// $ssoUser is Laravel\Socialite\AbstractUser
$user->fill([
'first_name' => $ssoUser->user['given_name'] ?? null,
'last_name' => $ssoUser->user['family_name'] ?? null,
]);
$user->save();
});Fires before session is created.
Event::listen('winter.sso.google.beforeLogin', function ($user, $ssoUser) {
// Perform checks before allowing login
if ($user->is_suspended) {
throw new AuthenticationException('Account suspended');
}
});Fires after successful login and session creation.
Event::listen('winter.sso.google.afterLogin', function ($user, $ssoUser) {
// Track login
Log::info('User logged in via Google', ['user_id' => $user->id]);
// Update last login timestamp
$user->last_sso_login = now();
$user->save();
});User SSO data is stored in the user's metadata:
// Get SSO data for a specific provider
$googleId = $user->getSsoValue('google', 'id');
$token = $user->getSsoValue('google', 'token');
// Set SSO data
$user->setSsoValues('google', [
'id' => '12345',
'token' => 'abc123',
'custom_field' => 'value',
]);Metadata structure: Backend\Models\User::metadata['winter.sso'][$provider][$key]
Once a user connects via a provider, their SSO ID is stored. On subsequent logins, the ID must match. This prevents account takeover if someone else registers the same email with a different provider.
// First login: ID stored
$user->setSsoValues('google', ['id' => '123456']);
// Later login: ID must match
if ($ssoUser->getId() !== $user->getSsoValue('google', 'id')) {
throw new InvalidSsoIdException();
}Emails are normalized to prevent duplicate accounts:
- Gmail: Dots are removed from usernames (
[email protected]→[email protected]) - All domains: Lowercased
All authentication attempts are logged with:
- Provider used
- Action taken
- User ID
- SSO provider ID
- Email provided
- IP address
- Metadata (remember me, etc.)
View logs: Settings → Logs → SSO Logs
The plugin automatically adjusts session.same_site from strict to lax when secure sessions are enabled, ensuring OAuth callbacks work correctly.
Cause: Provider not in enabled_providers array.
Solution: Add provider to config:
'enabled_providers' => ['github', 'google'],Cause: Session lost between redirect and callback, or CSRF protection too strict.
Solutions:
- Ensure sessions are working correctly
- Check
session.same_sitesetting (plugin auto-adjusts tolax) - Clear browser cookies and try again
Cause: User doesn't exist and allow_registration is false.
Solution: Either:
- Create the user account manually in the backend
- Enable registration:
'allow_registration' => true
Cause: User previously connected with a different account from the same provider.
Solution: This is a security feature. The user must use the original account, or an admin must clear the SSO data:
$user->setSsoValues('provider', ['id' => null]);Checklist:
- Provider is in
enabled_providersarray client_idis set in provider config- Environment variables are loaded correctly
- Assets are published (
php artisan winter:mirror)
Check the provider's setup guide in docs/providers/ for common issues.
Force SSO-only login:
'prevent_native_auth' => true,This will:
- Hide the username/password form
- Disable the login AJAX handler
- Show only SSO buttons
If only one provider is enabled, users are redirected directly to that provider.
Override button configuration per provider:
'providers' => [
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'button' => [
'label' => 'Custom Button Text',
'view' => 'your.custom.view', // Custom button template
],
],
],Request additional OAuth scopes:
'providers' => [
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'scopes' => ['user:email', 'read:user'],
],
],Configure HTTP client for providers behind proxies or with special requirements:
'providers' => [
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'guzzle' => [
'timeout' => 10,
'proxy' => 'http://proxy.example.com:8080',
],
],
],- Architecture Overview - How the plugin works internally
- Creating Provider Plugins - Build custom provider plugins
- Google Setup Guide - Detailed Google OAuth setup
Contributions are welcome! Please submit pull requests to the Winter CMS repository.
This plugin is licensed under the MIT License.