Installation Add the bundle via Composer:
composer require axium/identity
Enable the bundle in config/app.php under providers:
Axium\IdentityBundle\AxiumIdentityBundle::class,
Configuration Publish the default config:
php artisan vendor:publish --provider="Axium\IdentityBundle\AxiumIdentityBundle" --tag="config"
Update config/axium_identity.php with your OAuth2/Keycloak endpoints, client credentials, and scopes.
First Use Case: Authentication
Inject the AxiumIdentityClient into a controller or service:
use Axium\IdentityBundle\Client\AxiumIdentityClient;
public function __construct(private AxiumIdentityClient $identityClient) {}
public function login(Request $request) {
$token = $this->identityClient->getAccessToken($request->input('code'));
// Store token in session or database
}
Key Files to Review
config/axium_identity.php (configuration)src/Client/AxiumIdentityClient.php (core client logic)src/DependencyInjection/Configuration.php (schema validation)Redirect to Keycloak Generate an auth URL in a controller:
$authUrl = $this->identityClient->getAuthorizationUrl(['openid', 'profile']);
return redirect($authUrl);
Callback Handling Exchange the code for a token:
$token = $this->identityClient->getAccessToken($request->query->get('code'));
$userInfo = $this->identityClient->getUserInfo($token);
Token Storage
Store tokens in the session or database (e.g., using Laravel's auth:attempt with a custom user provider):
session(['access_token' => $token->getToken()]);
session(['refresh_token' => $token->getRefreshToken()]);
Automate token refresh and API calls:
public function fetchProtectedData() {
$response = $this->identityClient->get('/api/protected', [
'headers' => ['Authorization' => 'Bearer ' . session('access_token')]
]);
return json_decode($response->getBody(), true);
}
Extend Laravel's User model or create a custom guard:
// config/auth.php
'guards' => [
'keycloak' => [
'driver' => 'session',
'provider' => 'keycloak',
],
],
'providers' => [
'keycloak' => [
'driver' => 'custom',
'model' => Axium\IdentityBundle\User\KeycloakUser::class,
],
],
email, name) from /userinfo.Token Expiry Handling
public function handle($request, Closure $next) {
if (session('access_token_expires_at') < time()) {
$token = $this->identityClient->refreshToken(session('refresh_token'));
session(['access_token' => $token->getToken()]);
}
return $next($request);
}
CSRF in OAuth2 Redirects
state or nonce. Always include them:
$authUrl = $this->identityClient->getAuthorizationUrl(['openid'], [
'state' => bin2hex(random_bytes(32)),
'nonce' => bin2hex(random_bytes(32)),
]);
Configuration Overrides
.env:
// .env
KEYCLOAK_CLIENT_ID=your_client_id
KEYCLOAK_CLIENT_SECRET=your_secret
CORS Issues
theme or client-scopes.Enable Verbose Logging
Add to config/axium_identity.php:
'debug' => env('APP_DEBUG', false),
Logs will appear in storage/logs/laravel.log.
Validate Token Responses
Use dd($token->getArrayCopy()) to inspect token payloads for errors.
Keycloak Admin Console
Check /admin in Keycloak for client events, errors, or token revocations.
Custom Token Storage
Override AxiumIdentityClient to save tokens to a database:
class CustomIdentityClient extends AxiumIdentityClient {
public function storeToken(Token $token) {
TokenModel::create([
'access_token' => $token->getToken(),
'refresh_token' => $token->getRefreshToken(),
'expires_at' => $token->getExpiresAt(),
]);
}
}
Add Scopes Dynamically Extend the client to fetch available scopes from Keycloak:
public function getAvailableScopes() {
$response = $this->get('/protocol/openid-connect/scopes');
return json_decode($response->getBody(), true);
}
Laravel Events Dispatch events for token refreshes or user info updates:
event(new TokenRefreshed($token));
How can I help you explore Laravel packages today?