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

Csrf Bundle Laravel Package

depthbomb/csrf-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require depthbomb/csrf-bundle
    

    Ensure your project meets the requirements (PHP >= 8.1, Symfony 6.3.x).

  2. Enable the Bundle: Add Depthbomb\CsrfBundle\DepthbombCsrfBundle::class to the config/bundles.php array.

  3. First Use Case: Protect a controller or method with the #[CsrfProtected] attribute:

    use Depthbomb\CsrfBundle\Attribute\CsrfProtected;
    
    #[CsrfProtected('edit_user')]
    public function edit(User $user): Response
    {
        // Your logic here
    }
    
  4. Generate a Token: In a controller or service:

    $token = $this->tokenManager->getToken('edit_user');
    

    Or in Twig:

    <input type="hidden" name="csrf_token" value="{{ csrf_token('edit_user') }}">
    
  5. Validate the Token: The bundle automatically validates the token when the protected route is accessed. If invalid, a 428 Precondition Required error is thrown.


Implementation Patterns

Workflows

  1. Token Generation:

    • Controllers/Services: Use $this->tokenManager->getToken('token_id') to generate tokens dynamically.
    • Twig Templates: Use the csrf_token() function for static forms.
    • APIs: Generate tokens in JSON responses for AJAX calls:
      return $this->json(['csrf_token' => $this->tokenManager->getToken('api_action')]);
      
  2. Token Validation:

    • The bundle validates tokens before the controller action is executed. No manual validation is needed.
    • Customize the validation behavior by extending the CsrfTokenManager or overriding the CsrfListener.
  3. Integration with Forms:

    • Embed tokens in forms:
      <form method="POST">
          {{ csrf_token('form_action') }}
          <!-- form fields -->
      </form>
      
    • For dynamic forms (e.g., AJAX), generate tokens client-side via API calls.
  4. Route-Specific Protection:

    • Protect individual actions with #[CsrfProtected('token_id')].
    • Protect entire controllers by applying the attribute at the class level.
    • Exclude specific actions by not applying the attribute.
  5. Token Storage:

    • Tokens are stored in the session by default. Customize storage by configuring the CsrfTokenStorage service.

Integration Tips

  • Symfony UX Turbo/Stimulus: Use tokens in Turbo/Stimulus-driven forms to prevent CSRF on partial updates. Example:

    {{ csrf_token('turbo_action')|raw }}  {# Hidden in Turbo stream #}
    
  • APIs with JWT: If using JWT, generate tokens server-side and include them in the Authorization header:

    $token = $this->tokenManager->getToken('jwt_action');
    return $this->json(['token' => $token]);
    

    Client-side, send the token as:

    Authorization: Bearer <csrf_token>
    
  • Custom Token Namespaces: Use namespaced token IDs (e.g., user.123.edit) to avoid collisions in multi-tenant apps.


Gotchas and Tips

Pitfalls

  1. Token Mismatch:

    • If the token ID in the attribute (#[CsrfProtected('token_id')]) does not match the token generated in the template/API (csrf_token('token_id')), validation fails with a 428 error.
    • Fix: Ensure consistency between token IDs in attributes and generation calls.
  2. Session Dependence:

    • Tokens rely on the session. If the session is disabled or expired, tokens will fail validation.
    • Fix: Regenerate tokens after session expiration or use alternative storage (e.g., database).
  3. Double Submissions:

    • Resubmitting a form with the same token may cause validation to fail if the token is single-use (default behavior).
    • Fix: Configure the CsrfTokenManager to allow multiple uses:
      # config/packages/depthbomb_csrf.yaml
      depthbomb_csrf:
          token_manager:
              allow_multiple_uses: true
      
  4. Caching Issues:

    • Tokens are not cached by default. In high-traffic apps, token generation may become a bottleneck.
    • Fix: Implement a cache layer for token storage (extend CsrfTokenStorage).
  5. Attribute Override:

    • Applying #[CsrfProtected] at both the class and method level will use the method-level token ID.
    • Tip: Use class-level for broad protection and method-level for overrides.

Debugging

  1. Token Not Found:

    • Check if the token ID matches exactly (case-sensitive).
    • Verify the token was generated before the request (e.g., in Twig or a controller before the protected action).
  2. 428 Errors:

    • Ensure the token is included in the request (e.g., as a hidden form field or header).
    • Check for typos in the token ID or missing CSRF middleware (though the bundle adds its own listener).
  3. Token Expiration:

    • Tokens expire after one use by default. If you need longer validity, adjust the lifetime in config:
      depthbomb_csrf:
          token_manager:
              lifetime: 3600  {# 1 hour #}
      

Extension Points

  1. Custom Token Storage: Extend Depthbomb\CsrfBundle\Security\CsrfTokenStorageInterface to store tokens in a database or cache:

    class DatabaseCsrfTokenStorage implements CsrfTokenStorageInterface
    {
        public function generateToken(string $id): string { /* ... */ }
        public function isTokenValid(string $id, string $token): bool { /* ... */ }
    }
    

    Register it as a service:

    services:
        Depthbomb\CsrfBundle\Security\CsrfTokenStorageInterface: '@database_csrf_token_storage'
    
  2. Override Validation Logic: Extend the CsrfListener to customize validation (e.g., allow empty tokens for specific routes):

    class CustomCsrfListener extends CsrfListener
    {
        protected function isCsrfTokenValid(Request $request, string $tokenId): bool
        {
            if ($request->getPathInfo() === '/public-route') {
                return true;
            }
            return parent::isCsrfTokenValid($request, $tokenId);
        }
    }
    

    Override the service in config/services.yaml:

    Depthbomb\CsrfBundle\EventListener\CsrfListener: '@custom_csrf_listener'
    
  3. Token Generation Hooks: Subscribe to the csrf.token.generate event to modify tokens before generation:

    use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
    
    #[AsEventListener(event: 'csrf.token.generate')]
    public function onCsrfTokenGenerate(CsrfTokenEvent $event): void
    {
        if ($event->getTokenId() === 'admin_action') {
            $event->setToken('admin_' . bin2hex(random_bytes(4)));
        }
    }
    
  4. API Headers: Modify the bundle to accept tokens in headers (e.g., X-CSRF-Token) instead of form fields:

    // Extend CsrfListener and override getCsrfToken()
    protected function getCsrfToken(Request $request): ?string
    {
        return $request->headers->get('X-CSRF-Token') ?? parent::getCsrfToken($request);
    }
    

Config Quirks

  • Default Configuration: The bundle uses sensible defaults (e.g., 428 status code, session storage). Override in config/packages/depthbomb_csrf.yaml:

    depthbomb_csrf:
        http_status_code: 403  {# Change error code #}
        token_manager:
            storage: '@custom_token_storage'
    
  • Environment-Specific Tokens: Use parameter bags to define tokens per environment:

    # config/packages/dev/depthbomb_csrf.yaml
    depthbomb_csrf:
        token_manager:
            dev_tokens:
                - 'dev_only_action'
    

    Then check in your listener:

    if (in_array($tokenId, $this->container->getParameter('dev_tokens'))) {
        // Allow in dev
    }
    
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