09/02/2026

Leaving FOSUserBundle: what replaces each piece

FOSUserBundle's own README is the clearest statement of where it stands: it receives minimal maintenance so existing projects can upgrade, existing projects are expected to plan a migration away, and new projects should not use it.

That is not a bundle that went bad. It is a bundle whose job the framework absorbed. When it was written, Symfony gave you a firewall and left registration, password resets, email confirmation and user management as an exercise. Most of that is now either in the framework or in a bundle that does one thing.

What replaces what

FOSUserBundle gave you Now
A User entity and base class make:user, generating an entity you own
A user provider Symfony's EntityUserProvider, configured in security.yaml
Password encoding The PasswordHasher component, and UserPasswordHasherInterface
Registration form A normal Symfony form; make:registration-form scaffolds it
Email confirmation SymfonyCasts VerifyEmailBundle
Password reset SymfonyCasts ResetPasswordBundle
Change password A form and one call to the hasher
fos:user:* console commands Commands you write, if you still want them

The through-line is that you end up owning the User entity rather than inheriting from one. That is the actual benefit and the actual cost: no more fighting a base class over a field you wanted shaped differently, and no more getting four features for free.

The user provider is most of the configuration

# config/packages/security.yaml
security:
    password_hashers:
        App\Entity\User: 'auto'

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        main:
            lazy: true
            provider: app_user_provider

property: email is the field the identifier is looked up on, and it is the same value your entity's getUserIdentifier() should return. If those two disagree, authentication succeeds and then behaves strangely, which is an unpleasant afternoon.

The old password hashes still work

This is the part people expect to be hard and is not. password_hashers set to auto uses the best hasher available and verifies against whatever algorithm each stored hash actually used, so accounts created under FOSUserBundle keep working after the bundle is gone.

Symfony can also upgrade them in place as people sign in, if the user class and provider implement PasswordUpgraderInterface — which make:user generates for you. Old hashes get replaced with current ones one successful login at a time, and nobody is asked to reset anything.

What to delete, and in what order

  1. Generate the replacement pieces first, while the bundle is still installed: make:user against your existing table, then the registration and reset flows.
  2. Point security.yaml at the new provider and hasher.
  3. Replace the bundle's routes and templates with your own.
  4. Remove the bundle last, and only once nothing references FOS\UserBundle anywhere.

Doing it in that order means the application authenticates throughout. The version where the bundle comes out first has a window where nobody can log in, including you.

One thing not to carry across

If your migration notes include re-authenticating somebody from a value read out of a cookie, leave it behind. Encryption is not authentication, and a cookie that says who you are without proving it is an authentication bypass regardless of how it is encoded. Symfony's remember-me handles the legitimate version of that requirement, signed, and it is one line of firewall configuration.