scheb / 2fa

Two-factor authentication for Symfony applications 🔐
MIT License
495 stars 72 forks source link

2FA not detected/working on my project #225

Closed nobodyjoh closed 5 months ago

nobodyjoh commented 5 months ago

Bundle version: 6.1 Symfony version: 5.4 PHP version: 8

Hello,

I wish to add two-factor authentication to my project and I came across your solution which seems great to me. Initially, the installation seemed straightforward, but I must be missing something because my authentication still works with email and password as usual.

I follow the installation guide and this is a part of my security.yaml :

main:
          lazy: true
          provider: app_user_provider
          remember_me:
              secret:   '%kernel.secret%'
              token_provider: 'Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider'
              lifetime: 68400
          form_login:
              # "login" is the name of the login route
              login_path: login
              check_path: login
              enable_csrf: true
          logout:
              path: logout
              # where to redirect after logout
              target: 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

My SecurityController look like this :

#[Route('/login', name: 'login')]
    public function index(AuthenticationUtils $authenticationUtils): Response
    {
        if($this->isGranted('ROLE1') || $this->isGranted('ROLE2') ){
            return $this->redirectToRoute('home_master_user');
        }
       elseif($this->isGranted('ROLE3') && $this->isGranted('ROLE')) {
            return $this->redirectToRoute('home');
        }
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('login/index.html.twig', [
            'last_username' => $lastUsername,
            'error' => $error
        ]);
    }

To be honest, I don't understand why it's not working even after following the various tutorials I've read.

Regards

scheb commented 5 months ago

Follow the troubleshooting guide: https://symfony.com/bundles/SchebTwoFactorBundle/6.x/troubleshooting.html#two-factor-authentication-form-is-not-shown-after-login

nobodyjoh commented 5 months ago

I already check but i don't find my problem. I try to update the security.yaml and to put this (add default_target_path: 2fa_login in form_login) :

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        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
            ...
            form_login:
                # "login" is the name of the login route
                login_path: login
                check_path: login
                default_target_path: 2fa_login
                enable_csrf: true

I am correctly redirected to the /2fa page but end up with an 'access denied' error. I think I'm missing something to instruct the login to go to the /2fa page with the IS_AUTHENTICATED_2FA_IN_PROGRESS rights, but I thought it was managed by the module.

Sorry i'm a begginer with symfony and I might be overlooking something trivial to you.

scheb commented 5 months ago

Please provide your full security.yaml so we can properly assess the situation.

nobodyjoh commented 5 months ago

yes no problem:

security:
    enable_authenticator_manager: true
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        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
            lazy: true
            provider: app_user_provider
            remember_me:
                secret:   '%kernel.secret%'
                token_provider: 'Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider'
                lifetime: 68400
            form_login:
                # "login" is the name of the login route
                login_path: login
                check_path: login
                enable_csrf: true
            logout:
                path: logout
                # where to redirect after logout
                target: login
            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#the-firewall

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true

    role_hierarchy:
        ROLE_MASTER: [ROLE_MASTER2,ROLE_MASTER, ROLE_ABONNE, ROLE_ALLOWED_TO_SWITCH]
        ROLE_MASTER2 : [ROLE_MASTER2,ROLE_ADMIN_OF, ROLE_ABONNE, ROLE_ALLOWED_TO_SWITCH]
        ROLE_ADMIN_OF: [ROLE_ADMIN_CENTRE]
        ROLE_ADMIN_CENTRE: [ROLE_INSTRUCTEUR, ROLE_CANDIDAT]
        ROLE_INSTRUCTEUR : [ROLE_INSTRUCTEUR, ROLE_USER]
        ROLE_CANDIDAT : [ROLE_CANDIDAT,  ROLE_USER]
        ROLE_CLASSIC : ROLE_CLASSIC
        ROLE_ZEN : [ROLE_CLASSIC,ROLE_ZEN,ROLE_ALLOWED_TO_SWITCH]
        ROLE_ABONNE : ROLE_ABONNE
    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        #- { path: ^/dronEdifice, roles: ROLE_MASTER }
        # This makes the logout route accessible during two-factor authentication. Allows the user to
        # cancel two-factor authentication, if they need to.
        - { path: ^/logout, role: PUBLIC_ACCESS }
        # This ensures that the form can only be accessed when two-factor authentication is in progress.
        - { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        - { path: ^/centre/espace-candidat, roles: ROLE_CANDIDAT }
        - { path: ^/centre/candidat/mes-convocations, roles: ROLE_CANDIDAT }
        - { path: ^/mot-de-passe-oublie, roles: PUBLIC_ACCESS }
        - { path: ^/api/annuaire-candidats-certifies, roles: PUBLIC_ACCESS }

        - { path: ^/centre, roles: ROLE_INSTRUCTEUR }
scheb commented 5 months ago

Can't see anything obvious wrong. You should check if you still have a TwoFactorToken security token when the 2fa page is requests and drill down where this "access denied" error is caused.

nobodyjoh commented 5 months ago

I look at Session storage on my chrome debugger and i don't have any token when i'm on /2fa. It seems like the 2FA form is never actually called; I always go through my usual form_login.

nobodyjoh commented 5 months ago

I found the solution after a lot of search, the problem was in the security.yaml :

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
                provider: app_user_provider
                enable_csrf: true

I put the provider in the two factor and now it works

scheb commented 5 months ago

Glad that you found the issue. I wouldn't have guessed that either 🤷