mohsen-mhm / laravel-keycloak-auth
Laravel package for Keycloak authentication integration using SocialiteProviders
Requires
- php: ^8.2
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
- laravel/socialite: ^5.0
- socialiteproviders/keycloak: ^4.0
Requires (Dev)
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0
- phpunit/phpunit: ^9.0|^10.0|^11.0
README
A Laravel package that provides seamless integration with Keycloak for authentication using SocialiteProviders. This package enables Laravel applications to authenticate users via a self-hosted Keycloak server with minimal configuration.
Features
- Easy integration with self-hosted Keycloak servers
- Automatic user creation and synchronization
- Web authentication for traditional Laravel Blade applications
- API authentication for separate frontend applications (React, Vue, etc.)
- Configurable authentication routes
- Middleware for protecting routes
- Laravel Sanctum token support for API authentication
- Support for multiple Laravel versions (9.x, 10.x, 11.x, 12.x)
- Built on top of Laravel Socialite and SocialiteProviders
Installation
Install the package via Composer:
composer require mohsen-mhm/laravel-keycloak-auth
For API authentication, install either Laravel Sanctum or Laravel Passport:
Option A: Laravel Sanctum (recommended for SPAs)
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Option B: Laravel Passport (full OAuth2 server)
composer require laravel/passport php artisan vendor:publish --tag=passport-migrations php artisan migrate php artisan passport:install
Publish the configuration file:
php artisan vendor:publish --tag=keycloak-auth-config
Environment Configuration
Add the following environment variables to your .env
file:
KEYCLOAK_SERVER_URL=http://localhost:8080 KEYCLOAK_REALM=your-realm KEYCLOAK_CLIENT_ID=your-client-id KEYCLOAK_CLIENT_SECRET=your-client-secret KEYCLOAK_AUTO_CREATE_USERS=true KEYCLOAK_VERIFY_SSL=true # Optional: API callback URL for separate frontends KEYCLOAK_API_CALLBACK_URL=http://your-frontend.com/auth/callback
Database Migration
Add a keycloak_id
column to your users table:
Schema::table('users', function (Blueprint $table) { $table->string('keycloak_id')->nullable()->unique(); });
Usage
This package supports two authentication modes:
1. Web Authentication (Traditional Laravel with Blade)
Basic Authentication
Redirect users to Keycloak for authentication:
// In your controller return redirect()->route('keycloak.login');
Or create a login link in your view:
<a href="{{ route('keycloak.login') }}">Login with Keycloak</a>
Protecting Routes
Use the provided middleware to protect your routes:
// In your routes/web.php Route::middleware(['keycloak.auth'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); Route::get('/profile', [ProfileController::class, 'show']); });
Logout
Logout from both Laravel and Keycloak:
// In your controller return redirect()->route('keycloak.logout');
Or create a logout link:
<form method="POST" action="{{ route('keycloak.logout') }}"> @csrf <button type="submit">Logout</button> </form>
2. API Authentication (Separate Frontend Applications)
Perfect for React, Vue, Angular, or mobile applications that need to authenticate with Keycloak and get API tokens.
Available API Endpoints
GET /api/auth/keycloak/login-url
- Get Keycloak login URLPOST /api/auth/keycloak/callback
- Handle OAuth callback and get tokensPOST /api/auth/keycloak/refresh
- Refresh expired tokensPOST /api/auth/keycloak/logout
- Logout and revoke tokensGET /api/auth/keycloak/user
- Get authenticated user info
Frontend Integration Example
Step 1: Get Login URL
const response = await fetch('/api/auth/keycloak/login-url?redirect_uri=http://your-frontend.com/callback'); const { login_url, state } = await response.json(); // Redirect user to Keycloak window.location.href = login_url;
Step 2: Handle Callback (when user returns from Keycloak)
// Extract code and state from URL parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); // Exchange code for tokens const response = await fetch('/api/auth/keycloak/callback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: code, state: state, redirect_uri: 'http://your-frontend.com/callback' }) }); const { access_token, keycloak_token, refresh_token, user } = await response.json(); // Store tokens securely localStorage.setItem('access_token', access_token); localStorage.setItem('refresh_token', refresh_token);
Step 3: Make Authenticated API Calls
const response = await fetch('/api/auth/keycloak/user', { headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, 'Accept': 'application/json' } }); const userData = await response.json();
Step 4: Refresh Tokens
const response = await fetch('/api/auth/keycloak/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: localStorage.getItem('refresh_token') }) }); const { access_token, refresh_token } = await response.json(); // Update stored tokens
Step 5: Logout
await fetch('/api/auth/keycloak/logout', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: localStorage.getItem('refresh_token') }) }); // Clear stored tokens localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token');
Protecting API Routes
You have two options for protecting API routes:
Option 1: Use Keycloak API Middleware (validates Keycloak tokens directly)
// In your routes/api.php Route::middleware(['keycloak.api'])->group(function () { Route::get('/protected-data', [ApiController::class, 'getData']); Route::post('/create-resource', [ApiController::class, 'create']); }); // Access Keycloak user info in your controller class ApiController extends Controller { public function getData(Request $request) { $keycloakUser = $request->get('keycloak_user'); // $keycloakUser contains: sub, email, name, preferred_username, etc. return response()->json(['data' => 'protected data']); } }
Option 2: Use Laravel Sanctum middleware (validates Laravel API tokens)
// In your routes/api.php Route::middleware(['auth:sanctum'])->group(function () { Route::get('/protected-data', [ApiController::class, 'getData']); Route::post('/create-resource', [ApiController::class, 'create']); }); // Access Laravel user in your controller class ApiController extends Controller { public function getData(Request $request) { $user = Auth::user(); // Laravel User model return response()->json(['data' => 'protected data']); } }
Option 3: Use Laravel Passport middleware (validates OAuth2 tokens)
// In your routes/api.php Route::middleware(['auth:api'])->group(function () { Route::get('/protected-data', [ApiController::class, 'getData']); Route::post('/create-resource', [ApiController::class, 'create']); }); // Access Laravel user in your controller class ApiController extends Controller { public function getData(Request $request) { $user = Auth::user(); // Laravel User model return response()->json(['data' => 'protected data']); } }
When to use which:
- Use
keycloak.api
when you want to validate Keycloak tokens directly (stateless, no database lookup) - Use
auth:sanctum
when you need simple API tokens for SPAs and mobile apps - Use
auth:api
when you need full OAuth2 server capabilities with Passport
3. Passport Authentication (OAuth2 Server)
For applications requiring full OAuth2 server capabilities with Laravel Passport.
Available Passport API Endpoints
GET /api/auth/keycloak/passport/login-url
- Get Keycloak login URLPOST /api/auth/keycloak/passport/callback
- Handle OAuth callback and get Passport tokensPOST /api/auth/keycloak/passport/refresh
- Refresh expired tokensPOST /api/auth/keycloak/passport/logout
- Logout and revoke tokensGET /api/auth/keycloak/passport/user
- Get authenticated user info
Passport Frontend Integration Example
Step 1: Get Login URL
const response = await fetch('/api/auth/keycloak/passport/login-url?redirect_uri=http://your-frontend.com/callback'); const { login_url, state } = await response.json(); // Redirect user to Keycloak window.location.href = login_url;
Step 2: Handle Callback
// Extract code and state from URL parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); // Exchange code for Passport OAuth2 tokens const response = await fetch('/api/auth/keycloak/passport/callback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: code, state: state, redirect_uri: 'http://your-frontend.com/callback' }) }); const { access_token, token_type, expires_in, keycloak_token } = await response.json(); // Store OAuth2 tokens localStorage.setItem('access_token', access_token); localStorage.setItem('token_type', token_type);
Step 3: Make Authenticated API Calls
const response = await fetch('/api/auth/keycloak/passport/user', { headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}`, 'Accept': 'application/json' } }); const userData = await response.json();
Using the Service
You can also use the KeycloakAuthService directly:
use MohsenMhm\LaravelKeycloakAuth\Services\KeycloakAuthService; class YourController extends Controller { public function __construct(private KeycloakAuthService $keycloakAuth) { } public function checkKeycloakStatus() { if ($this->keycloakAuth->isKeycloakAvailable()) { return 'Keycloak is available'; } return 'Keycloak is not available'; } }
Configuration
The package configuration file (config/keycloak-auth.php
) allows you to customize:
- Keycloak server settings (URL, realm, client credentials)
- Authentication routes
- User model and field mapping
- Redirect URLs after login/logout
- Auto user creation settings
Keycloak Setup
- Create a new client in your Keycloak realm
- Set the client type to "OpenID Connect"
- Enable "Client authentication"
- Add valid redirect URIs:
- For web authentication:
http://your-app.com/auth/keycloak/callback
- For API authentication:
http://your-frontend.com/auth/callback
(or your frontend's callback URL)
- For web authentication:
- Configure the client scopes as needed (openid, profile, email)
- Set the access type to "confidential"
- Enable "Authorization Code Flow" in the authentication flow settings
Requirements
- PHP 8.2 or higher
- Laravel 9.x, 10.x, 11.x, or 12.x
- A running Keycloak server
- Laravel Sanctum (for simple API authentication) OR Laravel Passport (for OAuth2 server)
Security Considerations
- Always use HTTPS in production
- Keep your Keycloak client secret secure
- Regularly update the package and its dependencies
- Configure proper CORS settings in Keycloak
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This package is open-sourced software licensed under the MIT license.
Support
If you encounter any issues or have questions, please create an issue on the GitHub repository.