In applications using Symfony Flex, run this command to
install the security feature before using it:

1

$ composer require symfony/security-bundle

Tip

A new experimental Security
was introduced in Symfony 5.1, which will eventually replace security in
Symfony 6.0. This system is almost fully backwards compatible with the
current Symfony security, add this line to your security configuration to start
using it:

No matter how you will authenticate (e.g. login form or API tokens) or where
your user data will be stored (database, single sign-on), the next step is always the same:
create a "User" class. The easiest way is to use the MakerBundle.

Let's assume that you want to store your user data in the database with Doctrine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

$ php bin/console make:user
The name of the security user class (e.g. User) [User]:> UserDo you want to store user data in the database (via Doctrine)? (yes/no) [yes]:> yesEnter a property name that will be the unique "display" name for the user (e.g.email, username, uuid [email]> emailDoes this app need to hash/check user passwords? (yes/no) [yes]:> yescreated: src/Entity/User.phpcreated: src/Repository/UserRepository.phpupdated: src/Entity/User.phpupdated: config/packages/security.yaml

That's it! The command asks several questions so that it can generate exactly what
you need. The most important is the User.php file itself. The only rule about
your User class is that it must implement UserInterface.
Feel free to add any other fields or logic you need. If your User class is
an entity (like in this example), you can use the make:entity command
to add more fields. Also, make sure to make and run a migration for the new entity:

In addition to your User class, you also need a "User provider": a class that
helps with a few things, like reloading the User data from the session and some
optional features, like remember me and
impersonation.

Fortunately, the make:user command already configured one for you in your
security.yaml file under the providers key.

If your User class is an entity, you don't need to do anything else. But if
your class is not an entity, then make:user will also have generated a
UserProvider class that you need to finish. Learn more about user providers
here: User Providers.

Not all applications have "users" that need passwords. If your users have passwords,
you can control how those passwords are encoded in security.yaml. The make:user
command will pre-configure this for you:

A "firewall" is your authentication system: the configuration below it defines
how your users will be able to authenticate (e.g. login form, API token, etc).

Only one firewall is active on each request: Symfony uses the pattern key
to find the first match (you can also match by host or other things).
The dev firewall is really a fake firewall: it just makes sure that you don't
accidentally block Symfony's dev tools - which live under URLs like /_profiler
and /_wdt.

All real URLs are handled by the main firewall (no pattern key means
it matches all URLs). A firewall can have many modes of authentication,
in other words many ways to ask the question "Who are you?". Often, the
user is unknown (i.e. not logged in) when they first visit your website. The
anonymous mode, if enabled, is used for these requests.

In fact, if you go to the homepage right now, you will have access and you'll
see that you're "authenticated" as anon.. The firewall verified that it
does not know your identity, and so, you are anonymous:

It means any request can have an anonymous token to access some resource,
while some actions (i.e. some pages or buttons) can still require specific
privileges. A user can then access a form login without being authenticated
as a unique user (otherwise an infinite redirection loop would happen
asking the user to authenticate while trying to doing so).

You'll learn later how to deny access to certain URLs, controllers, or part of
templates.

Tip

The lazy anonymous mode prevents the session from being started if
there is no need for authorization (i.e. explicit check for a user
privilege). This is important to keep requests cacheable (see
HTTP Cache).

Authentication in Symfony can feel a bit "magic" at first. That's because, instead
of building a route & controller to handle login, you'll activate an
authentication provider: some code that runs automatically before your controller
is called.

Symfony has several built-in authentication providers.
If your use-case matches one of these exactly, great! But, in most cases - including
a login form - we recommend building a Guard Authenticator: a class that allows
you to control every part of the authentication process (see the next section).

Tip

If your application logs users in via a third-party service such as Google,
Facebook or Twitter (social login), check out the HWIOAuthBundle community
bundle.

Users can now log in to your app using your login form. Great! Now, you need to learn
how to deny access and work with the User object. This is called authorization,
and its job is to decide if a user can access some resource (a URL, a model object,
a method call, ...).

The process of authorization has two different sides:

The user receives a specific set of roles when logging in (e.g. ROLE_ADMIN).

You add code so that a resource (e.g. URL, controller) requires a specific
"attribute" (most commonly a role like ROLE_ADMIN) in order to be
accessed.

When a user logs in, Symfony calls the getRoles() method on your User
object to determine which roles this user has. In the User class that we
generated earlier, the roles are an array that's stored in the database, and
every user is always given at least one role: ROLE_USER:

// config/packages/security.php$container->loadFromExtension('security',[// ...'firewalls'=>[// ...'main'=>[// ...],],'access_control'=>[// require ROLE_ADMIN for /admin*['path'=>'^/admin','roles'=>'ROLE_ADMIN'],// require ROLE_ADMIN or IS_AUTHENTICATED_FULLY for /admin*['path'=>'^/admin','roles'=>['ROLE_ADMIN','IS_AUTHENTICATED_FULLY']],// the 'path' value can be any valid regular expression// (this one will match URLs like /api/post/7298 and /api/comment/528491)['path'=>'^/api/(post|comment)/\d+$','roles'=>'ROLE_USER'],],]);

You can define as many URL patterns as you need - each is a regular expression.
BUT, only one will be matched per request: Symfony starts at the top of
the list and stops when it finds the first match:

// src/Controller/AdminController.php// ...publicfunctionadminDashboard(){$this->denyAccessUnlessGranted('ROLE_ADMIN');// or add an optional message - seen by developers$this->denyAccessUnlessGranted('ROLE_ADMIN',null,'User tried to access a page without having ROLE_ADMIN');}

That's it! If access is not granted, a special
AccessDeniedException
is thrown and no more code in your controller is executed. Then, one of two things
will happen:

If the user isn't logged in yet, they will be asked to log in (e.g. redirected
to the login page).

If the user is logged in, but does not have the ROLE_ADMIN role, they'll
be shown the 403 access denied page (which you can
customize).

Thanks to the SensioFrameworkExtraBundle, you can also secure your controller
using annotations:

Most applications require more specific access rules. For instance, a user
should be able to only edit their own comments on a blog. Voters allow you
to write whatever business logic you need to determine access. Using
these voters is similar to the role-based access checks implemented in the
previous chapters. Read How to Use Voters to Check User Permissions to learn how to implement
your own voter.

If you only want to check if a user is logged in (you don't care about roles),
you have two options. First, if you've given every user ROLE_USER, you can
just check for that role. Otherwise, you can use a special "attribute" in place
of a role:

You can use IS_AUTHENTICATED_FULLY anywhere roles are used: like
access_control or in Twig.

IS_AUTHENTICATED_FULLY isn't a role, but it kind of acts like one, and every
user that has logged in will have this. Actually, there are some special attributes
like this:

IS_AUTHENTICATED_REMEMBERED: All logged in users have this, even
if they are logged in because of a "remember me cookie". Even if you don't
use the remember me functionality,
you can use this to check if the user is logged in.

IS_AUTHENTICATED_FULLY: This is similar to IS_AUTHENTICATED_REMEMBERED,
but stronger. Users who are logged in only because of a "remember me cookie"
will have IS_AUTHENTICATED_REMEMBERED but will not have IS_AUTHENTICATED_FULLY.

After authentication, the User object of the current user can be accessed
via the getUser() shortcut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

publicfunctionindex(){// usually you'll want to make sure the user is authenticated first$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');// returns your User object, or null if the user is not authenticated// use inline documentation to tell your editor your exact User class/** @var \App\Entity\User $user */$user=$this->getUser();// Call whatever methods you've added to your User class// For example, if you added a getFirstName() method, you can use that.returnnewResponse('Well hi there '.$user->getFirstName());}

Next, you'll need to create a route for this URL (but not a controller):

Annotations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// src/Controller/SecurityController.phpnamespaceApp\Controller;useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;useSymfony\Component\Routing\Annotation\Route;classSecurityControllerextendsAbstractController{/** * @Route("/logout", name="app_logout", methods={"GET"}) */publicfunctionlogout(){// controller can be blank: it will never be executed!thrownew\Exception('Don\'t forget to activate logout in security.yaml');}}

New in version 5.1: The LogoutEvent was introduced in Symfony 5.1. Prior to this
version, you had to use a
logout success handler
to customize the logout.

In some cases you need to execute extra logic upon logout (e.g. invalidate
some tokens) or want to customize what happens after a logout. During
logout, a LogoutEvent
is dispatched. Register an event listener or subscriber
to execute custom logic. The following information is available in the
event class:

getToken()

Returns the security token of the session that is about to be logged
out.

getRequest()

Returns the current request.

getResponse()

Returns a response, if it is already set by a custom listener. Use
setResponse() to configure a custom logout response.

Tip

Every Security firewall has its own event dispatcher
(security.event_dispatcher.FIREWALLNAME). The logout event is
dispatched on both the global and firewall dispatcher. You can register
on the firewall dispatcher if you want your listener to only be
executed for a specific firewall. For instance, if you have an api
and main firewall, use this configuration to register only on the
logout event in the main firewall:

Users with the ROLE_ADMIN role will also have the
ROLE_USER role. And users with ROLE_SUPER_ADMIN, will automatically have
ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH and ROLE_USER (inherited from ROLE_ADMIN).

For role hierarchy to work, do not try to call $user->getRoles() manually.
For example, in a controller extending from the base controller:

1
2
3
4
5
6

// BAD - $user->getRoles() will not know about the role hierarchy$hasAccess=in_array('ROLE_ADMIN',$user->getRoles());// GOOD - use of the normal security methods$hasAccess=$this->isGranted('ROLE_ADMIN');$this->denyAccessUnlessGranted('ROLE_ADMIN');

Note

The role_hierarchy values are static - you can't, for example, store the
role hierarchy in a database. If you need that, create a custom
security voter that looks for the user roles
in the database.

Yes! But it's usually not necessary. Each firewall is like a separate security
system. And so, unless you have very different authentication needs, one
firewall usually works well. With Guard authentication,
you can create various, diverse ways of allowing authentication (e.g. form login,
API key authentication and LDAP) all under the same firewall.

Can I Share Authentication Between Firewalls?

Yes, but only with some configuration. If you're using multiple firewalls and
you authenticate against one firewall, you will not be authenticated against
any other firewalls automatically. Different firewalls are like different security
systems. To do this you have to explicitly specify the same
Firewall Context for different firewalls. But usually
for most applications, having one main firewall is enough.

Security doesn't seem to work on my Error Pages

As routing is done before security, 404 error pages are not covered by
any firewall. This means you can't check for security or even access the
user object on these pages. See How to Customize Error Pages
for more details.

My Authentication Doesn't Seem to Work: No Errors, but I'm Never Logged In

Sometimes authentication may be successful, but after redirecting, you're
logged out immediately due to a problem loading the User from the session.
To see if this is an issue, check your log file (var/log/dev.log) for
the log message:

Cannot refresh token because user has changed

If you see this, there are two possible causes. First, there may be a problem
loading your User from the session. See Understanding how Users are Refreshed from the Session. Second,
if certain user information was changed in the database since the last page
refresh, Symfony will purposely log out the user for security reasons.