Symfony2: logging out
- Transfer
One of the golden rules of Symfony2 is never to hardcode any links and paths inside code or templates. Compliance with this rule and generating links via a router will greatly facilitate your life. However, there is one thing that I often observe: people continue to hardcode links to logout, for example, as "/ logout", only the logout process itself is a little more complicated than it may seem and using such a link can work in most cases, but it will not be the best solution to the problem.
Most developers know that you can make several protected sections within one project. For example, it could be a panel for regular users (registered users) with the / secure path . And, perhaps, your project may have a separate admin panel at / admin and there is a separate zone for API users located in the section with the / api address . Also, you can make a "protected zone", which does not need protection at all - this approach is used in the Symfony2 toolbarfor developers. In the end, all this can be transferred to one large zone, in which several options for determining who has access to the protected part of the project will be implemented. In general, although dividing a project into separate zones makes your project more difficult, it gives some advantages.
Each of the protected zones calls its own firewall, which determines whether to authenticate the user or not. Each firewall is separate from the others: if you authenticate in one of them, this does not mean that you are automatically authenticated in others and there is only one active firewall (the one that matches the URL pattern). This matters because different firewalls can use different databases or just use different authentication methods (for example, in the API you can use OAuth Token, while the rest of the sections can use the login form).
This also means that each firewall has different logout paths, and for some of them the logout as such does not exist. Security.yml example
This is the “firewall” block in security.yml and it defines 3 firewalls - dev , superadminstuff and main . dev does not use authentication at all (security = false), which means that access is allowed to everyone and the paths "/ js", "/ css" and others are not controlled by the main firewall.
The following set of rules protects the administration area. It uses http_basicas an input, that is, the browser will show a dialog box asking you to specify a username and password (in fact, this is not very safe, since they will be transmitted as plain text). Moreover - the browser will send a username and password with every request to the project. Symfony2 can verify this data using the “memory_user_provider” provider, the block of which I did not cite, but it usually indicates several standard users and their username / password (directly in the configuration file, not in the database).
In http-basic, there really is no logout because the only way out in this case is to stop sending requests. Clearing the cache or or restarting the browser usually helps to log out in this case.
The last firewall is main. Instead of http-basic, it uses a login form. It uses FOSUserBundle, which has its own login form and methods for processing it, so the only thing that is required from the developer is to customize them a bit, and not write your own.
If you open a page in this firewall and have not logged in before, Symfony2 will automatically redirect the user to the login page, which is specified in the login_path parameter in the form_login block . Usually (by default), this is the path with the address / login (you can change it as you like, or even, if you want, you can specify a route). As soon as the user logs in, Symfony2 saves the user and the role inside his session, and the next time the user prompts, he does not have to log in again.
Exiting such a firewall is quite simple - you need to go to its exit page. But what is this page?
In the example above, the parameter "logout: true" is used. It is worth noting that this parameter is located in the firewall block, and not in the form_login block. By specifying logout: true , we tell Symfony2 to use the standard logout settings, namely:
As you can see, the path along which the exit will take place is indicated. But there is one oddity: by default, the logout listener is launched before calling any controller or action, and then it redirects to the page specified in the “target” parameter. If you have your own logout event handler, which is specified in the “handlers” parameter, and it does NOT return an HTTP Response object, then the current route is called. That is, by default, your controller / action will not be called, BUT they must be specified (that is, the Symfony2 router must be required to know about it). For this reason, we can find the weird logout action in the FOSUserBundle throwing an exception since it will never be called.
So what to do with the exit? First of all, do not hardcode URLs. Even if you use a route instead of url, you can change it inside the configuration and the output will stop working. What is really worth doing is to indicate via twig the link or route specified in the configuration. Fortunately, SecurityBundle has an extension for twig that will help to do this. These are the logout_url and logout_path functions. These functions receive the firewall id (for example, "main", "dev", etc.) and generate the correct exit address for it:
In this case, the correct address will be selected and csrf-token will be added as a bonus, if this was specified in the configuration. Thus, instead of specifying the page address in the template, you need to specify the firewall that is currently being used.
However, now your templates know more than necessary and you must specify the name of the firewall manually. In most cases, this is normal, but sometimes it can cause problems (for example, if you use a menu where twig is used). To avoid problems with this, it is possible to get the name of the current firewall, albeit a little incorrect:
Inside the security context token is the name of the current firewall that we need. The “incorrectness” of the solution is that the global variable app.security in Symfony version 2.6 will be deprecated and removed in version 3.0. Over time, I am sure there will be other ways of generating a way out.
A little information about the component (and bundle) of Symfony2 Security
Most developers know that you can make several protected sections within one project. For example, it could be a panel for regular users (registered users) with the / secure path . And, perhaps, your project may have a separate admin panel at / admin and there is a separate zone for API users located in the section with the / api address . Also, you can make a "protected zone", which does not need protection at all - this approach is used in the Symfony2 toolbarfor developers. In the end, all this can be transferred to one large zone, in which several options for determining who has access to the protected part of the project will be implemented. In general, although dividing a project into separate zones makes your project more difficult, it gives some advantages.
Each of the protected zones calls its own firewall, which determines whether to authenticate the user or not. Each firewall is separate from the others: if you authenticate in one of them, this does not mean that you are automatically authenticated in others and there is only one active firewall (the one that matches the URL pattern). This matters because different firewalls can use different databases or just use different authentication methods (for example, in the API you can use OAuth Token, while the rest of the sections can use the login form).
This also means that each firewall has different logout paths, and for some of them the logout as such does not exist. Security.yml example
# Раздел разработчика
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# Раздел админа
superadminstuff:
pattern: ^/admin
http_basic:
provider: memory_user_provider
realm: "Super Admin section!"
# все остальное с обычной формой для входа
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
login_path: /login
logout: true
This is the “firewall” block in security.yml and it defines 3 firewalls - dev , superadminstuff and main . dev does not use authentication at all (security = false), which means that access is allowed to everyone and the paths "/ js", "/ css" and others are not controlled by the main firewall.
The following set of rules protects the administration area. It uses http_basicas an input, that is, the browser will show a dialog box asking you to specify a username and password (in fact, this is not very safe, since they will be transmitted as plain text). Moreover - the browser will send a username and password with every request to the project. Symfony2 can verify this data using the “memory_user_provider” provider, the block of which I did not cite, but it usually indicates several standard users and their username / password (directly in the configuration file, not in the database).
In http-basic, there really is no logout because the only way out in this case is to stop sending requests. Clearing the cache or or restarting the browser usually helps to log out in this case.
The last firewall is main. Instead of http-basic, it uses a login form. It uses FOSUserBundle, which has its own login form and methods for processing it, so the only thing that is required from the developer is to customize them a bit, and not write your own.
If you open a page in this firewall and have not logged in before, Symfony2 will automatically redirect the user to the login page, which is specified in the login_path parameter in the form_login block . Usually (by default), this is the path with the address / login (you can change it as you like, or even, if you want, you can specify a route). As soon as the user logs in, Symfony2 saves the user and the role inside his session, and the next time the user prompts, he does not have to log in again.
Exiting such a firewall is quite simple - you need to go to its exit page. But what is this page?
In the example above, the parameter "logout: true" is used. It is worth noting that this parameter is located in the firewall block, and not in the form_login block. By specifying logout: true , we tell Symfony2 to use the standard logout settings, namely:
logout:
csrf_parameter: _csrf_token
csrf_token_generator: ~
csrf_token_id: logout
path: /logout
target: /
success_handler: ~
invalidate_session: true
delete_cookies:
name:
path: null
domain: null
handlers: []
As you can see, the path along which the exit will take place is indicated. But there is one oddity: by default, the logout listener is launched before calling any controller or action, and then it redirects to the page specified in the “target” parameter. If you have your own logout event handler, which is specified in the “handlers” parameter, and it does NOT return an HTTP Response object, then the current route is called. That is, by default, your controller / action will not be called, BUT they must be specified (that is, the Symfony2 router must be required to know about it). For this reason, we can find the weird logout action in the FOSUserBundle throwing an exception since it will never be called.
Logout
So what to do with the exit? First of all, do not hardcode URLs. Even if you use a route instead of url, you can change it inside the configuration and the output will stop working. What is really worth doing is to indicate via twig the link or route specified in the configuration. Fortunately, SecurityBundle has an extension for twig that will help to do this. These are the logout_url and logout_path functions. These functions receive the firewall id (for example, "main", "dev", etc.) and generate the correct exit address for it:
Logout
In this case, the correct address will be selected and csrf-token will be added as a bonus, if this was specified in the configuration. Thus, instead of specifying the page address in the template, you need to specify the firewall that is currently being used.
However, now your templates know more than necessary and you must specify the name of the firewall manually. In most cases, this is normal, but sometimes it can cause problems (for example, if you use a menu where twig is used). To avoid problems with this, it is possible to get the name of the current firewall, albeit a little incorrect:
Logout
Inside the security context token is the name of the current firewall that we need. The “incorrectness” of the solution is that the global variable app.security in Symfony version 2.6 will be deprecated and removed in version 3.0. Over time, I am sure there will be other ways of generating a way out.
Only registered users can participate in the survey. Please come in.
Firewall
- 41.8% Firewall 97
- 58.1% Firewall 135