Authorization

Authorization

When any of the authentication providers (see Authentication Providers) has verified the still-unauthenticated token, an authenticated token will be returned. The authentication listener should set this token directly in the Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface using its [setToken()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.php "Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface::setToken()") method.

From then on, the user is authenticated, i.e. identified. Now, other parts of the application can use the token to decide whether or not the user may request a certain URI, or modify a certain object. This decision will be made by an instance of Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface.

An authorization decision will always be based on a few things:

  • The current token

    For instance, the token’s [getRoles()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php "Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getRoles()") method may be used to retrieve the roles of the current user (e.g. ROLE_SUPER_ADMIN), or a decision may be based on the class of the token.

  • A set of attributes

    Each attribute stands for a certain right the user should have, e.g. ROLE_ADMIN to make sure the user is an administrator.

  • An object (optional)

    Any object for which access control needs to be checked, like an article or a comment object.

Access Decision Manager

Since deciding whether or not a user is authorized to perform a certain action can be a complicated process, the standard Symfony\Component\Security\Core\Authorization\AccessDecisionManager itself depends on multiple voters, and makes a final verdict based on all the votes (either positive, negative or neutral) it has received. It recognizes several strategies:

affirmative (default)

grant access as soon as there is one voter granting access;

consensus

grant access if there are more voters granting access than there are denying;

unanimous

only grant access if none of the voters has denied access. If all voters abstained from voting, the decision is based on the allow_if_all_abstain config option (which defaults to false).

Usage of the available options in detail:

  1. use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
  2. // instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
  3. $voters = [...];
  4. // one of "affirmative", "consensus", "unanimous"
  5. $strategy = ...;
  6. // whether or not to grant access when all voters abstain
  7. $allowIfAllAbstainDecisions = ...;
  8. // whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
  9. $allowIfEqualGrantedDeniedDecisions = ...;
  10. $accessDecisionManager = new AccessDecisionManager(
  11. $voters,
  12. $strategy,
  13. $allowIfAllAbstainDecisions,
  14. $allowIfEqualGrantedDeniedDecisions
  15. );

See also

You can change the default strategy in the configuration.

Voters

Voters are instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface, which means they have to implement a few methods which allows the decision manager to use them:

vote(TokenInterface $token, $object, array $attributes)

this method will do the actual voting and return a value equal to one of the class constants of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface, i.e. VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED or VoterInterface::ACCESS_ABSTAIN;

The Security component contains some standard voters which cover many use cases:

AuthenticatedVoter

The Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter voter supports the attributes IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, and IS_AUTHENTICATED_ANONYMOUSLY and grants access based on the current level of authentication, i.e. is the user fully authenticated, or only based on a “remember-me” cookie, or even authenticated anonymously?:

  1. use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;
  2. $trustResolver = new AuthenticationTrustResolver();
  3. $authenticatedVoter = new AuthenticatedVoter($trustResolver);
  4. // instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
  5. $token = ...;
  6. // any object
  7. $object = ...;
  8. $vote = $authenticatedVoter->vote($token, $object, ['IS_AUTHENTICATED_FULLY']);

RoleVoter

The Symfony\Component\Security\Core\Authorization\Voter\RoleVoter supports attributes starting with ROLE_ and grants access to the user when at least one required ROLE_* attribute can be found in the array of roles returned by the token’s [getRoles()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php "Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getRoles()") method:

  1. use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
  2. $roleVoter = new RoleVoter('ROLE_');
  3. $roleVoter->vote($token, $object, ['ROLE_ADMIN']);

RoleHierarchyVoter

The Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter extends Symfony\Component\Security\Core\Authorization\Voter\RoleVoter and provides some additional functionality: it knows how to handle a hierarchy of roles. For instance, a ROLE_SUPER_ADMIN role may have sub-roles ROLE_ADMIN and ROLE_USER, so that when a certain object requires the user to have the ROLE_ADMIN role, it grants access to users who in fact have the ROLE_ADMIN role, but also to users having the ROLE_SUPER_ADMIN role:

  1. use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
  2. use Symfony\Component\Security\Core\Role\RoleHierarchy;
  3. $hierarchy = [
  4. 'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN', 'ROLE_USER'],
  5. ];
  6. $roleHierarchy = new RoleHierarchy($hierarchy);
  7. $roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);

ExpressionVoter

The Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter grants access based on the evaluation of expressions created with the ExpressionLanguage component. These expressions have access to a number of special security variables:

  1. use Symfony\Component\ExpressionLanguage\Expression;
  2. use Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter;
  3. // Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
  4. $expressionLanguage = ...;
  5. // instance of Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface
  6. $trustResolver = ...;
  7. // Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
  8. $authorizationChecker = ...;
  9. $expressionVoter = new ExpressionVoter($expressionLanguage, $trustResolver, $authorizationChecker);
  10. // instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
  11. $token = ...;
  12. // any object
  13. $object = ...;
  14. $expression = new Expression(
  15. '"ROLE_ADMIN" in roles or (not is_anonymous() and user.isSuperAdmin())'
  16. );
  17. $vote = $expressionVoter->vote($token, $object, [$expression]);

Note

When you make your own voter, you can use its constructor to inject any dependencies it needs to come to a decision.

Roles

Roles are strings that give expression to a certain right the user has (e.g. “edit a blog post”, “create an invoice”). You can freely choose those strings. The only requirement is that they must start with the ROLE_ prefix (e.g. ROLE_POST_EDIT, ROLE_INVOICE_CREATE).

Using the Decision Manager

The Access Listener

The access decision manager can be used at any point in a request to decide whether or not the current user is entitled to access a given resource. One optional, but useful, method for restricting access based on a URL pattern is the Symfony\Component\Security\Http\Firewall\AccessListener, which is one of the firewall listeners (see Firewall Listeners) that is triggered for each request matching the firewall map (see A Firewall for HTTP Requests).

It uses an access map (which should be an instance of Symfony\Component\Security\Http\AccessMapInterface) which contains request matchers and a corresponding set of attributes that are required for the current user to get access to the application:

  1. use Symfony\Component\HttpFoundation\RequestMatcher;
  2. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
  3. use Symfony\Component\Security\Http\AccessMap;
  4. use Symfony\Component\Security\Http\Firewall\AccessListener;
  5. $accessMap = new AccessMap();
  6. $tokenStorage = new TokenStorage();
  7. $requestMatcher = new RequestMatcher('^/admin');
  8. $accessMap->add($requestMatcher, ['ROLE_ADMIN']);
  9. $accessListener = new AccessListener(
  10. $tokenStorage,
  11. $accessDecisionManager,
  12. $accessMap,
  13. $authenticationManager
  14. );

Authorization Checker

The access decision manager is also available to other parts of the application via the [isGranted()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php "Symfony\Component\Security\Core\Authorization\AuthorizationChecker::isGranted()") method of the Symfony\Component\Security\Core\Authorization\AuthorizationChecker. A call to this method will directly delegate the question to the access decision manager:

  1. use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
  2. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  3. $authorizationChecker = new AuthorizationChecker(
  4. $tokenStorage,
  5. $authenticationManager,
  6. $accessDecisionManager
  7. );
  8. if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
  9. throw new AccessDeniedException();
  10. }

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.