Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Symfony Security
https://github.com/symfony/security
Admin
Symfony Security is a complete security system for web applications providing authentication,
...
Tokens:
6,685
Snippets:
46
Trust Score:
-
Update:
1 month ago
Context
Skills
Chat
Benchmark
51.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Symfony Security Symfony Security is a comprehensive security system for PHP web applications that provides complete authentication, authorization, and protection mechanisms. The component enables developers to implement secure user authentication through multiple methods (HTTP basic, form login, X.509 certificates), authorize users based on roles and custom voters, encode passwords using modern algorithms, and protect against CSRF attacks. The security system is highly extensible and integrates seamlessly with the Symfony framework. The component is organized into four main subcomponents: Core (authentication, authorization, user management, password encoding), Guard (simplified custom authenticator system), Csrf (CSRF token protection), and Http (firewall and access control). These modules work together to provide a layered security architecture that handles everything from user credential validation to fine-grained access decisions on protected resources. ## Security Helper Class The `Security` class provides convenient helper methods for common security tasks including retrieving the current authenticated user and checking access permissions. It serves as the main entry point for security operations in your application. ```php <?php use Symfony\Component\Security\Core\Security; class DashboardController { private Security $security; public function __construct(Security $security) { $this->security = $security; } public function index() { // Get the currently authenticated user $user = $this->security->getUser(); if ($user === null) { throw new \Exception('User not authenticated'); } // Check if user has specific role/permission if ($this->security->isGranted('ROLE_ADMIN')) { // Admin-only functionality return $this->renderAdminDashboard(); } // Check permission on specific subject $post = $this->getPost(123); if ($this->security->isGranted('edit', $post)) { // User can edit this post } // Get the authentication token directly $token = $this->security->getToken(); $username = $token->getUsername(); $roles = $token->getRoleNames(); } } ``` ## UserInterface Implementation The `UserInterface` defines the contract for user objects in the security system. Implement this interface to create custom user classes that integrate with Symfony's authentication layer. ```php <?php use Symfony\Component\Security\Core\User\UserInterface; class AppUser implements UserInterface { private string $username; private string $password; private array $roles; public function __construct(string $username, string $password, array $roles = ['ROLE_USER']) { $this->username = $username; $this->password = $password; $this->roles = $roles; } public function getRoles(): array { // Guarantee every user has at least ROLE_USER $roles = $this->roles; $roles[] = 'ROLE_USER'; return array_unique($roles); } public function getPassword(): ?string { return $this->password; } public function getSalt(): ?string { // Modern encoders don't need a salt return null; } public function getUsername(): string { return $this->username; } public function eraseCredentials(): void { // Clear any temporary sensitive data // $this->plainPassword = null; } } ``` ## UserProviderInterface Implementation The `UserProviderInterface` defines how users are loaded from storage. Implement this to create custom user providers that fetch users from databases, APIs, or other sources. ```php <?php use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; class DatabaseUserProvider implements UserProviderInterface { private \PDO $connection; public function __construct(\PDO $connection) { $this->connection = $connection; } public function loadUserByUsername($username): UserInterface { $stmt = $this->connection->prepare( 'SELECT id, username, password, roles FROM users WHERE username = :username' ); $stmt->execute(['username' => $username]); $userData = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$userData) { $exception = new UsernameNotFoundException( sprintf('Username "%s" does not exist.', $username) ); $exception->setUsername($username); throw $exception; } return new AppUser( $userData['username'], $userData['password'], json_decode($userData['roles'], true) ?? ['ROLE_USER'] ); } public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof AppUser) { throw new UnsupportedUserException( sprintf('Instances of "%s" are not supported.', get_class($user)) ); } return $this->loadUserByUsername($user->getUsername()); } public function supportsClass($class): bool { return AppUser::class === $class || is_subclass_of($class, AppUser::class); } } ``` ## InMemoryUserProvider The `InMemoryUserProvider` provides a simple way to configure users directly in code, useful for testing, prototyping, or simple applications with fixed user sets. ```php <?php use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; // Create provider with users defined as array $userProvider = new InMemoryUserProvider([ 'admin' => [ 'password' => '$2y$13$encodedPasswordHash', 'roles' => ['ROLE_ADMIN', 'ROLE_USER'], 'enabled' => true, ], 'user' => [ 'password' => '$2y$13$anotherEncodedHash', 'roles' => ['ROLE_USER'], ], ]); // Or create users programmatically $newUser = new User( 'john_doe', // username '$2y$13$encodedHash', // encoded password ['ROLE_USER', 'ROLE_EDITOR'], // roles true, // enabled true, // accountNonExpired true, // credentialsNonExpired true // accountNonLocked ); $userProvider->createUser($newUser); // Load a user $user = $userProvider->loadUserByUsername('admin'); echo $user->getUsername(); // admin print_r($user->getRoles()); // ['ROLE_ADMIN', 'ROLE_USER'] ``` ## Password Encoding with NativePasswordEncoder The `NativePasswordEncoder` uses PHP's `password_hash()` function to securely encode passwords with modern algorithms like bcrypt and Argon2. ```php <?php use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; // Create encoder with default settings (auto-selects best algorithm) $encoder = new NativePasswordEncoder(); // Or configure specific options $encoder = new NativePasswordEncoder( null, // opsLimit for Argon2 (null = default) null, // memLimit for Argon2 (null = default) 13, // cost for bcrypt (4-31) 'argon2i' // algorithm: 'argon2i', 'argon2id', or '2y' for bcrypt ); // Encode a password $plainPassword = 'my_secure_password'; $encodedPassword = $encoder->encodePassword($plainPassword, null); // Output: $argon2id$v=19$m=65536,t=4,p=1$... // Verify a password $isValid = $encoder->isPasswordValid($encodedPassword, $plainPassword, null); // Returns: true $isValid = $encoder->isPasswordValid($encodedPassword, 'wrong_password', null); // Returns: false // Check if password needs rehashing (e.g., algorithm changed) if ($encoder->needsRehash($encodedPassword)) { $newEncodedPassword = $encoder->encodePassword($plainPassword, null); // Save $newEncodedPassword to database } ``` ## UserPasswordEncoder The `UserPasswordEncoder` provides a higher-level API for encoding passwords tied to specific user classes, automatically selecting the appropriate encoder based on the user type. ```php <?php use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; // Configure encoder factory with encoders per user class $encoderFactory = new EncoderFactory([ AppUser::class => new NativePasswordEncoder(null, null, 13), AdminUser::class => new NativePasswordEncoder(null, null, 15), // Higher cost for admins ]); $userPasswordEncoder = new UserPasswordEncoder($encoderFactory); // Encode password for a user $user = new AppUser('john', '', ['ROLE_USER']); $encodedPassword = $userPasswordEncoder->encodePassword($user, 'plain_password'); // Verify password $isValid = $userPasswordEncoder->isPasswordValid($user, 'plain_password'); // Returns: true // Check if rehashing is needed if ($userPasswordEncoder->needsRehash($user)) { $newHash = $userPasswordEncoder->encodePassword($user, $currentPlainPassword); $user->setPassword($newHash); // Persist user } ``` ## UsernamePasswordToken The `UsernamePasswordToken` represents an authentication token containing username and password credentials. It's used throughout the authentication process to carry user information. ```php <?php use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; // Create unauthenticated token (for authentication attempt) $token = new UsernamePasswordToken( 'john_doe', // username or UserInterface 'plain_password', // credentials 'main' // firewall/provider key ); echo $token->getUsername(); // john_doe echo $token->getCredentials(); // plain_password echo $token->getProviderKey(); // main echo $token->isAuthenticated(); // false // Create authenticated token (after successful authentication) $authenticatedToken = new UsernamePasswordToken( $user, // UserInterface instance null, // credentials (null after auth) 'main', // provider key ['ROLE_USER', 'ROLE_ADMIN'] // roles ); echo $authenticatedToken->isAuthenticated(); // true print_r($authenticatedToken->getRoleNames()); // ['ROLE_USER', 'ROLE_ADMIN'] // Erase sensitive data after authentication $authenticatedToken->eraseCredentials(); echo $authenticatedToken->getCredentials(); // null ``` ## TokenStorage The `TokenStorage` holds the current user's authentication token and provides access to it throughout the request lifecycle. ```php <?php use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; $tokenStorage = new TokenStorage(); // Store authenticated token $token = new UsernamePasswordToken($user, null, 'main', ['ROLE_USER']); $tokenStorage->setToken($token); // Retrieve current token $currentToken = $tokenStorage->getToken(); if ($currentToken !== null) { $user = $currentToken->getUser(); $username = $currentToken->getUsername(); $isAuthenticated = $currentToken->isAuthenticated(); } // Clear authentication (logout) $tokenStorage->setToken(null); // Reset storage (useful in long-running processes) $tokenStorage->reset(); // Set lazy initializer for deferred token loading $tokenStorage->setInitializer(function () use ($tokenStorage, $sessionHandler) { // Load token from session only when needed $token = $sessionHandler->loadToken(); $tokenStorage->setToken($token); }); ``` ## AuthorizationChecker The `AuthorizationChecker` is the main entry point for authorization decisions, checking if the current user has the required permissions. ```php <?php use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; // Setup authorization checker $tokenStorage = new TokenStorage(); $accessDecisionManager = new AccessDecisionManager([new RoleVoter()]); $authenticationManager = /* ... */; $authorizationChecker = new AuthorizationChecker( $tokenStorage, $authenticationManager, $accessDecisionManager, false // alwaysAuthenticate ); // Check role-based access if ($authorizationChecker->isGranted('ROLE_ADMIN')) { // User has admin role } // Check access on a specific subject $post = getPost(123); if ($authorizationChecker->isGranted('edit', $post)) { // User can edit this post } // Check multiple attributes (deprecated, use expression instead) if ($authorizationChecker->isGranted('ROLE_USER')) { if ($authorizationChecker->isGranted('ROLE_MODERATOR')) { // User has both roles } } ``` ## AccessDecisionManager The `AccessDecisionManager` coordinates voter decisions using configurable strategies (affirmative, consensus, unanimous) to determine access control outcomes. ```php <?php use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; // Create with affirmative strategy (default): grants if ANY voter grants $manager = new AccessDecisionManager( [new RoleVoter(), new AuthenticatedVoter()], AccessDecisionManager::STRATEGY_AFFIRMATIVE, false, // allowIfAllAbstainDecisions true // allowIfEqualGrantedDeniedDecisions ); // Consensus strategy: grants if majority grants $consensusManager = new AccessDecisionManager( $voters, AccessDecisionManager::STRATEGY_CONSENSUS ); // Unanimous strategy: grants only if ALL voters grant (or abstain) $unanimousManager = new AccessDecisionManager( $voters, AccessDecisionManager::STRATEGY_UNANIMOUS ); // Make access decision $token = $tokenStorage->getToken(); $granted = $manager->decide($token, ['ROLE_ADMIN'], null); // Decision with subject $granted = $manager->decide($token, ['edit'], $post); ``` ## Custom Voter Implementation The `Voter` abstract class simplifies creating custom voters for fine-grained authorization logic on specific subjects. ```php <?php use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class PostVoter extends Voter { public const VIEW = 'view'; public const EDIT = 'edit'; public const DELETE = 'delete'; protected function supports($attribute, $subject): bool { // Only vote on Post objects with supported attributes if (!in_array($attribute, [self::VIEW, self::EDIT, self::DELETE])) { return false; } if (!$subject instanceof Post) { return false; } return true; } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool { $user = $token->getUser(); // Anonymous users can only view published posts if (!$user instanceof UserInterface) { return $attribute === self::VIEW && $subject->isPublished(); } /** @var Post $post */ $post = $subject; switch ($attribute) { case self::VIEW: return $this->canView($post, $user); case self::EDIT: return $this->canEdit($post, $user); case self::DELETE: return $this->canDelete($post, $user); } return false; } private function canView(Post $post, UserInterface $user): bool { // Published posts visible to all authenticated users if ($post->isPublished()) { return true; } // Drafts only visible to owner return $user === $post->getOwner(); } private function canEdit(Post $post, UserInterface $user): bool { // Only owner can edit return $user === $post->getOwner(); } private function canDelete(Post $post, UserInterface $user): bool { // Admins and owners can delete return in_array('ROLE_ADMIN', $user->getRoles()) || $user === $post->getOwner(); } } // Register voter in AccessDecisionManager $manager = new AccessDecisionManager([ new RoleVoter(), new PostVoter(), ]); // Usage $granted = $authChecker->isGranted('edit', $post); ``` ## RoleHierarchy The `RoleHierarchy` allows defining role inheritance, where higher roles automatically include permissions of lower roles. ```php <?php use Symfony\Component\Security\Core\Role\RoleHierarchy; // Define role hierarchy $hierarchy = new RoleHierarchy([ 'ROLE_ADMIN' => ['ROLE_MODERATOR', 'ROLE_ALLOWED_TO_SWITCH'], 'ROLE_MODERATOR' => ['ROLE_USER'], 'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN', 'ROLE_DEVELOPER'], ]); // Get all reachable roles for a given role $roles = $hierarchy->getReachableRoleNames(['ROLE_ADMIN']); // Returns: ['ROLE_ADMIN', 'ROLE_MODERATOR', 'ROLE_ALLOWED_TO_SWITCH', 'ROLE_USER'] $roles = $hierarchy->getReachableRoleNames(['ROLE_SUPER_ADMIN']); // Returns: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_DEVELOPER', 'ROLE_MODERATOR', // 'ROLE_ALLOWED_TO_SWITCH', 'ROLE_USER'] // Use with RoleHierarchyVoter for authorization use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; $voter = new RoleHierarchyVoter($hierarchy); $manager = new AccessDecisionManager([$voter]); // User with ROLE_ADMIN will be granted ROLE_USER access $token = new UsernamePasswordToken($user, null, 'main', ['ROLE_ADMIN']); $granted = $manager->decide($token, ['ROLE_USER'], null); // true ``` ## CsrfTokenManager The `CsrfTokenManager` generates and validates CSRF tokens to protect forms against cross-site request forgery attacks. ```php <?php use Symfony\Component\Security\Csrf\CsrfTokenManager; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator; use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; use Symfony\Component\HttpFoundation\Session\Session; // Create CSRF token manager $session = new Session(); $tokenStorage = new SessionTokenStorage($session); $tokenGenerator = new UriSafeTokenGenerator(); $csrfManager = new CsrfTokenManager($tokenGenerator, $tokenStorage); // Generate token for a form $token = $csrfManager->getToken('authenticate'); $tokenValue = $token->getValue(); // In your form template: // <input type="hidden" name="_csrf_token" value="<?= $tokenValue ?>"> // Validate submitted token $submittedToken = $_POST['_csrf_token'] ?? ''; $csrfToken = new CsrfToken('authenticate', $submittedToken); if (!$csrfManager->isTokenValid($csrfToken)) { throw new \RuntimeException('Invalid CSRF token'); } // Refresh token (generates new value) $newToken = $csrfManager->refreshToken('authenticate'); // Remove token (e.g., after successful form submission) $csrfManager->removeToken('authenticate'); // Different token IDs for different forms $loginToken = $csrfManager->getToken('login_form'); $deleteToken = $csrfManager->getToken('delete_item'); $settingsToken = $csrfManager->getToken('user_settings'); ``` ## Guard Authenticator Interface The Guard component provides a simplified way to create custom authentication systems. Implement `AuthenticatorInterface` to control the entire authentication process. ```php <?php use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; class ApiTokenAuthenticator extends AbstractGuardAuthenticator { public function supports(Request $request): bool { return $request->headers->has('X-API-TOKEN'); } public function getCredentials(Request $request) { return [ 'api_key' => $request->headers->get('X-API-TOKEN'), ]; } public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface { $apiKey = $credentials['api_key']; if (null === $apiKey) { return null; } // Load user by API key from your database return $this->userRepository->findByApiKey($apiKey); } public function checkCredentials($credentials, UserInterface $user): bool { // API key authentication doesn't need additional credential check // The key itself is the credential, validated in getUser() return true; } public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response { // Return null to continue the request return null; } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response { return new JsonResponse([ 'error' => 'Authentication failed', 'message' => $exception->getMessage(), ], Response::HTTP_UNAUTHORIZED); } public function start(Request $request, AuthenticationException $authException = null): Response { return new JsonResponse([ 'error' => 'Authentication required', 'message' => 'Please provide a valid API token via X-API-TOKEN header', ], Response::HTTP_UNAUTHORIZED); } public function supportsRememberMe(): bool { return false; } } ``` ## AbstractFormLoginAuthenticator The `AbstractFormLoginAuthenticator` provides a base class for creating form-based login authenticators with common functionality pre-implemented. ```php <?php use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; class LoginFormAuthenticator extends AbstractFormLoginAuthenticator { private UserPasswordEncoderInterface $passwordEncoder; private CsrfTokenManagerInterface $csrfTokenManager; public function __construct( UserPasswordEncoderInterface $passwordEncoder, CsrfTokenManagerInterface $csrfTokenManager ) { $this->passwordEncoder = $passwordEncoder; $this->csrfTokenManager = $csrfTokenManager; } public function supports(Request $request): bool { return $request->getPathInfo() === '/login' && $request->isMethod('POST'); } public function getCredentials(Request $request) { return [ 'username' => $request->request->get('_username'), 'password' => $request->request->get('_password'), 'csrf_token' => $request->request->get('_csrf_token'), ]; } public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface { // Validate CSRF token $token = new CsrfToken('authenticate', $credentials['csrf_token']); if (!$this->csrfTokenManager->isTokenValid($token)) { throw new InvalidCsrfTokenException(); } return $userProvider->loadUserByUsername($credentials['username']); } public function checkCredentials($credentials, UserInterface $user): bool { return $this->passwordEncoder->isPasswordValid($user, $credentials['password']); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse { // Redirect to intended page or default $targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path'); return new RedirectResponse($targetPath ?? '/dashboard'); } protected function getLoginUrl(): string { return '/login'; } } ``` ## GuardAuthenticatorHandler The `GuardAuthenticatorHandler` manages the authentication flow, handling token storage and dispatching authentication events. ```php <?php use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; $tokenStorage = new TokenStorage(); $eventDispatcher = /* EventDispatcherInterface instance */; $handler = new GuardAuthenticatorHandler( $tokenStorage, $eventDispatcher, ['api'] // stateless provider keys (don't migrate session) ); // Manual authentication (useful for registration or programmatic login) $user = $userRepository->find(123); $request = Request::createFromGlobals(); $authenticator = new LoginFormAuthenticator(/* ... */); // Authenticate user and handle success response $response = $handler->authenticateUserAndHandleSuccess( $user, $request, $authenticator, 'main' // firewall name ); // Or authenticate with existing token $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles()); $handler->authenticateWithToken($token, $request, 'main'); // Handle authentication failure $exception = new AuthenticationException('Invalid credentials'); $response = $handler->handleAuthenticationFailure($exception, $request, $authenticator, 'main'); ``` ## Summary Symfony Security is ideal for building secure web applications that require user authentication, role-based access control, and protection against common security vulnerabilities. Common use cases include: implementing login systems with multiple authentication methods (form login, API tokens, OAuth), protecting routes based on user roles, creating fine-grained access control using custom voters, securing forms against CSRF attacks, and implementing remember-me functionality. The component excels in scenarios requiring complex authorization rules, such as content management systems where users can only edit their own content, or multi-tenant applications with organization-level permissions. Integration with Symfony applications is seamless through the SecurityBundle, but the component can also be used standalone in any PHP application. The Guard component particularly simplifies custom authentication by providing a single interface to implement the entire authentication flow. For best practices, always use the password encoder with bcrypt or Argon2 algorithms, implement CSRF protection on all state-changing forms, use voters for object-level permissions, and configure role hierarchies to avoid redundant role checks. The component's event system allows extending the security layer with custom listeners for audit logging, multi-factor authentication, or account lockout policies.