Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded to Symfony 6 from 5.4, can no longer log in (Error: The presented password is invalid.) #3087

Open
kcaporaso opened this issue Oct 17, 2024 · 3 comments

Comments

@kcaporaso
Copy link

kcaporaso commented Oct 17, 2024

Symfony FOSUserBundle versions: Symfony: v6.3.12, FOSUserBundle: v3.4.0

Description of the problem including expected versus actual behavior:
After upgrading Symfony 5.4 to 6.3.12 and FOSUserBundle from 3.2.1 to 3.4.0 I am unable to log into the symfony application.
I am not sure if FOSUserBundle no longer works with 6.0+ or not, perhaps I need to implement my own Authenticator?
It might be a simple oversight with the yaml file or perhaps I need to implement something?

I would expect to be able to login as usual, however I know there are quite a few changes to the Authentication system when moving from 5.4 to 6.0 so please elaborate if I missed something.

Thank you.

Provide logs (if relevant):

[2024-10-17 07:49:21] security.DEBUG: Checking for authenticator support. {"firewall_name":"main","authenticators":2} []
[2024-10-17 07:49:21] security.DEBUG: Checking support on authenticator. {"firewall_name":"main","authenticator":"Symfony\\Component\\Security\\Http\\Authenticator\\FormLoginAuthenticator"} []
[2024-10-17 07:49:21] security.DEBUG: Checking support on authenticator. {"firewall_name":"main","authenticator":"Symfony\\Component\\Security\\Http\\Authenticator\\RememberMeAuthenticator"} []
[2024-10-17 07:49:21] security.DEBUG: Authenticator does not support the request. {"firewall_name":"main","authenticator":"Symfony\\Component\\Security\\Http\\Authenticator\\RememberMeAuthenticator"} []

...
[2024-10-17 07:49:21] app.DEBUG: Notified event "Symfony\Component\Security\Http\Event\CheckPassportEvent" to listener "Symfony\Component\Security\Http\EventListener\UserProviderListener::checkPassport". {"event":"Symfony\\Component\\Security\\Http\\Event\\CheckPassportEvent","listener":"Symfony\\Component\\Security\\Http\\EventListener\\UserProviderListener::checkPassport"} []
[2024-10-17 07:49:21] app.DEBUG: Notified event "Symfony\Component\Security\Http\Event\CheckPassportEvent" to listener "Symfony\Component\Security\Http\EventListener\CsrfProtectionListener::checkPassport". {"event":"Symfony\\Component\\Security\\Http\\Event\\CheckPassportEvent","listener":"Symfony\\Component\\Security\\Http\\EventListener\\CsrfProtectionListener::checkPassport"} []
[2024-10-17 07:49:21] app.DEBUG: Notified event "Symfony\Component\Security\Http\Event\CheckPassportEvent" to listener "Symfony\Component\Security\Http\EventListener\UserCheckerListener::preCheckCredentials". {"event":"Symfony\\Component\\Security\\Http\\Event\\CheckPassportEvent","listener":"Symfony\\Component\\Security\\Http\\EventListener\\UserCheckerListener::preCheckCredentials"} []
[2024-10-17 07:49:21] app.DEBUG: Notified event "Symfony\Component\Security\Http\Event\CheckPassportEvent" to listener "Symfony\Component\Security\Http\EventListener\CheckCredentialsListener::checkPassport". {"event":"Symfony\\Component\\Security\\Http\\Event\\CheckPassportEvent","listener":"Symfony\\Component\\Security\\Http\\EventListener\\CheckCredentialsListener::checkPassport"} []
[2024-10-17 07:49:21] security.INFO: Authenticator failed. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\BadCredentialsException(code: 0): The presented password is invalid. at /private/var/www/dms-portal/digiflo/vendor/symfony/security-http/EventListener/CheckCredentialsListener.php:69)","authenticator":"Symfony\\Component\\Security\\Http\\Authenticator\\FormLoginAuthenticator"} []
[2024-10-17 07:49:21] security.DEBUG: Authentication failure, redirect triggered. {"failure_path":"/login"} []

security.yaml snippet BEFORE:

# you can read more about security in the related section of the documentation
# http://symfony.com/doc/current/book/security.html
security:
    enable_authenticator_manager: true
    # http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
    #encoders:

    # http://symfony.com/doc/current/book/security.html#hierarchical-roles
    role_hierarchy:
        ROLE_USER: [ROLE_ADMIN]

    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    #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'
        FOS\UserBundle\Model\UserInterface: sha512
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

        #in_memory:
        #    memory:
        #        users:
        #            user:  { password: userpass, roles: [ 'ROLE_USER' ] }
        #            admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }

    # the main part of the security, where you can set up firewalls
    # for specific sections of your app
    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern:  ^/(_(profiler|wdt|fragment|error)|css|images|js)/
            security: false
        main:
            lazy: true
            #provider: users_in_memory -- ./bin/console barks about this setting.

            pattern: ^/
            user_checker: fos_user.user_checker
            entry_point: app.handler.entry_point
            remember_me:
                secret: "%env(resolve:APP_SECRET)%"
                path: /iframe
            form_login:
                provider: fos_userbundle
                enable_csrf: true
                #csrf_token_generator: security.csrf.token_manager # pre2.8 form.csrf_provider
                success_handler: app.handler.login_success_handler
                use_referer: true
            logout:
                path: fos_user_security_logout

    # with these settings you can restrict or allow access for different parts
    # of your application based on roles, ip, host or methods
    # http://symfony.com/doc/current/cookbook/security/access_control.html
    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/registration, role: IS_AUTHENTICATED_ANONYMOUSLY }


security.yaml snippet AFTER:

security:
    # http://symfony.com/doc/current/book/security.html#hierarchical-roles
    role_hierarchy:
        ROLE_USER: [ROLE_ADMIN]

    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
   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'
        FOS\UserBundle\Model\UserInterface: sha512

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    # the main part of the security, where you can set up firewalls
    # for specific sections of your app
    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern:  ^/(_(profiler|wdt|fragment|error)|css|images|js)/
            security: false
        main:
            lazy: true
            #provider: users_in_memory -- ./bin/console barks about this setting.

            pattern: ^/
            user_checker: fos_user.user_checker
            entry_point: app.handler.entry_point
            remember_me:
                secret: "%env(resolve:APP_SECRET)%"
                path: /iframe
            form_login:
                provider: fos_userbundle
                enable_csrf: true
                #csrf_token_generator: security.csrf.token_manager # pre2.8 form.csrf_provider
                success_handler: app.handler.login_success_handler
                use_referer: true
            logout:
                path: fos_user_security_logout

    access_control:
        - { path: ^/login$, role: PUBLIC_ACCESS }
        - { path: ^/register, role: PUBLIC_ACCESS }
        - { path: ^/resetting, role: PUBLIC_ACCESS }
        - { path: ^/registration, role: PUBLIC_ACCESS }
@stof
Copy link
Member

stof commented Oct 17, 2024

Please show your SecurityBundle configuration both before and after the migration.

The authentication is not a feature provided by FOSUserBundle but by the SecurityBundle of Symfony.
The logs say BadCredentialsException(code: 0): The presented password is invalid., which indicates a failure when the core authenticator of Symfony validates credentials.

@kcaporaso
Copy link
Author

@stof thank you, I've added the BEFORE and AFTER now, I think you're referring to the security.yaml file. I think I mainly changed to now using PUBLIC_ACCESS vs the older IS_AUTHENTICATED_ANONYMOUSLY. Let me know if I misunderstood your request.

@kcaporaso
Copy link
Author

Got it resolved.

CheckCredentialsListener.php and MessageDigestPasswordHasher.php is what tipped me off on the solution. Got a breakpoint down in there and saw the salt was passing as null. I needed to update my ./src/Entity/User.php to implement: LegacyPasswordAuthenticatedUserInterface so that it would take the salt from the DB user.

if (!$this->hasherFactory->getPasswordHasher($user)->verify($user->getPassword(), $presentedPassword, 
    $user instanceof LegacyPasswordAuthenticatedUserInterface ? $user->getSalt() : null)) {
                throw new BadCredentialsException('The presented password is invalid.');
     
}

// hash_equals was failing with null salt because i'm a legacy system.
public function verify(string $hashedPassword, #[\SensitiveParameter] string $plainPassword, ?string $salt = null): bool
{
        if (\strlen($hashedPassword) !== $this->hashLength || str_contains($hashedPassword, '$')) {
            return false;
        }

        return !$this->isPasswordTooLong($plainPassword) && hash_equals($hashedPassword, $this->hash($plainPassword, $salt));
}

I realize that the newer user security bundle and internal symfony authenticators handle the salting, but this is a system I've migrated and maintained for about 10 years now. Started in symfony 2.x and am one step away from 7.x now.

Thanks for always being here, @stof , have admired yours and all the symfony greats for many years now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants