dcplibrary / entra-sso
Simple Entra (Azure AD) SSO package for Laravel 12
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/dcplibrary/entra-sso
Requires
- php: ^8.2|^8.3
- guzzlehttp/guzzle: ^7.2
- illuminate/http: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
README
Entra SSO Package for Laravel
A simple, reusable Entra (Azure AD) Single Sign-On package for Laravel 12 with role mapping, group sync, token refresh, and custom claims support.
Table of Contents
- Features
- Requirements
- Installation
- Usage
- Starter Kit Configuration
- Development
- Troubleshooting
- License
Features
- ✅ Easy Azure AD/Entra authentication
- ✅ Auto-create users on first login
- ✅ Group synchronization and role mapping
- ✅ Automatic token refresh for long sessions
- ✅ Custom claims handling
- ✅ State validation for CSRF protection
- ✅ Configurable via environment variables
- ✅ Works across multiple Laravel instances
Requirements
- PHP: 8.2 or higher
- Laravel: 12.0 or higher
- Session Driver: Any (database, redis, file, etc.)
- Database: Any supported by Laravel (MySQL, PostgreSQL, SQLite, etc.)
Azure AD Setup
Before installing the package, you'll need to configure an application in Azure AD:
- Register an application in Azure AD
- Configure redirect URIs
- Generate a client secret
- Note your Tenant ID and Client ID
📖 Detailed setup instructions: Azure AD Configuration Guide
Framework Compatibility
This package is framework-agnostic and works with all Laravel frontend stacks:
| Starter Kit | Compatible | Notes |
|---|---|---|
| None (Blade only) | ✅ Yes | Recommended - zero conflicts |
| React (Inertia) | ⚠️ Conflicts | Uses Breeze auth - see note below |
| Vue (Inertia) | ⚠️ Conflicts | Uses Breeze auth - see note below |
| Livewire | ⚠️ Conflicts | Uses Fortify auth - see note below |
| Laravel Breeze | ⚠️ Conflicts | See note below |
| Laravel Jetstream | ⚠️ Conflicts | See note below |
⚠️ Important: Starter Kit Conflicts
Laravel starter kits provide authentication features that conflict with Entra SSO:
- Competing login routes (
/login) - Email verification (redundant - Azure AD verifies emails)
- Password management (redundant - Azure AD manages passwords)
- Two-factor authentication (redundant - Azure AD provides MFA)
All starter kits from laravel new include authentication, which conflicts with Entra SSO:
- React → Installs Laravel Breeze with Inertia + React
- Vue → Installs Laravel Breeze with Inertia + Vue
- Livewire → Installs Livewire with Fortify authentication
Recommended Installation Approaches:
Option 1: None (No starter kit) - Recommended
laravel new my-app # When prompted: Select "None" cd my-app composer require dcplibrary/entra-sso php artisan entra:install
This is the cleanest approach with zero conflicts. You can still use React/Vue/Livewire by installing them separately without auth.
Option 2: Starter kit with auto-fix
laravel new my-app # Select React, Vue, or Livewire cd my-app composer require dcplibrary/entra-sso php artisan entra:install --fix-starter-kit
The install command will detect and fix authentication conflicts automatically.
Option 3: Manual installation (existing project) If you already have a starter kit installed, see Starter Kit Configuration below.
Fresh vs Existing Laravel Install
Fresh Laravel Install (Recommended):
composer create-project laravel/laravel my-app
cd my-app
composer require dcplibrary/entra-sso
php artisan entra:install
Existing Laravel Install:
- ✅ Works with existing User model (gets extended automatically)
- ✅ Works with existing authentication
- ✅ Works with existing migrations
- ⚠️ Backup your
app/Models/User.phpfirst (command creates backup) - ⚠️ Review the User model changes after running
entra:install
Installation
Quick Install (Recommended)
Install via Composer and run the interactive setup wizard:
composer require dcplibrary/entra-sso php artisan entra:install
The entra:install command will:
- Prompt for Azure AD credentials and add to
.env - Automatically update
app/Models/User.phpto extend EntraUser - Fix the
casts()method to merge with parent - Run migrations
- Optionally publish config and views
Manual Installation
If you prefer to set up manually:
1. Install Package
composer require dcplibrary/entra-sso
2. Environment Variables
The package configuration is automatically available. Just add these variables to your .env file:
ENTRA_TENANT_ID=your-tenant-id ENTRA_CLIENT_ID=your-client-id ENTRA_CLIENT_SECRET=your-client-secret ENTRA_REDIRECT_URI="${APP_URL}/auth/entra/callback" ENTRA_AUTO_CREATE_USERS=true # Group & Role Sync ENTRA_SYNC_GROUPS=true ENTRA_SYNC_ON_LOGIN=true ENTRA_DEFAULT_ROLE=user # Group to Role Mapping (simple format) # Maps Azure AD group names to application roles ENTRA_GROUP_ROLES="IT Admins:admin,Developers:developer,Staff:user" # Token Refresh ENTRA_ENABLE_TOKEN_REFRESH=true ENTRA_REFRESH_THRESHOLD=5 # Custom Claims ENTRA_STORE_CUSTOM_CLAIMS=false
3. Run Migrations
The package migrations will run automatically. Just run:
php artisan migrate
4. Update User Model
Edit app/Models/User.php and make these two required changes:
Change #1 - Import and extend the package User model:
// Change this: use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable // To this: use Dcplibrary\EntraSSO\Models\User as EntraUser; class User extends EntraUser
Change #2 - Merge parent casts:
// Change this: protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; } // To this: protected function casts(): array { return array_merge(parent::casts(), [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]); }
Complete example:
<?php namespace App\Models; use Dcplibrary\EntraSSO\Models\User as EntraUser; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Notifications\Notifiable; class User extends EntraUser { use HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', // Note: Entra fields are automatically included via getFillable() ]; protected $hidden = [ 'password', 'remember_token', ]; protected function casts(): array { return array_merge(parent::casts(), [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]); } }
5. (Optional) Publish Assets
The package works out of the box without publishing anything. However, you can publish assets if you need to customize them:
Publish config (only needed for advanced scenarios):
php artisan vendor:publish --tag=entra-sso-config
Publish config if you need:
- Complex group mappings (multiple groups → one role)
- Custom claims field mapping
- Role model integration (e.g., Spatie Permissions)
For simple group-to-role mapping, use ENTRA_GROUP_ROLES in .env instead.
Publish views (only if you need to customize the login view):
php artisan vendor:publish --tag=entra-sso-views
Command Options
The entra:install command supports several options:
# Automatically detect and fix starter kit conflicts php artisan entra:install --fix-starter-kit # Skip User model modifications (if already done) php artisan entra:install --skip-user-model # Skip environment variable setup (if already configured) php artisan entra:install --skip-env # Force overwrite existing configuration php artisan entra:install --force
The wizard will automatically detect Breeze/Jetstream/Fortify and prompt to fix conflicts.
Usage
Login Button
<a href="{{ route('entra.login') }}">Sign in with Microsoft</a>
Protect Routes
// By role Route::middleware(['auth', 'entra.role:admin'])->group(function () { Route::get('/admin', [AdminController::class, 'index']); }); // By Entra group Route::middleware(['auth', 'entra.group:IT Admins'])->group(function () { Route::get('/servers', [ServerController::class, 'index']); });
Using with Existing Authentication
You can use Entra SSO alongside traditional email/password authentication:
Option 1: Redirect to Entra SSO
// routes/web.php Route::get('/login', function () { return redirect()->route('entra.login'); });
Option 2: Add SSO button to existing login page
<!-- resources/views/auth/login.blade.php --> <div class="mb-4"> <a href="{{ route('entra.login') }}" class="btn btn-primary w-full"> Sign in with Microsoft </a> </div> <div class="divider">OR</div> <!-- Your existing email/password form --> <form method="POST" action="{{ route('login') }}"> <!-- ... --> </form>
Option 3: Use only Entra SSO
// config/auth.php - No changes needed! // The package works with Laravel's default auth setup
Group to Role Mapping
Map Azure AD groups to application roles using the .env file:
ENTRA_GROUP_ROLES="IT Admins:admin,Developers:developer,Staff:user"
How it works:
- When a user logs in, their Azure AD groups are synced
- Groups are matched against the
ENTRA_GROUP_ROLESmapping - The first matching group sets the user's role
- If no groups match, the
ENTRA_DEFAULT_ROLEis used
Advanced mapping (requires publishing config):
If you need complex mappings (multiple groups → one role), publish the config:
php artisan vendor:publish --tag=entra-sso-config
Then edit config/entra-sso.php:
'group_role_mapping' => [ 'IT Admins' => 'admin', 'Computer Services' => 'admin', // Multiple groups can map to same role 'Developers' => 'developer', 'Staff' => 'user', ],
Starter Kit Configuration
If you already have a Laravel starter kit installed, you'll need to configure it to work with Entra SSO.
Auto-detection: The entra:install command automatically detects and can fix these configurations. Run with --fix-starter-kit flag for non-interactive fixing.
React/Vue Starter Kits (Inertia + Breeze)
The React and Vue options from laravel new install Laravel Breeze with Inertia. Follow the same steps as Laravel Breeze below.
The entra:install command will automatically detect Breeze (via routes/auth.php) and offer to fix conflicts.
Livewire Starter Kit (Fortify)
The Livewire starter kit uses Laravel Fortify for authentication. Make these changes:
1. Disable Fortify's login views (config/fortify.php):
'views' => false, // Change from true to false
This prevents Fortify from registering its own /login route.
2. Remove email verification middleware (routes/web.php):
// Before: Route::get('/dashboard', function () { //... })->middleware(['auth', 'verified']); // After (remove 'verified'): Route::get('/dashboard', function () { //... })->middleware(['auth']);
Azure AD already verifies emails, so this middleware is redundant and blocks SSO users.
3. (Optional) Disable unused Fortify features (config/fortify.php):
'features' => [ // Features::registration(), // Disabled - users created via SSO // Features::resetPasswords(), // Disabled - Azure AD manages passwords // Features::emailVerification(), // Disabled - Azure AD verifies emails // Features::updateProfileInformation(), // Disabled - managed via Azure AD // Features::updatePasswords(), // Disabled - Azure AD manages passwords // Features::twoFactorAuthentication([ // Disabled - Azure AD provides MFA // 'confirm' => true, // 'confirmPassword' => true, // ]), ],
Laravel Breeze
1. Remove Breeze routes (routes/auth.php or routes/web.php):
// Comment out or remove all Breeze auth routes // require __DIR__.'/auth.php';
2. Redirect login to Entra (routes/web.php):
Route::get('/login', function () { return redirect()->route('entra.login'); })->name('login');
3. Remove email verification middleware (same as Fortify above).
Laravel Jetstream
1. Disable Jetstream features (config/jetstream.php):
'features' => [ // Features::termsAndPrivacyPolicy(), // Features::profilePhotos(), // Features::api(), // Features::teams(['invitations' => true]), // Features::accountDeletion(), ],
2. Redirect login (same as Breeze above).
3. Remove verification middleware (same as above).
Why These Changes Are Needed
Azure AD/Entra SSO already provides:
- ✅ User authentication - No need for password-based login
- ✅ Email verification - Microsoft verifies all emails
- ✅ Password management - Handled by Azure AD
- ✅ Two-factor authentication - Azure AD provides MFA
- ✅ Password reset - Managed through Azure AD portal
- ✅ Profile management - Managed through Azure AD
Starter kits provide these same features, creating conflicts and redundancy.
Documentation
Starter kits provide these same features, creating conflicts and redundancy.
Development
Local Package Development
If you're developing this package locally and want to test changes in a Laravel application, you can use the automated setup script or do it manually.
Option 1: Automated Setup (Recommended)
- Clone this repository alongside your Laravel app:
cd /path/to/your/projects git clone https://github.com/dcplibrary/entra-sso.git cd your-laravel-app
- Run the development setup script:
bash ../entra-sso/setup-dev.sh
The script will:
- Add path repository to composer.json
- Install the package locally
- Update User model to extend EntraUser
- Add environment variables
- Run migrations
- Optionally publish config and views
Important: The script will prompt you to manually fix the casts() method in app/Models/User.php. This is required!
Option 2: Manual Setup
- Clone this repository alongside your Laravel app:
cd /path/to/your/projects git clone https://github.com/dcplibrary/entra-sso.git cd your-laravel-app
- Add to your Laravel app's
composer.json:
{
"repositories": [
{
"type": "path",
"url": "../entra-sso"
}
],
"require": {
"dcplibrary/entra-sso": "*"
}
}
- Install or update the package:
composer update dcplibrary/entra-sso
-
Follow the installation steps from the main README (environment variables, User model updates, migrations)
-
Make changes in the
entra-ssodirectory, then refresh in your Laravel app:
# Publish updated config if needed php artisan vendor:publish --tag=entra-sso-config --force # Clear caches php artisan config:clear php artisan cache:clear
Contributing
See CONTRIBUTING.md for development guidelines.
Troubleshooting
Having issues? Check our comprehensive troubleshooting guide:
Common issues covered:
- Login redirects not working
- "Invalid state parameter" errors
- User not being created automatically
- Groups not syncing
- Token refresh failures
- Middleware permission errors
📖 Full troubleshooting guide: Troubleshooting Guide
Still stuck? Open an issue on GitHub
License
MIT