Upgraded to Symfony 6 from 5.4, can no longer log in (Error: The presented password is invalid.)
kcaporaso opened this issue · 3 comments
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 }
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.
@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.
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.