dmstr/keycloak-security-bundle
To begin using this package, ensure your project meets the minimum PHP 8.4 requirement (⚠️ BREAKING in v0.2.0). Install via Composer:
composer require vendor/package-name
Keycloak integration now includes a KeycloakUser model and KeycloakUserProvider, designed for seamless OAuth2/Keycloak authentication. Start by:
php artisan vendor:publish --provider="Vendor\Package\ServiceProvider"
.env and config/keycloak.php with your Keycloak realm, client ID, and credentials.AuthServiceProvider:
public function boot()
{
$this->app['auth']->provider('keycloak', function ($app) {
return new \Vendor\Package\KeycloakUserProvider($app['config']['keycloak']);
});
}
KeycloakUser model in your User trait or extend it directly.For testing, the package now includes unit tests for KeycloakUser/KeycloakUserProvider, which can serve as a reference for your own implementations.
Authentication Flow:
use Vendor\Package\Facades\KeycloakAuth;
return KeycloakAuth::redirectToKeycloak();
/auth/keycloak/callback) with middleware:
Route::get('/auth/keycloak/callback', [AuthController::class, 'handleKeycloakCallback']);
$user = $this->auth->guard('web')->user(); // If using default guard
// Or manually:
$provider = app('auth')->createUserProvider('keycloak');
$user = $provider->retrieveByCredentials(['id_token' => $request->id_token]);
User Model Integration:
KeycloakUser or use it as a trait:
use Vendor\Package\Models\KeycloakUser;
class User extends Authenticatable implements KeycloakUser
{
// ...
}
email, name) to your DB:
$user->syncFromKeycloak($keycloakUserData);
Role/Group Mapping:
KeycloakUserProvider to resolve roles/groups from the token:
$roles = $provider->getUserRoles($user);
Token Refresh:
Leverage the provider’s refreshToken() method if your Keycloak client supports silent refresh:
$provider->refreshToken($user->refresh_token);
Custom Claims:
Extend KeycloakUserProvider to handle custom claims:
class CustomKeycloakUserProvider extends KeycloakUserProvider
{
public function getCustomClaim($user, $claimName)
{
return $this->getKeycloakClient()->getCustomClaim($user->id_token, $claimName);
}
}
CI/CD: The package now includes a CI workflow (GitHub Actions) as a template. Adapt it for your pipeline to run tests against your Keycloak instance.
Token Validation:
TokenExpiredException gracefully:
try {
$user = $provider->retrieveByCredentials(['id_token' => $token]);
} catch (\Vendor\Package\Exceptions\TokenExpiredException $e) {
return redirect()->route('keycloak.login')->with('error', 'Session expired');
}
validateToken() method to check token validity before processing.Role/Group Sync:
KeycloakUserProvider:
protected function mapKeycloakRoleToLocal($role)
{
return strtolower(str_replace(' ', '_', $role));
}
CI/CD Quirks:
KEYCLOAK_URL, KEYCLOAK_ADMIN_CREDENTIALS) to configure it dynamically.Enable Debugging:
Set KEYCLOAK_DEBUG=true in .env to log raw token responses and errors.
Token Inspection: Decode Keycloak tokens manually to verify claims:
php -r 'echo base64_decode(explode(".", "YOUR_TOKEN_HERE")[1]);'
Middleware Order:
Ensure KeycloakAuthMiddleware runs after VerifyCsrfToken but before Authenticate in your middleware stack to avoid token validation issues.
Custom User Provider:
Override KeycloakUserProvider to add logic (e.g., custom user resolution):
class CustomUserProvider extends KeycloakUserProvider
{
public function retrieveById($identifier)
{
// Custom logic to fetch user from DB + Keycloak
}
}
Event Listeners:
Listen for KeycloakUserLoggedIn or KeycloakTokenRefreshed events to trigger post-auth actions:
Event::listen(KeycloakUserLoggedIn::class, function ($user) {
// Log user activity or update last_login_at
});
Testing: Use the provided unit tests as a scaffold for your own tests. Mock the Keycloak HTTP client:
$mockClient = Mockery::mock(\Vendor\Package\KeycloakClient::class);
$provider = new KeycloakUserProvider($config, $mockClient);
How can I help you explore Laravel packages today?