scheb / two-factor-bundle

[ABANDONED] Two-factor authentication for Symfony 2 & 3 applications 🔐. Please use the newer versions from https://github.com/scheb/2fa.
https://github.com/scheb/2fa
MIT License
385 stars 111 forks source link

Login doesnt redirect to 2fa, but everything else works #276

Closed mike240se closed 4 years ago

mike240se commented 4 years ago

Bundle version: 4.0.14 Symfony version: 3.4.35

I installed everyhing and setup email support and when i login it redirects back to my apps home page. The token is set for your bundle and the user is partially logged in (it shows username in the dev bar). I cant get to any secure part of the site. If I visit mysite.com/2fa it will show me the 2fa and i can use it and it all works.

So the only issue is that while logging in it redirects to the wrong place:

    secured_area:
        two_factor:
            auth_form_path: 2fa_login    # The route name you have used in the routes.yaml
            check_path: 2fa_login_check  # The route name you have used in the routes.yaml
        pattern:    ^/
        form_login: ~
        logout: true
        anonymous: ~
        logout_on_user_change: true
        user_checker: adminbundle.userchecker

i am using the default routes you provide.

scheb commented 4 years ago

Here in the troubleshooting guide https://github.com/scheb/two-factor-bundle/blob/master/Resources/doc/troubleshooting.md it says:

The page you've seen after login doesn't require a fully authenticated user. Most likely that path is accessible to IS_AUTHENTICATED_ANONYMOUSLY via your security access_control configuration. Either change your access_control configuration or after login force-redirect to user to a page that requires full authentication.

I believe that's the problem.

mike240se commented 4 years ago

Here in the troubleshooting guide https://github.com/scheb/two-factor-bundle/blob/master/Resources/doc/troubleshooting.md it says:

The page you've seen after login doesn't require a fully authenticated user. Most likely that path is accessible to IS_AUTHENTICATED_ANONYMOUSLY via your security access_control configuration. Either change your access_control configuration or after login force-redirect to user to a page that requires full authentication.

I believe that's the problem.

i'm so sorry i forgot to include i already went through the trouble shooting guide (and read every relevant closed issue) and didnt find a fix.

its not that, if i try to access authenticated part of the site after logging in, i get the login prompt again. if i login again, again i am redirected to the start page of the app.

mike240se commented 4 years ago

I think I found the issue. the install docs for e-mail say to use:

security:
    firewalls:
        main:
            two_factor:
                auth_form_path: 2fa_login    # The route name you have used in the routes.yaml
                check_path: 2fa_login_check  # The route name you have used in the routes.yaml

but when i check the config reference it shows as:

  auth_form_path: /2fa                  # Path or route name of the two-factor form
                check_path: /2fa_check                # Path or route name of the two-factor code check

When i changed from 2fa_login (route name) to /2fa (path) it started working as expected....

I pasted this in my app/config/routing.yml:

2fa_login:
    path: /2fa
    defaults:
        _controller: "scheb_two_factor.form_controller:form"

2fa_login_check:
    path: /2fa_check

EDIT: I am not sure why but it worked once after i made this changed and now does the same as before. back to square one.

scheb commented 4 years ago

If you're using a path or the route name in the configuration shouldn't make a difference. The route name is definitely the cleaner solution.

Would you please show me your security.yaml configuration, all of it.

mike240se commented 4 years ago

Here it is:

security:

    encoders:
       # Symfony\Component\Security\Core\User\User: plaintext
       AdminBundle\Entity\User:
          algorithm: bcrypt

    role_hierarchy:
        ROLE_SUPPORT:     [ROLE_USER]
        ROLE_ADMIN:       [ROLE_USER, ROLE_SUPPORT]
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
    providers:
        administrators:
                    entity: { class: AdminBundle:User, property: username }
    #    users:
     #               entity: { class: PrivateBundle:User, property: username }

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        #main:
        #    anonymous: ~

            #        login:
            #           two_factor:
            #   auth_form_path: 2fa_login    # The route name you have used in the routes.yaml
            #   check_path: 2fa_login_check  # The route name you have used in the routes.yaml
            #attern:  ^/admin/login$
            #ecurity: false
            #nonymous: ~

        secured_area:
            two_factor:
                auth_form_path: /2fa    # The route name you have used in the routes.yaml
                check_path: /2fa_check  # The route name you have used in the routes.yaml
            pattern:    ^/
            form_login: ~
            logout: true
            anonymous: ~
            logout_on_user_change: true
            user_checker: adminbundle.userchecker

    access_control:
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY , requires_channel: https } #, requires_channel: https,  host: example\.org$
        - { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        #- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: http, host: localhost$ }
        - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } #, requires_channel: https
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY }
        #- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: http, host: localhost$ }
        - { path: ^/admin/, roles: ROLE_ADMIN }  # , requires_channel: https
        - { path: ^/private/, roles: ROLE_SUPPORT }
        - { path: ^/user/, roles: ROLE_USER }
         # This makes the logout route available during two-factor authentication, allows the user to cancel

        # This ensures that the form can only be accessed when two-factor authentication is in progress

you can see at one point i had 2 firewalls, a seperate for the login, i combined them as your bundle requires yet still it doesnt work.

if i visit any secure part of the site i get redirected back to the login path or the app start path (unsecure area)

scheb commented 4 years ago

This looks odd to me:

    access_control:
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY , requires_channel: https } #, requires_channel: https,  host: example\.org$

Having this as your first rule in the access_contol list makes this the first rule that is checked (access_contol is check in order). The pattern path: ^/ matches any path, so this rule always matches and all the following rules are impossible to be reached.

I'd recommend to move this rule to the very bottom of the list, so all the more specific rules are checked first and this is only the last rule to be applied.

Let's see if that changes anything for you.

Note: I replaced your hostname with example.org, not everyone on Github needs to know the URL to your website ;)

mike240se commented 4 years ago

This looks odd to me:

    access_control:
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY , requires_channel: https } #, requires_channel: https,  host: example\.org$

Having this as your first rule in the access_contol list makes this the first rule that is checked (access_contol is check in order). The pattern path: ^/ matches any path, so this rule always matches and all the following rules are impossible to be reached.

I'd recommend to move this rule to the very bottom of the list, so all the more specific rules are checked first and this is only the last rule to be applied.

Let's see if that changes anything for you.

Note: I replaced your hostname with example.org, not everyone on Github needs to know the URL to your website ;)

Thanks you were too quick, i edited it when i noticed but you had already replied! Thanks i appreciate it.

I have that rule so that people can visit the start page of the app without being logged in path / -- this is how i have been doing it for years, is it bad practice the way i am handling it?

I will move the rule to the bottom and try, i did experiment with moving the rules around as i saw in other closed issues a lot of people you helped had issues with the order of the access rules. I am not sure if i tried moving it to the bottom though, trying that now.

mike240se commented 4 years ago

I changed it and it still does the same thing. I login, it redirects me to path / but I am partially authenticated and have the 2factortoken, if i go to /2fa i get the e-mail and can then login.

TwoFactorToken {#248 ▼ -authenticatedToken: UsernamePasswordToken {#249 …} -credentials: null -providerKey: "secured_area" -attributes: [] -twoFactorProviders: [▶] }

Roles: None

mike240se commented 4 years ago

It looks like the 2fa partially authenticated role is not being assigned?

scheb commented 4 years ago

I have that rule so that people can visit the start page of the app without being logged in path / -- this is how i have been doing it for years, is it bad practice the way i am handling it? Nothing wrong with that approach. It's just that the order of access control rules matters and having this rule first will make all following rules obsolete. So you should change the order.

Side note: I believe, you actually don't need it at all, because every path that doesn't match a access control rule is accessible anyways. My little test app doesn't have such a rule and I can still access any path that isn't explicitly protected => https://github.com/scheb/two-factor-app/blob/master/config/packages/security.yaml

It looks like the 2fa partially authenticated role is not being assigned? It's not a role, it's an attribute (similar to IS_AUTHENTICATED_ANONYMOUSLY) that is internally evaluated. It doesn't need to present on the user entity.

So let's proceed:

Now that you moved the rule to the bottom (you did, right?), can you try to access a page that requires you to be logged in, such as /user/. Does that redirect you to the 2fa form?

mike240se commented 4 years ago

I have that rule so that people can visit the start page of the app without being logged in path / -- this is how i have been doing it for years, is it bad practice the way i am handling it? Nothing wrong with that approach. It's just that the order of access control rules matters and having this rule first will make all following rules obsolete. So you should change the order.

Side note: I believe, you actually don't need it at all, because every path that doesn't match a access control rule is accessible anyways. My little test app doesn't have such a rule and I can still access any path that isn't explicitly protected => https://github.com/scheb/two-factor-app/blob/master/config/packages/security.yaml

It looks like the 2fa partially authenticated role is not being assigned? It's not a role, it's an attribute (similar to IS_AUTHENTICATED_ANONYMOUSLY) that is internally evaluated. It doesn't need to present on the user entity.

So let's proceed:

Now that you moved the rule to the bottom (you did, right?), can you try to access a page that requires you to be logged in, such as /user/. Does that redirect you to the 2fa form?

YES, it works! If i try to access /admin after logging in and getting redirecfted to / it sends me to /2fa

Should i remove that rule entirely now? (its at the bottom now)

Also should clarify i am still getting redirected to / after logging in, its just that now if i go to /admin it loads /2fa for me.

mike240se commented 4 years ago

Great news, anticipating your next message being to remove that access control rule entirely, i deleted it and now everyhing is working perfect! Thank you!

mike240se commented 4 years ago

Instead of opening another question or adding to a closed one, i had a question about resending the e-mail code. SOmeone requested it and you said its easy to just resend it ourselves, which it is but i am wondering what role should the user have at that point and could i use it to secure the controller method?

scheb commented 4 years ago

If the user is in the middle of two-factor authentication, it has the IS_AUTHENTICATED_2FA_IN_PROGRESS attribute, so I'd use that one.

mike240se commented 4 years ago

If the user is in the middle of two-factor authentication, it has the IS_AUTHENTICATED_2FA_IN_PROGRESS attribute, so I'd use that one.

Thanks!

mike240se commented 4 years ago

If the user is in the middle of two-factor authentication, it has the IS_AUTHENTICATED_2FA_IN_PROGRESS attribute, so I'd use that one.

So i added a button to the 2FA form that says "resend e-mail with code" and that fires a simple controller method that just sends an email. But it doesnt work..... /resend2FA/{userid} is the path and its not in a protected area of the site. I originally had it protected with IS_AUTHENTICATED_2FA_IN_PROGRESS but even after removing that completely, when i try to call that method it just redirects to /2FA

so my question is, once in the middle of 2fa verification, does your bundle then redirect any other traffic to /2fa?

It sure seems like this is related to my first issue but i dont see how at this point, i did not change my security and /resend2FA/{userid} should not be protected.

mike240se commented 4 years ago

So i had to add to the top of my security.yml access control

I am not sure why. /resend2FA does not fall under any protected routes, i would expect it to work just like the other public parts of my site. not sure what i am missing here.......

scheb commented 4 years ago

Well, I don't know either. Works for me in my little test application.

mike240se commented 4 years ago

I am running into a new problem today, getting lots of access denied while testing, would this be related to 2FA you think (or the changes i made to security.yml?) I want to make sure i am not chasing my tail looking for a red herring:

[2020-03-25 15:04:39] security.DEBUG: Read existing security token from the session. {"key":"_security_secured_area","token_class":"Symfony\\Component\\Security\\Core\\Authentication\\Token\\Usern amePasswordToken"} [] [2020-03-25 15:04:39] security.DEBUG: User was reloaded from a user provider. {"provider":"Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider","username":"testuser"} [] **[2020-03-25 15:04:39] security.DEBUG: Access denied, the user is neither anonymous, nor remember-me. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException(co de: 403): Access Denied. at /var/www/sncirs/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:68)"} []** [2020-03-25 15:04:39] request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException: "Access Denied." at /var/www/sncirs/vendor/symfony/symfony/src/Symfony /Component/Security/Http/Firewall/ExceptionListener.php line 128 {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException(code: 0): Access Denied. at /var/www/s ncirs/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php:128, Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException(code: 403): Access Denied. at /var/www/sncirs/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:68)"} [] [2020-03-25 15:04:39] security.DEBUG: Stored the security token in the session. {"key":"_security_secured_area"} []

scheb commented 4 years ago

Looks like something in your access_control configuration says you shouldn't have access to this path.

=> Find out which rule matches the path (remember: they're checked in order) and then check if your current user (looks like the username is "testuser") has the required roles.

mike240se commented 4 years ago

Thanks Scheb, probably my access control was always a bit off and now that i added 2FA bundle its more obvious :)

mike240se commented 4 years ago

Yeah you were right (again). The issue is that in addition to that ^/ access rule we deleted previously, i also had in there these `- { path: ^/admin/, roles: ROLE_ADMIN } # , requires_channel: https

and for routing i had this:

`private: resource: "@PrivateBundle/Controller/" type: annotation prefix: /private

public: resource: "@PublicBundle/Controller/" type: annotation prefix: /

admin: resource: "@AdminBundle/Controller/" type: annotation prefix: /admin`

but then, i had some methods in AdminBundle (under /admin and requring ROLE_ADMIN) that i gave permission to non-admin users. I use frame work extra bundle @Security("has_role('ROLE_ADMIN')") to protect all my methods. so i had changed from has_role(admin) to has_role(support-lower role).

For whatever reason this worked up until now and either removing that rule i didnt need ^/ OR updating symfony version 3.4.21 to latest cause it to stop allowing that to happen.

unless i am missing something, I am thinking since I already protect every method individually i can just remove these lines

`- { path: ^/admin/, roles: ROLE_ADMIN } ,

Would you mind recommending how you would fix it? do you typically protect areas with access control or use the per method has_role?

I want to try to get back to best practices after so many years of bad habits and carrying over old stuff from early symfony2 days.

thanks again

EDIT: I am thinking the problem with removing those rules is if I accidentally foget to protect a method i now have a security hole..... having those access control rules for areas of the site were kind of like blanket catch alls. I am thinking instead of removing them, I should change them to require the lowest role level that the methods in the area allow. so reduce role_admin to role_user.

scheb commented 4 years ago

EDIT: I am thinking the problem with removing those rules is if I accidentally foget to protect a method i now have a security hole..... having those access control rules for areas of the site were kind of like blanket catch alls. I am thinking instead of removing them, I should change them to require the lowest role level that the methods in the area allow. so reduce role_admin to role_user.

This sounds reasonable. To have access_control apply the minimum required role for that area, but do the fine-grained control on the per action level. But what do I know, I also haven't built a real Symfony application in the last 5 years :D

mike240se commented 4 years ago

got everything working great now, thanks much for your help!