Authentication : app_dev.php/_wdt/511509b611682 instead of homepage - symfony

I'm looking at security in Symfony 2.0 and I have a problem I can't explain.
My security bundle is very simple for now.
I try to put everything working before putting real providers.
So, now, when I go on the site, It sends me on the login form as expected. I put the user and the password and then, instead of home page, i am send to "/.../app_dev.php/_wdt/511509b611682" (different number each time).
My user isn't marked as authenticated in the debug toolbar.
If i take off the end of the url, i arrive on homepage. My user seems to be identified and authenticated in the debug toolbar.
This arrives only in dev environment. In prod environment, it seems to work as expected.
Thanks for your help

Further to #artworkad's answer, you have to add the dev firewall before your main firewall, otherwise it will never match:
security:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
#...

When you log in using development environment you will be redirected to index_dev.php/_wdt/4e95412bc6871.
WDT aka web debug toolbar can be removed from the scope of the firewall via
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
Actually it is not related to SecurityBundle, anyways it is documented here https://github.com/FriendsOfSymfony/FOSUserBundle/issues/368

Also you can put this lines in access_control:
{ path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
{ path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
but I prefer keep my access_control for only my app routes.

Related

Symfony firewall access_control doesn't work if no firewall match

I have a problem with symfony firewalls...
Here an example:
app_customer_provider:
security: true
lazy: true
stateless person: true
custom_authenticators:
- XXX\Authenticator
- XXX\Authenticator
request_matcher: - XXX\Mathcer
In the access control access_control I have a protected route, ex /ping.
When I try to login with a JWT that matches the request_matcher, the authenticator works fine; blocking or allow access to the various routes.
The problem arises when no request_matcher intervenes. All routes are open despite being role protected, ex /ping is accessible.
Can you help me about it? Is there something I’m doing wrong or I’m missing?
It seems it is a symfony behavior...
If no firewall is hitted, the security_access is not considered.
I use this workaround, use a fallback firewall in this way :
secured_area:
pattern: ^/

Symfony Logon failing on TEST and PROD environments

I have a symfony 5.3 installation with 2 security providers.
If I'm using the DEV environment I can login using users from both the providers, but when I change the environment to TEST or PROD I can only login using one of them.
If I add logging to my authenticator I see that I reach the function onAuthorizationSuccess with the right user on both cases, but if I'm on TEST or PROD, the page that I see is again the login page. If I switch between environments I can replicate the behaviour any time: dev ok, test and prod not passing from the login page for one of the providers. The other one ok in all the situations.
I understand that Symfony is working differently on dev than test or prod, but I'm not able to figure out what is the difference.
There are no errors on the logs and I have cache:clear a few times..
Is there any way I can debug this behaviour?
The behaviour changes completely if I'm on DEV or TEST/PROD environments. The test environment uses a .env.test copy of .env.dev, so it should'nt affect the result.
If I change .env to use DEV and clean cache it works: the 2 types of users can login, if I change .env to use TEST for example and clean cache again it doesn't work...
My security.yaml (relevant parts):
security:
chain_provider:
chain:
providers: [app_user_provider, sec_user_provider]
app_user_provider:
entity:
class: App\Entity\WebUser
property: login
sec_user_provider:
entity:
class: App\Entity\Security\User
property: login
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: chain_provider
guard:
authenticators:
- App\Security\BasicAuthenticator
logout:
path: app_logout

Symfony 4 - Security - Share context from multiple firewalls to one another

I think I reached some limit with the Symfony security component. Here's my problem: I have two firewall to manage two users type (with two distinct entities) authentication and access to two different part of the website. I have a third part to manage files, uploads, ... that have to be private and both users types need to access it.
So I made multiple providers in security.yml:
providers:
# used to reload user from session & other features (e.g. switch_user)
core_user_provider:
entity:
class: Akyos\CoreBundle\Entity\User
property: email
platform_user_provider:
entity:
class: App\Entity\Platform\UserPlatform
property: email
file_manager_provider:
chain:
providers: [core_user_provider, platform_user_provider]
And multiple firewalls
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
core:
pattern: ^/(app|admin)/
context: shared
provider: core_user_provider
anonymous: lazy
guard:
authenticators:
- Akyos\CoreBundle\Security\CoreBundleAuthenticator
logout:
path: app_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
file_manager:
pattern: ^/(file-manager)
context: shared
provider: file_manager_provider
anonymous: lazy
guard:
authenticators:
- App\Security\FileManagerAuthenticator
logout:
path: file_manager_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
platform:
pattern: ^/(platorm_login|plateforme)
context: shared
provider: platform_user_provider
anonymous: lazy
guard:
authenticators:
- App\Security\PlatformAuthenticator
logout:
path: platform_logout
target: 'home'
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
main:
anonymous: lazy
So a Platform user can't access Core, and a Core user can't access Platform. But both users needs to access File-manager, without re-log in. I can't place /file-manager urls under Core or Platform firewall because the other wouldn't grant access to it. So I need a third firewall to manage File-manager access. It use a chain provider that groups both Core and Platform users. It doesn't work either because if a Core user authenticate through the Core firewall it is not authenticated for the File-manager one, so it redirect to File-manager login page.. if the user logs in File-manager part it can access it, but when it turn back to Core part it has to re-connect again.
I tried several things but the closest solution is to use the context option on firewalls, so when a user is logged in through Core part it can access File-manager part without re-log because both firewalls shared the same context. That's what I want. But I also need it for the Platform firewall! So I also add same context option to it, and it works, both users types can access File-manager without log in again :D But as the three firewalls share the same context, Core users can access to Platform and vice-versa, and that breaks all the separation logic.. :'(
I need a way to tell security component "File-manager firewall has same context as Core firewall, and File-manager firewall has same context as Platform firewall, but Core and Platform firewalls doesn't share the same context". Something like this:
firewalls:
core:
context: core
file_manager:
context: [core,platform]
platform:
context: platform
main:
anonymous: lazy
I found nothing about it. Maybe it can't be done, maybe I have to create custom provider or authenticator to hack it. Maybe I can make it without Symfony, it's only php after all, so could I make the file-manager part accessible to every one (so under the main firewall) and add a Listener that would check if the request is for file-manager, find in session if there is a previous logged-in user, check if the user is a Core or a Platform user and then redirect if not... ? How can I find the previous Core or Platform user in session, when on a "main firewall" page (= authenticated as anonymous), without Symfony functions ? I'm not good enough to know how I could achieve that. Help ?
Thanks
I've finally let 3 providers and firewalls with context shared between it. To prevent Core users to access Platform, and vice-versa, I added access_control:
- { path: ^/file-manager, roles: [ROLE_PLATFORM, ROLE_CORE] }
- { path: ^/core, roles: ROLE_CORE }
- { path: ^/plateforme, roles: ROLE_PLATFORM }
so it ends with a 403 access denied error. That's not the behavior I want so I also added 'access_denied_url' option on both core and platform firewall to redirect user on the good login page. As contexts are shared, users are already logged, so on login template I check instance of user object to advice him to disconnect first before trying to access this part.
{% if instanceOf(app.user, 'App\\Entity\\PlatformUser') %}
You're already logged in Platform space, please log out before access Core space.
{% else %}
You're already logged in as {{ app.user.username }}, log out or access core panel.
{% endif %}
It's a bit confusing to have context shared between parts that shouldn't share anything but access to file-manager, but no user can access other part so.. that works.

Symfony - How to log a user on one sub domain (not all) dynamically

I have a site with multiple subdomains. I would like to log users that have certain rights to one of the sub domain only when he's already authenticated on the 'main' site. Let's say that my main domain is www.domain.com, i have sub1.domain.com, sub2.domain.com, sub3.domain.com.
When a user is authenticated on domain.com, i would like to be able to redirect him to sub2.domain.com without asking him to re-authenticate. But it should not be authenticate to sub1.domain.com or sub3.domain.com.
I have read about setting the cookie_domain in the config.xml but in this case the user will be logged for all subdomains.
Is that possible ?
Thanks !
Edit for more info
I'm working with Symfony 2.7 and i have tried both solution in the security.yml : one main shared firewall and one per sub domain (See below).
But i have not configured the session cookie_domain in config.yml to '.domain.com' as i don't want to log the user in all the subdomains.
firewalls:
main:
pattern: ^/
host: %main_domain%
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout:
path: /logout
target: /login
anonymous: true
context: main_context
sub1:
pattern: ^/
host: %sub1_domain%
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout:
path: /logout
target: /login
anonymous: true
context: main_context
sub2:
pattern: ^/
host: %sub2_domain%
....
It is possible in multiple ways. Since you log in in your main domain, you need some way to specify which user has access to which sub domain. Using different roles seems like a reasonable approach for this. So, for example a user who has access to subdomain1 will also have a role like ROLE_USER_SUB1.
With this set up, you can modify your security.yaml and use the access_control settings to restrict access to certain roles based on the domain, using additional matching options
security:
access_control:
...
- { path: ^/, roles: ROLE_USER_SUB1, host: sub1\..* }
- { path: ^/, roles: ROLE_USER_SUB2, host: sub2\..* }
You might have to tweak this to your needs and you also have to be careful to have the correct ordering of the routes, as the first matching rule will be used.
Another possible solution is to use a simple event listener that is triggered on each request at the kernel.request (be careful to check the priority, as you probably need to put your listener after the firewall listener) or kernel.controller event and then use the access decision manager or Symfony\Component\Security\Core\Security to check whether the user is (a) logged in and (b) has the correct role set, see https://symfony.com/doc/current/security/securing_services.html

how to get all users connected to default user without going through login page?

I want to secure my API with FOSUserBundle (and avoid the use of FOSOAuthServerBundle due to lack of documentation with complete example).
To make my API secure, I just created firewalls as follows:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_doc:
pattern: ^/api/doc
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
logout: true
anonymous: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
- { path: ^/admin/, roles: [ROLE_SUPER_ADMIN]}
This is great! But this requires that every one has to be connected before using the website so that I can display the list of ads, etc. ( Even GET queries needs a connected for security purpose)
To overcome, I am thinking about having all the users connected with a "default" user with limited privileges (just enough for simple querying without posting).
Is it possible to do so?
Thanks for your help.
PS: I did manage to get FOSUserBundle and FOSOAuthServerBundle working together. In fact, I was able to create a client and get an access token for it through command lines. But, I was not able to config the security.yml properly so that I get a login page that returns an access token.
Take a look at the symfony entry on How to Authenticate Users with API Keys. The entry has most of the code that you need and is well documented.
You do have to add a field for the API key in your fos_user table e.g.
ALTER TABLE fos_user ADD api_key VARCHAR(255) DEFAULT NULL;
You also need to correctly implement the ApiKeyUserProvider::getUsernameForApiKey to look up in your database for the user based on the given API key, something like this:
$repository = $this->manager->getRepository('Application\UserBundle\Entity\User');
$user = $repository->findOneBy(array('apiKey' => $apiKey));
return empty($user) ? $user : $user->getUsername();
I implemented this a few years ago, it was simple to follow and has worked great!

Resources