Symfony Roles & Permissions: A Complete Tutorial (Beginner-Friendly)
Managing user access is crucial for web applications. Symfony provides a powerful role-based access control (RBAC) system to restrict what users can do.
In this guide, you’ll learn:
✅ How to define roles in Symfony
✅ Restricting routes by roles
✅ Creating custom permissions with voters
✅ Best practices for security
Let’s build a secure role-based system step by step!
1. Understanding Symfony Roles
What Are Roles?
Roles define user access levels (e.g., ROLE_USER, ROLE_ADMIN).
Default Roles in Symfony
ROLE_USER→ Basic authenticated userROLE_ADMIN→ Full access
Where Are Roles Stored?
In the User entity (generated by make:user):
// src/Entity/User.php
#[ORM\Column(type: 'json')]
private array $roles = [];
public function getRoles(): array
{
$roles = $this->roles;
$roles[] = 'ROLE_USER'; // Every user has ROLE_USER by default
return array_unique($roles);
}
2. Assigning Roles to Users
Manually Assigning Roles
Edit a user in the database:
UPDATE user SET roles = '["ROLE_ADMIN"]' WHERE id = 1;
Programmatically (e.g., in Registration)
// src/Controller/RegistrationController.php
$user->setRoles(['ROLE_USER']); // Default role
Promoting a User to Admin
$user->setRoles(['ROLE_ADMIN', 'ROLE_USER']);
3. Restricting Access by Role
Method 1: In security.yaml
# config/packages/security.yaml
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
Method 2: In Controllers (Annotations)
// src/Controller/AdminController.php
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_ADMIN')]
public function adminDashboard(): Response
{
return $this->render('admin/dashboard.html.twig');
}
Method 3: Twig Templates
{% if is_granted('ROLE_ADMIN') %}
<a href="/admin">Admin Panel</a>
{% endif %}
4. Creating Custom Permissions with Voters
Sometimes, roles aren’t enough (e.g., "Can this user edit this post?"). For fine-grained control, use Voters.
Step 1: Generate a Voter
php bin/console make:voter
Name it PostVoter.
Step 2: Define Permissions
Edit src/Security/Voter/PostVoter.php:
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, ['EDIT', 'DELETE']) && $subject instanceof Post;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) return false;
$post = $subject;
return match ($attribute) {
'EDIT' => $user === $post->getAuthor() || in_array('ROLE_ADMIN', $user->getRoles()),
'DELETE' => in_array('ROLE_ADMIN', $user->getRoles()),
default => false,
};
}
Step 3: Use the Voter in a Controller
#[IsGranted('EDIT', subject: 'post')]
public function edit(Post $post): Response
{
// Only the author or admin can edit
}
5. Hierarchical Roles (Role Inheritance)
Define role hierarchy in security.yaml:
security:
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_MODERATOR]
ROLE_MODERATOR: [ROLE_USER]
Now:
- Admins automatically have
ROLE_MODERATORandROLE_USER. - Moderators have
ROLE_USER.
6. Testing Roles & Permissions
Check Roles in PHP
$this->isGranted('ROLE_ADMIN'); // Returns true/false
Mocking Users in Tests
$user = new User();
$user->setRoles(['ROLE_ADMIN']);
$this->client->loginUser($user);
Best Practices for Symfony Security
🔹 Always use ROLE_ prefix (Symfony convention).
🔹 Prefer voters over multiple roles for complex rules.
🔹 Never store sensitive logic in Twig (handle in PHP).
🔹 Use $this->denyAccessUnlessGranted() for quick checks.
Final Thoughts
You’ve now mastered Symfony’s role and permission system!
Recap:
✅ Defined roles and restricted routes
✅ Created custom permissions with voters
✅ Used role hierarchy for inheritance
Next Steps:
➡️ Try API tokens for stateless auth (lexik/jwt-authentication-bundle)
➡️ Explore Symfony ACL for advanced permissions
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.