symfony/security-core
Symfony Security Core provides the building blocks for authentication and authorization. Use tokens, voters, role hierarchies, and an access decision manager to cleanly separate access rules from user providers and credential storage.
To integrate symfony/security-core into a Laravel project, start by installing the package and setting up a basic authentication workflow:
composer require symfony/security-core
First Use Case: Role-Based Access Control
Define a User Entity:
Implement Symfony\Component\Security\Core\User\UserInterface in your Laravel user model (e.g., app/Models/User.php):
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
public function getRoles(): array
{
return ['ROLE_USER']; // Default role
}
public function getPassword(): string
{
return $this->password;
}
// ... other required methods
}
Create a Token:
Use UsernamePasswordToken to represent an authenticated user:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
$user = User::find(1);
$token = new UsernamePasswordToken(
$user,
'main', // Firewall name
$user->getRoles()
);
Check Authorization:
Use AccessDecisionManager to evaluate access:
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
$accessDecisionManager = new AccessDecisionManager([
new AuthenticatedVoter(),
new RoleVoter(),
]);
if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) {
abort(403, 'Access denied');
}
Where to Look First:
src/Symfony/Component/Security/Core/Authorization/ for voters and decision managers.src/Symfony/Component/Security/Core/Authentication/ for token and user provider logic.UserProviderInterface to load users (e.g., from a database or LDAP).
use Symfony\Component\Security\Core\User\UserProviderInterface;
class EloquentUserProvider implements UserProviderInterface
{
public function loadUserByIdentifier(string $identifier): UserInterface
{
return User::where('email', $identifier)->firstOrFail();
}
}
AuthenticationManager to authenticate tokens:
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
$authManager = new AuthenticationManager([
new AuthenticationProvider($userProvider),
]);
$authenticatedToken = $authManager->authenticate($token);
Voter for custom logic (e.g., TenantVoter):
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class TenantVoter extends Voter
{
protected function supports(string $attribute, mixed $subject): bool
{
return $attribute === 'ACCESS_TENANT';
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
return $user->tenantId === $subject;
}
}
$accessDecisionManager = new AccessDecisionManager([
new AuthenticatedVoter(),
new RoleVoter(),
new TenantVoter(),
]);
config/security.php:
'role_hierarchy' => [
'ROLE_ADMIN' => ['ROLE_USER'],
'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN'],
],
RoleHierarchyVoter to enable inheritance:
use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
$roleHierarchy = new RoleHierarchy([
'ROLE_ADMIN' => ['ROLE_USER'],
]);
$accessDecisionManager->addVoter(new RoleHierarchyVoter($roleHierarchy));
AccessDecisionManager:
namespace App\Http\Middleware;
use Closure;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
class AuthorizeMiddleware
{
public function __construct(
protected AccessDecisionManagerInterface $decisionManager
) {}
public function handle($request, Closure $next)
{
$token = auth()->user() ? auth()->user()->getToken() : null;
if (!$this->decisionManager->decide($token, ['ROLE_ADMIN'])) {
abort(403);
}
return $next($request);
}
}
app/Http/Kernel.php:
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\AuthorizeMiddleware::class,
];
VoteResult::ABSTAIN and extraData in voters for dynamic checks:
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$attributes = $subject->getAttributes();
if ($attributes['max_price'] > $token->getUser()->budget) {
return false;
}
return true;
}
Leverage Laravel’s Auth Facade:
Extend Laravel’s Auth facade to use Symfony’s AuthenticationManager:
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
class SymfonyAuthServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(AuthenticationManagerInterface::class, function ($app) {
return new AuthenticationManager([...]);
});
}
}
Caching Role Hierarchies: Cache the role hierarchy to improve performance:
$roleHierarchy = new RoleHierarchy($roles, CacheInterface::class);
Testing:
Use Symfony’s TestUser for unit tests:
use Symfony\Component\Security\Core\Authentication\Test\TestUser;
$user = new TestUser(['ROLE_ADMIN'], 'john@example.com', ['name' => 'John']);
$token = new UsernamePasswordToken($user, 'main', $user->getRoles());
OAuth2 Introspection:
Integrate with OAuth2 providers using OAuth2IntrospectionEndpoint:
use Symfony\Component\Security\Core\Authentication\Provider\OAuth2IntrospectionProvider;
$provider = new OAuth2IntrospectionProvider(
$client,
'https://oauth-provider.com/introspect'
);
Token Serialization:
Serializable interface or use serialize()/unserialize():
public function serialize(): string
{
return serialize([
$this->user->getRoles(),
$this->credentials,
]);
}
public function unserialize(string $serialized): void
{
[$roles, $credentials] = unserialize($serialized);
$this->user->setRoles($roles);
$this->credentials = $credentials;
}
Role Hierarchy Caching:
$roleHierarchy = new RoleHierarchy($roles, CacheInterface::class, 300); // 5-minute TTL
Voter Order Matters:
ACCESS_GRANTED or ACCESS_DENIED result stops further evaluation.TenantVoter) before generic ones (e.g., RoleVoter).PHPUnit Mocking:
createMock() with configured expectations or prefer self::assertInstanceOf():
$mockUser = $this->createMock(UserInterface::class);
$mockUser->method('getRoles')->willReturn(['ROLE_USER']);
Remember-Me Cookies:
How can I help you explore Laravel packages today?