Symfony Roles & Permissions: A Complete Tutorial (Beginner-Friendly)

Author

Kritim Yantra

Apr 18, 2025

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 user
  • ROLE_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_MODERATOR and ROLE_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

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts