Weave 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

Bbn Security Bundle Laravel Package

bburnichon/bbn-security-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require bburnichon/bbn-security-bundle:@dev
    

    Add to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):

    BBn\SecurityBundle\BBnSecurityBundle::class => ['all' => true],
    
  2. Configure security.yaml Define a firewall with api_key authentication:

    security:
        firewalls:
            api:
                pattern: ^/api/
                stateless: true
                provider: api_user_provider
                api_key:
                    parameter: X-API-KEY  # Defaults to 'apikey' if omitted
    
  3. Create a Custom User Provider Extend UserInterface and implement loadUserByApiKey() (or override loadUserByUsername()):

    use Symfony\Component\Security\Core\User\UserInterface;
    
    class ApiUser implements UserInterface
    {
        public function loadUserByApiKey($apiKey)
        {
            // Fetch user by API key (e.g., from DB)
            return new self($apiKey, '...', ['ROLE_USER']);
        }
    }
    
  4. Register the Provider In security.yaml:

    providers:
        api_user_provider:
            entity: { class: App\Entity\ApiUser, property: apiKey }
    
  5. Test the Flow Send a request with the X-API-KEY header:

    curl -H "X-API-KEY: your_api_key_here" http://your-app/api/endpoint
    

Implementation Patterns

Workflow: API Key Authentication

  1. Request Handling The bundle intercepts requests matching the firewall pattern and extracts the API key from the specified parameter (default: apikey query param or X-API-KEY header). Example request:

    curl -G --data-urlencode "apikey=your_key" http://your-app/api/data
    
  2. User Loading The api_key provider delegates to your custom UserProvider (e.g., loadUserByUsername() or loadUserByApiKey()). Ensure your provider returns a UserInterface object with roles.

  3. Stateless Authentication Since stateless: true is enforced, avoid sessions. Use tokens or JWTs for session-like behavior if needed.

  4. Role-Based Access Attach roles to users in the provider:

    return new ApiUser($apiKey, null, ['ROLE_API', 'ROLE_READ']);
    
  5. Integration with Symfony Components

    • Voters: Use Symfony’s VoterInterface for granular permissions.
    • Encoders: Works with Symfony’s password encoders (though API keys don’t need encoding).
    • Event Listeners: Listen to security.interactive_login or security.authentication_success for post-auth logic.

Common Use Cases

  • Microservices: Secure internal API calls between services.
  • Third-Party APIs: Authenticate external clients via API keys.
  • Legacy Systems: Replace basic auth with API keys for better security.

Gotchas and Tips

Pitfalls

  1. Provider Mismatch

    • The bundle expects the UserProvider to handle API keys as usernames. If loadUserByUsername() fails, authentication silently fails. Test with:
      $provider->loadUserByUsername('invalid_key'); // Should throw UsernameNotFoundException
      
  2. Stateless Constraints

    • No session storage means:
      • Avoid remember_me services.
      • Use stateless: true in all related firewalls.
      • Clear cached data manually if needed.
  3. Parameter Overrides

    • The parameter in api_key config is case-sensitive. Defaults to:
      • Query param: apikey
      • Header: X-API-KEY
    • Override in security.yaml:
      api_key:
          parameter: Authorization  # Custom header
      
  4. API Key Exposure

    • API keys are sent in plaintext. Mitigate risks:
      • Use HTTPS.
      • Rotate keys periodically.
      • Restrict IP ranges in the firewall:
        firewall:
            api:
                pattern: ^/api
                ip: 192.168.1.0/24
        

Debugging Tips

  1. Enable Debugging Add to config/packages/security.yaml:

    security:
        debug: true  # Logs authentication attempts
    
  2. Check User Loading Override loadUserByUsername() temporarily to log API keys:

    public function loadUserByUsername($apiKey)
    {
        error_log("API Key Attempt: $apiKey"); // Debug line
        // ... rest of logic
    }
    
  3. Firewall Matching Ensure the pattern in your firewall matches the request path exactly. Use regex if needed:

    pattern: ^/api/v1/.*
    

Extension Points

  1. Custom Authentication Logic Extend the ApiKeyAuthenticator class (located in BBn\SecurityBundle\Security\Firewall\ApiKeyAuthenticator) to add pre-auth checks:

    public function authenticate(Request $request)
    {
        $apiKey = $request->query->get('apikey');
        if (empty($apiKey) || !$this->isKeyValid($apiKey)) {
            throw new AuthenticationException('Invalid API key');
        }
        return $this->createToken($apiKey);
    }
    
  2. Dynamic Key Validation Integrate with a key validation service (e.g., Redis):

    public function isKeyValid($apiKey)
    {
        return $this->redis->exists("api_key:$apiKey");
    }
    
  3. Rate Limiting Combine with Symfony’s RateLimiter or a middleware to throttle API key usage:

    use Symfony\Component\HttpFoundation\RequestStack;
    
    public function __construct(RequestStack $requestStack, RateLimiterInterface $limiter)
    {
        $this->limiter = $limiter;
        $this->requestStack = $requestStack;
    }
    
    public function authenticate(Request $request)
    {
        $apiKey = $request->headers->get('X-API-KEY');
        if (!$this->limiter->isAllowed($apiKey, 100, 60)) {
            throw new TooManyRequestsException();
        }
        // ... rest of logic
    }
    
  4. Logging Log successful/failed attempts via Symfony’s LoggerInterface:

    use Psr\Log\LoggerInterface;
    
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
    
    public function authenticate(Request $request)
    {
        $apiKey = $request->query->get('apikey');
        $this->logger->info('API Key Auth Attempt', ['key' => $apiKey, 'ip' => $request->getClientIp()]);
        // ... rest of logic
    }
    
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.
frankkessler/guzzle-oauth2-middleware
delatbabel/site-config
delatbabel/applog
eusonlito/laravel-gettext
henzeb/housepedia-intelligence-laravel
aliqsm/laravel-authkit
itsjustvita/laravel-wp-migrator
uslanozan/laravel-cache-contract
adilazhari/laravel-trace
ulsyairil/ai-local-guardrails
dzwallet/laravel-sdk
glc36/maxloop-kernel
ikhbarnuridev/filament-mega-menu
mydnic/volet-chatbot
enadstack/laravel-localization
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder