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

Keycloack Token Bundle Laravel Package

amiltone/keycloack-token-bundle

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require amiltone/keycloack-token-bundle

Ensure your project meets the requirements: PHP 7.2.5+, Symfony 4.4/5.0/6.0.

  1. Keycloak Configuration:

    • Configure your Keycloak realm and client in the Keycloak admin console.
    • Note the realm, client ID, and public key (or use a JWKS endpoint).
  2. First Use Case:

    • Annotation Route: Add the @UserVerification annotation to a controller method to enforce token validation.
      use App\Annotation\UserVerification;
      
      class HomeController extends AbstractController {
          /**
           * @UserVerification
           */
          public function index(Request $request): Response {
              $user = $request->attributes->get('user'); // Validated user data
              return new Response('Authenticated');
          }
      }
      
    • YAML Route: Enable validation via route defaults:
      index:
          path: /home
          controller: App\Controller\HomeController::index
          defaults: { userVerification: true }
      
  3. Testing:

    • Send a request with a valid Keycloak token in the Authorization header:
      Authorization: Bearer <valid_keycloak_token>
      
    • The bundle will automatically validate the token and inject the decoded user data into the request attributes.

Implementation Patterns

Common Workflows

1. Token Validation in Controllers

  • Annotation-Based: Use @UserVerification on methods to enforce validation. The bundle injects the decoded user data into $request->attributes under the key user.
    public function profile(Request $request): Response {
        $user = $request->attributes->get('user');
        return $this->json($user['preferred_username']);
    }
    
  • YAML-Based: Define userVerification: true in route defaults for bulk validation.

2. Custom Claims Extraction

Extract specific claims from the token (e.g., roles, groups):

public function adminDashboard(Request $request): Response {
    $user = $request->attributes->get('user');
    $roles = $user['realm_access']['roles'] ?? [];
    if (!in_array('admin', $roles)) {
        throw $this->createAccessDeniedException();
    }
    return new Response('Welcome, Admin');
}

3. Integration with Symfony Security

Combine with Symfony’s security component for role-based access:

# config/packages/security.yaml
access_control:
    - { path: ^/admin, roles: ROLE_ADMIN }

Validate the token first, then use Symfony’s security system for granular permissions.

4. Token Refresh Logic

Handle short-lived tokens by implementing a refresh endpoint:

public function refreshToken(Request $request): Response {
    $refreshToken = $request->request->get('refresh_token');
    // Use Keycloak's token endpoint to refresh
    $client = new \GuzzleHttp\Client();
    $response = $client->post('https://your-keycloak/auth/realms/your-realm/protocol/openid-connect/token', [
        'form_params' => [
            'client_id' => 'your-client',
            'client_secret' => 'your-secret',
            'grant_type' => 'refresh_token',
            'refresh_token' => $refreshToken,
        ],
    ]);
    return new JsonResponse(json_decode($response->getBody(), true));
}

5. Event Listeners for Token Parsing

Listen to the kernel.request event to parse tokens globally (e.g., for logging or middleware):

// src/EventListener/TokenListener.php
namespace App\EventListener;

use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class TokenListener {
    public function onKernelRequest(RequestEvent $event) {
        $request = $event->getRequest();
        if ($request->headers->has('Authorization')) {
            $token = $this->parseToken($request->headers->get('Authorization'));
            $request->attributes->set('user', $token);
        }
    }

    private function parseToken(string $header): array {
        // Custom parsing logic or reuse bundle logic
    }
}

Register the listener in services.yaml:

services:
    App\EventListener\TokenListener:
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Integration Tips

1. Keycloak Configuration

  • Ensure your Keycloak client is configured with:
    • Access Token Lifespan: Adjust based on your security needs (default: 5 minutes).
    • Standard Flow Enabled: For password/implicit grants.
    • Valid Redirect URIs: If using OAuth flows.
  • For production, use JWKS (JSON Web Key Set) for public key validation instead of hardcoding keys.

2. Environment-Specific Config

Use Symfony’s parameter bag to manage Keycloak settings across environments:

# config/packages/keycloak_token.yaml
keycloak_token:
    realm: '%env(KEYCLOAK_REALM)%'
    client_id: '%env(KEYCLOAK_CLIENT_ID)%'
    jwks_uri: '%env(KEYCLOAK_JWKS_URI)%'

Define these in .env:

KEYCLOAK_REALM=your-realm
KEYCLOAK_CLIENT_ID=your-client
KEYCLOAK_JWKS_URI=https://your-keycloak/auth/realms/your-realm/protocol/openid-connect/certs

3. Testing Tokens

  • Generate test tokens using Keycloak’s Test Console or tools like jwt.io.
  • Mock the Authorization header in PHPUnit:
    $client->request('GET', '/home', [], [], [
        'HTTP_Authorization' => 'Bearer ' . $validToken,
    ]);
    

4. Performance Considerations

  • Cache the JWKS public keys to avoid repeated network calls:
    $cache = new \Symfony\Component\Cache\Simple\FilesystemCache();
    $jwks = $cache->get('keycloak_jwks', function() {
        return file_get_contents($this->jwksUri);
    });
    

Gotchas and Tips

Pitfalls

1. Token Expiration Handling

  • The bundle does not automatically refresh expired tokens. Implement a refresh endpoint or use Symfony’s AuthenticationUtils to handle 401 responses.
  • Symptom: 401 Unauthorized when tokens expire. Fix: Return a 401 with a WWW-Authenticate header pointing to the refresh endpoint.

2. Annotation vs. YAML Conflicts

  • If both @UserVerification and defaults: { userVerification: true } are set, the annotation takes precedence. This can lead to unexpected behavior if misconfigured.
  • Fix: Use one method consistently or document the precedence in your team’s style guide.

3. Public Key Validation

  • Hardcoding public keys in the bundle’s config is not recommended for production. Always use JWKS for dynamic key rotation.
  • Symptom: Token validation fails silently if the key is outdated. Fix: Log errors and monitor JWKS updates.

4. Symfony Version Compatibility

  • The bundle supports Symfony 4.4, 5.0, and 6.0, but some features (e.g., annotations) may behave differently across versions.
  • Symptom: Annotations ignored in Symfony 6. Fix: Ensure doctrine/annotations is installed and configured in config/packages/doctrine.yaml.

5. Token Parsing Errors

  • Malformed tokens (e.g., missing claims) may cause silent failures. Enable debug mode to catch parsing errors:
    // config/packages/dev/keycloak_token.yaml
    keycloak_token:
          debug: true
    
  • Symptom: $request->attributes->get('user') returns null. Fix: Check logs for KeycloakTokenException.

Debugging Tips

1. Enable Verbose Logging

Add this to config/packages/monolog.yaml:

handlers:
    keycloak:
        type: stream
        path: "%kernel.logs_dir%/keycloak.log"
        level: debug
        channels: ["keycloak_token"]

Then configure the bundle to use this channel:

keycloak_token:
    logging_channel: keycloak

2. Validate Tokens Manually

Use the bundle’s KeycloakTokenParser service directly to debug:

$parser = $container
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.
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
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle