Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Keycloak Guard Bundle Laravel Package

acsystems/keycloak-guard-bundle

Laravel guard/authentication bundle for integrating Keycloak. Adds a custom auth guard, handles token validation and user resolution from Keycloak, and supports protecting routes with Keycloak-backed authentication for API or web apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require acsystems/keycloak-guard-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        AcSystems\KeycloakGuardBundle\KeycloakGuardBundle::class => ['all' => true],
    ];
    
  2. Configuration Update config/packages/acsystems_keycloak_guard.yaml with your Keycloak realm details:

    keycloak:
        realm: 'your-realm'
        auth_server_url: 'https://your-keycloak-server/auth'
        resource: 'your-client-id'
        public_key: '-----BEGIN PUBLIC KEY-----...'  # From Keycloak admin console
    
  3. First Use Case: JWT Authentication Add the guard to security.yaml:

    security:
        firewalls:
            main:
                guard: keycloak
                stateless: true
    
  4. Verify Test with a JWT token in the Authorization: Bearer <token> header. The guard will validate it against Keycloak’s public key.


Implementation Patterns

Workflow: User Authentication Flow

  1. Token Acquisition Redirect users to Keycloak for login (e.g., /auth/realms/{realm}/protocol/openid-connect/auth). After login, Keycloak redirects back with a code or token in the URL/fragment.

  2. Token Validation Use the guard’s built-in validator:

    use AcSystems\KeycloakGuardBundle\Guard\KeycloakGuard;
    
    $guard = $this->get('security.guard.keycloak');
    $token = $request->headers->get('Authorization');
    $user = $guard->authenticate(new KeycloakToken($token));
    
  3. User Provider Integration Map Keycloak claims to your User entity via a custom UserProvider:

    class KeycloakUserProvider implements UserProviderInterface {
        public function loadUserByUsername($username) {
            // Fetch user from DB or API, then attach Keycloak claims.
            $user->setKeycloakClaims($this->decodeToken($token));
            return $user;
        }
    }
    
  4. Role-Based Access Use Keycloak’s realm_access.roles claim for authorization:

    security:
        access_control:
            - { path: ^/admin, roles: [admin] }
    

Integration Tips

  • Symfony Flex: Use make:guard to scaffold a custom guard if extending functionality.
  • API Platform: Combine with api_platform for JWT-protected endpoints:
    api_platform:
        formats:
            jsonld:
                mime_types: ['application/ld+json']
        patch_formats:
            json: true
            jsonld: true
    
  • Event Listeners: Hook into security.authentication.success to log or transform user data:
    public function onAuthenticationSuccess(AuthenticationSuccessEvent $event) {
        $user = $event->getUser();
        $user->setLastLogin(new \DateTime());
        $this->userManager->update($user);
    }
    

Gotchas and Tips

Pitfalls

  1. Public Key Rotation

    • Keycloak rotates public keys periodically. Cache the key and implement a refresh mechanism:
      $key = $this->getKeycloakPublicKey(); // Fetch fresh key
      $validator = new Lcobucci\JWT\Validation\Validator();
      $validator->setLeeway(60); // Allow 1-minute leeway for clock skew.
      
  2. Token Expiry Handling

    • Use Lcobucci\JWT\Token\Plain::fromString($token) to parse expiry claims. Redirect users to Keycloak if expired:
      if ($token->isExpired()) {
          return $this->redirectToKeycloakLogin();
      }
      
  3. Stateless vs. Stateful

    • The guard is stateless by default. For session-based apps, configure stateless: false and use HttpBasicAuthGuard as a fallback.
  4. Claim Mismatches

    • Keycloak claims (e.g., sub, email) may not match your User entity. Use a UserMapper to resolve conflicts:
      class KeycloakUserMapper {
          public function mapUserFromKeycloakClaims(array $claims): User {
              return User::create([
                  'username' => $claims['preferred_username'] ?? $claims['email'],
                  'email' => $claims['email'],
              ]);
          }
      }
      

Debugging

  • Enable Debug Mode Set debug: true in keycloak config to log JWT validation errors:

    keycloak:
        debug: true
    

    Check var/log/dev.log for detailed validation failures.

  • Token Inspection Decode JWTs manually to verify claims:

    curl -H "Authorization: Bearer $TOKEN" https://jwt.io
    

    Or use PHP:

    $decoded = (new Lcobucci\JWT\Parser())->parse($token)->decode();
    

Extension Points

  1. Custom Guard Extend KeycloakGuard to add pre-authentication logic:

    class CustomKeycloakGuard extends KeycloakGuard {
        public function authenticate(TokenInterface $token) {
            if (!$this->isValidTokenFormat($token)) {
                throw new BadCredentialsException('Invalid token format.');
            }
            return parent::authenticate($token);
        }
    }
    

    Register in services.yaml:

    services:
        App\Security\CustomKeycloakGuard:
            tags: [security.guard]
    
  2. Dynamic Realm Configuration Load realm/config from an API or database:

    $config = $this->configService->getKeycloakConfig();
    $guard->setConfig($config);
    
  3. Offline Tokens Support Keycloak’s offline access tokens by extending the validator:

    $validator->setIdClaim(new Lcobucci\JWT\Validation\Constraint\IssuedBy($config['auth_server_url']));
    $validator->setAudienceClaim(new Lcobucci\JWT\Validation\Constraint\PermittedBy($config['resource']));
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours