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 Bearer Only Adapter Bundle Laravel Package

abel/keycloak-bearer-only-adapter-bundle

Symfony bundle to secure APIs with Keycloak Bearer-Only clients. Provides adapter and configuration (issuer, realm, client id/secret) via Symfony Flex recipe or manual YAML/.env setup. Supports Keycloak distribution differences (e.g., /auth removal).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require abel/keycloak-bearer-only-adapter-bundle
    composer config extra.symfony.allow-contrib true
    

    Run composer update to apply the Symfony Flex recipe.

  2. Configure Keycloak

    • Ensure you have a Bearer-Only client in Keycloak.
    • Set environment variables in .env:
      OAUTH_KEYCLOAK_ISSUER=http://localhost:8080/auth/realms/{realm}
      OAUTH_KEYCLOAK_REALM={realm}
      OAUTH_KEYCLOAK_CLIENT_ID={client_id}
      OAUTH_KEYCLOAK_CLIENT_SECRET={client_secret}
      
  3. Enable the Bundle Add to config/bundles.php (if not auto-loaded):

    return [
        // ...
        Abel\KeycloakBearerOnlyAdapterBundle\AbelKeycloakBearerOnlyAdapterBundle::class => ['all' => true],
    ];
    
  4. First Use Case: Protect a Controller Annotate a controller method with [BearerOnly]:

    use Abel\KeycloakBearerOnlyAdapterBundle\Annotation\BearerOnly;
    
    class ApiController extends AbstractController
    {
        #[BearerOnly]
        public function secureEndpoint(): JsonResponse
        {
            return new JsonResponse(['message' => 'Access granted!']);
        }
    }
    

Implementation Patterns

Workflow: API Security Layer

  1. Authentication Middleware The bundle integrates with Symfony’s security system. Use it alongside api_platform or mercure for granular control:

    # config/packages/security.yaml
    security:
        firewalls:
            api:
                pattern: ^/api
                stateless: true
                bearer_only: true  # Enables the bundle's logic
    
  2. Token Validation The bundle validates JWT tokens against Keycloak’s public key. Extend validation logic by implementing TokenValidatorInterface:

    use Abel\KeycloakBearerOnlyAdapterBundle\Security\TokenValidatorInterface;
    
    class CustomTokenValidator implements TokenValidatorInterface
    {
        public function validate(string $token): bool
        {
            // Custom logic (e.g., check token claims)
            return true;
        }
    }
    

    Register the service in services.yaml:

    services:
        Abel\KeycloakBearerOnlyAdapterBundle\Security\TokenValidator:
            arguments:
                $validator: '@custom_token_validator'
    
  3. Role-Based Access Control (RBAC) Fetch roles from the token and apply Symfony’s voter system:

    #[BearerOnly]
    public function adminEndpoint(): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_ADMIN');
        return new JsonResponse(['message' => 'Admin access granted!']);
    }
    
  4. Integration with API Platform Use the bundle’s BearerOnly attribute alongside ApiPlatform\Metadata\ApiResource:

    #[ApiResource]
    #[BearerOnly]
    class User {}
    

Gotchas and Tips

Pitfalls

  1. Bearer-Only Client Requirement

    • The bundle only works with Keycloak clients configured as "Bearer-Only". Mixing with other access types (e.g., confidential) will fail silently or throw errors.
    • Debug Tip: Check Keycloak’s client settings under Clients > {client_id} > Settings > Access Type.
  2. Token Expiration Handling

    • The bundle does not auto-refresh expired tokens. Implement a custom TokenRefresher or use a library like league/oauth2-client for refresh logic.
    • Tip: Log 401 Unauthorized responses to monitor token expirations.
  3. CORS and CSRF

    • Bearer tokens are stateless but may trigger CORS issues if not configured. Ensure your frontend includes:
      Authorization: Bearer {token}
      
    • Disable CSRF for API routes in security.yaml:
      security:
          firewalls:
              api:
                  csrf_protection: false
      
  4. Environment Variables

    • The bundle expects exact env var names (OAUTH_KEYCLOAK_*). Typos (e.g., KEYCLOAK_ISSUER) will cause silent failures.
    • Tip: Use php artisan config:dump to verify loaded values.

Debugging

  1. Enable Debug Mode Set APP_DEBUG=true in .env to see detailed validation errors (e.g., malformed tokens, issuer mismatches).

  2. Log Token Claims Dump the decoded token for inspection:

    use Firebase\JWT\JWT;
    
    $token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    $decoded = JWT::decode($token, null, ['RS256']);
    var_dump($decoded);
    
  3. Keycloak Public Key Rotation

    • If Keycloak rotates its public key, the bundle may fail to validate tokens. Monitor Keycloak’s /auth/realms/{realm}/protocol/openid-connect/certs endpoint for changes.
    • Tip: Cache the public key in a service to reduce Keycloak API calls:
      use Symfony\Contracts\Cache\CacheInterface;
      
      class KeycloakKeyCache
      {
          public function __construct(private CacheInterface $cache) {}
      
          public function getPublicKey(): string
          {
              return $this->cache->get('keycloak_public_key', function () {
                  return file_get_contents('https://.../certs');
              });
          }
      }
      

Extension Points

  1. Custom Token Extraction Override the default Authorization: Bearer header logic by implementing TokenExtractorInterface:

    use Abel\KeycloakBearerOnlyAdapterBundle\Security\TokenExtractorInterface;
    
    class CustomTokenExtractor implements TokenExtractorInterface
    {
        public function extractToken(): ?string
        {
            return $_GET['token'] ?? null; // Example: Query param fallback
        }
    }
    
  2. Event Listeners Listen to security.interactive_login or kernel.exception to log token validation events:

    services:
        App\EventListener\TokenValidationListener:
            tags:
                - { name: 'kernel.event_listener', event: 'security.interactive_login', method: 'onTokenValidation' }
    
  3. Testing Use Symfony\Panther or Guzzle to simulate authenticated requests in tests:

    use Symfony\Component\Panther\PantherTestCase;
    
    class ApiTest extends PantherTestCase
    {
        public function testSecureEndpoint()
        {
            $client = static::createPantherClient();
            $client->request('GET', '/api/secure', [
                'headers' => ['Authorization' => 'Bearer ' . $this->getValidToken()]
            ]);
            $this->assertResponseIsSuccessful();
        }
    }
    
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.
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
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope