Great question. There are two clean ways to integrate Laravel with Keycloak, depending on your app shape:
- A. Web login (server-rendered Laravel or Inertia) → OIDC Authorization Code + PKCE
- B. API backend (separate SPA/mobile frontend) → Bearer-only API that validates Keycloak JWTs
Below is a battle-tested, step-by-step for both.
A) Web login with Authorization Code + PKCE (recommended)
1) Create a client in Keycloak
- Realm → Clients → Create
- Client type: OpenID Connect
- Authentication: Off (public client)
- Standard flow (authorization code): On
- Valid redirect URIs:
https://yourapp.com/auth/callback
(+ local dev)
- Web origins: your app origin(s)
PKCE is built into OIDC code flow; Keycloak supports it out of the box. (Keycloak)
2) Add a Laravel OIDC client
Easiest path is Socialite + SocialiteProviders/Keycloak:
composer require laravel/socialite socialiteproviders/keycloak
config/services.php
:
'keycloak' => [
'client_id' => env('KEYCLOAK_CLIENT_ID'),
'client_secret' => env('KEYCLOAK_CLIENT_SECRET'), // for confidential clients; omit for public
'redirect' => env('KEYCLOAK_REDIRECT_URI'),
'base_url' => env('KEYCLOAK_BASE_URL'), // e.g. https://sso.example.com/realms/myrealm
],
Routes:
Route::get('/auth/redirect', fn() => Socialite::driver('keycloak')->redirect());
Route::get('/auth/callback', function () {
$oidcUser = Socialite::driver('keycloak')->user(); // ID token + profile
// map to local user, create/update, then login()
// ...
return redirect('/dashboard');
});
Follow the provider’s short docs for Laravel 10/11 event wiring. (socialiteproviders.com, GitHub)
3) Roles/claims mapping
In Keycloak, add Role mappers so tokens contain realm_access.roles
or client roles; read them in Laravel to set abilities/policies. (Keycloak)
4) Logout (SSO sign-out)
Redirect users to Keycloak’s end_session_endpoint
(from the realm’s OIDC discovery doc) so sessions end centrally. (Keycloak)
Why this flow? It is the OIDC standard, supports PKCE, and is the recommended modern path as vendor-specific adapters were deprecated in favor of standard OIDC. (Keycloak, GitHub)
B) Bearer-only Laravel API (JWT validation)
Use this when your SPA/mobile gets tokens directly from Keycloak and calls your Laravel API with Authorization: Bearer <token>
.
1) Create a Bearer-only client in Keycloak
- Client type: OIDC
- Standard flow: Off
- Service accounts: Off
- Direct access grants: Off (for pure bearer-only)
The API will only validate incoming tokens. (Keycloak)
2) Validate JWTs in Laravel
Option 1 — Package guard:
composer require robsontenorio/laravel-keycloak-guard
config/auth.php
(add a guard that uses the package driver), then protect routes with that guard/middleware. The package reads Keycloak’s realm JWKS and validates RS256 signatures. (GitHub, Laravel News)
Option 2 — Manual JWT validation: fetch JWKS from
https://<host>/realms/<realm>/.well-known/openid-configuration
# → "jwks_uri", typically /realms/<realm>/protocol/openid-connect/certs
Cache keys and verify iss
, aud
, exp
, and signature. (Keycloak, Cloud-IAM Documentation)
3) Authorize by roles/scopes
Read realm_access.roles
or resource_access[client].roles
from the token and gate routes/policies accordingly. (Keycloak)
C) Offload login at the edge (optional but nice for EKS/Ingress)
If you’d rather keep Laravel “dumb” about OIDC, put oauth2-proxy (or a similar reverse proxy) in front of it with the Keycloak OIDC provider. It handles the whole OIDC/PKCE dance and passes user info in headers (e.g., X-Auth-Request-User
). Great with NGINX/Ingress. (OAuth2 Proxy, GitHub, lf-onap.atlassian.net)
Quick checklists
Keycloak
- Realm, users/groups/roles
- Client (public or confidential) with redirect URIs & web origins
- (Optional) Mappers → add roles, email, name claims
- JWKS reachable for token verification (Keycloak)
Laravel
- Web flow: Socialite + Keycloak provider; handle callback; persist/link users; CSRF/session hardening. (socialiteproviders.com, GitHub)
- API flow: Keycloak guard or custom JWT validation against realm JWKS; 401/403 handling; role gates/policies. (GitHub, Cloud-IAM Documentation)
- Logout: call Keycloak end_session_endpoint. (Keycloak)
If you tell me which you’re building (SSR Laravel vs SPA+API, plus Laravel version), I’ll drop in exact auth.php
, middleware, and example controller/policy code tailored to your project.