Symfony Security

authentication-librarySymfonyPHPfirewallauthenticationauthorizationsecuritymiddlewareCSRF

Authentication Library

Symfony Security

Overview

Symfony Security is the core security component of the Symfony framework. It provides a comprehensive system for authentication and authorization, integrating features like firewalls, authentication providers, and access control to enable secure web application development.

Details

Symfony Security offers flexible and extensible security features through modular design. Centered around the firewall system, it provides authentication mechanisms, role-based access control (RBAC), CSRF protection, two-factor authentication support, and other enterprise-level security requirements.

Key Components

  • Firewall: Request filtering and authentication functionality
  • Authenticator: Middleware that defines authentication methods
  • User Provider: User information retrieval and loading
  • Access Control: URL-based authorization rules
  • Guard Middleware: Enforced authentication checks

Authentication Methods

  • Form Login: Form-based authentication
  • HTTP Basic: Basic authentication
  • Bearer Token: JWT, API token authentication
  • JSON Login: JSON format login
  • X.509: Client certificate authentication
  • Remote User: External authentication system integration

Advanced Security Features

  • 2FA (Two-Factor Authentication): TOTP, SMS authentication
  • Remember Me: Persistent login functionality
  • CSRF Protection: Cross-site request forgery prevention
  • Security Voters: Custom authorization logic

Pros and Cons

Pros

  • Comprehensive Security: Complete coverage from authentication to authorization
  • Flexible Configuration: Detailed configuration via YAML, XML, PHP
  • Multiple Authentication Methods: Rich authentication options
  • Enterprise Ready: Applicable to large-scale systems
  • Community Support: Rich documentation and support
  • Modular Design: Use only required features

Cons

  • Learning Curve: Complex configuration requiring time to master
  • Performance: Processing overhead due to multiple features
  • Symfony Dependency: Exclusive to Symfony framework
  • Configuration Complexity: Detailed settings required, difficult initial setup

Reference Links

Code Examples

Basic Configuration (security.yaml)

# config/packages/security.yaml
security:
    enable_authenticator_manager: true
    
    password_hashers:
        App\Entity\User:
            algorithm: auto
    
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        
        main:
            lazy: true
            provider: app_user_provider
            form_login:
                login_path: app_login
                check_path: app_login
            logout:
                path: app_logout
    
    access_control:
        - { path: ^/admin, roles: ROLE_ADMIN }
        - { path: ^/profile, roles: ROLE_USER }

Custom Authentication Provider

<?php
// src/Security/ApiKeyAuthenticator.php
namespace App\Security;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

class ApiKeyAuthenticator extends AbstractAuthenticator
{
    public function supports(Request $request): ?bool
    {
        return $request->headers->has('X-API-TOKEN');
    }

    public function authenticate(Request $request): Passport
    {
        $apiToken = $request->headers->get('X-API-TOKEN');
        
        if (null === $apiToken) {
            throw new CustomUserMessageAuthenticationException('No API token provided');
        }

        return new SelfValidatingPassport(new UserBadge($apiToken));
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        $data = [
            'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
        ];

        return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
    }
}

Bearer Token Authentication Configuration

# config/packages/security.yaml
security:
    firewalls:
        api:
            pattern: ^/api/
            stateless: true
            bearer_token:
                token_handler: App\Security\AccessTokenHandler
        
        main:
            lazy: true
            form_login:
                login_path: app_login
                check_path: app_login

Access Token Handler

<?php
// src/Security/AccessTokenHandler.php
namespace App\Security;

use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;

class AccessTokenHandler implements AccessTokenHandlerInterface
{
    public function getUserBadgeFrom(string $accessToken): UserBadge
    {
        // Token validation and user identification
        if (!$this->isValidToken($accessToken)) {
            throw new BadCredentialsException('Invalid credentials.');
        }

        $userIdentifier = $this->getUserIdentifierFromToken($accessToken);
        
        return new UserBadge($userIdentifier);
    }

    private function isValidToken(string $token): bool
    {
        // Token validation logic
        return hash('sha256', 'your-secret') === $token;
    }

    private function getUserIdentifierFromToken(string $token): string
    {
        // Get user identifier from token
        return '[email protected]';
    }
}

Multiple Authentication Methods Combination

# config/packages/security.yaml
security:
    firewalls:
        main:
            lazy: true
            provider: app_user_provider
            # Enable multiple authentication methods simultaneously
            form_login:
                login_path: app_login
                check_path: app_login
            custom_authenticators:
                - App\Security\SocialConnectAuthenticator
                - App\Security\ApiKeyAuthenticator
            
            # Explicitly specify entry point
            entry_point: form_login

Guard Middleware Usage Example

<?php
// src/Controller/AdminController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

class AdminController extends AbstractController
{
    #[Route('/admin', name: 'admin_dashboard')]
    #[IsGranted('ROLE_ADMIN')]
    public function dashboard(): Response
    {
        // Only accessible to administrators
        return $this->render('admin/dashboard.html.twig');
    }
    
    #[Route('/admin/users', name: 'admin_users')]
    public function users(): Response
    {
        // Programmatic role verification
        $this->denyAccessUnlessGranted('ROLE_ADMIN');
        
        return $this->render('admin/users.html.twig');
    }
}

CSRF Protection Implementation

<?php
// src/Form/LoginType.php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class LoginType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email', TextType::class)
            ->add('password', PasswordType::class)
            ->add('login', SubmitType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'csrf_protection' => true,
            'csrf_field_name' => '_token',
            'csrf_token_id'   => 'authenticate',
        ]);
    }
}

User Entity Example

<?php
// src/Entity/User.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 180, unique: true)]
    private string $email;

    #[ORM\Column(type: 'json')]
    private array $roles = [];

    #[ORM\Column(type: 'string')]
    private string $password;

    public function getUserIdentifier(): string
    {
        return $this->email;
    }

    public function getRoles(): array
    {
        $roles = $this->roles;
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function getPassword(): string
    {
        return $this->password;
    }

    public function eraseCredentials(): void
    {
        // Clear temporary sensitive data
    }
}