FOSUserBundle on Symfony 7 and 8: what replaced it, piece by piece (2026)
FOSUserBundle still installs on Symfony 7.4, not on Symfony 8, and its own maintainers tell new projects not to use it. Here is what replaces each of its features, and how to move an existing users table without resetting a single password.
Short answer: FOSUserBundle installs on Symfony 7.3 and 7.4, not on Symfony 8, and its maintainers say new projects should not use it. Every feature it gave you now exists in Symfony itself or in a small, maintained bundle. The migration is mostly deleting code, with one trap: the password hashes already in your database.
Everything below was checked on Packagist and GitHub on September 25, 2026. Versions and dates are written down so you can re-check them. Disclosure: I sell ShipAnvil, a Symfony SaaS kit whose user system is described at the end. Read accordingly.
Where FOSUserBundle stands in 2026
- Latest release:
friendsofsymfony/user-bundlev4.1.0, February 13, 2026. It requiressymfony/framework-bundle ^7.3and PHP^8.2. The 4.1 changelog is maintenance: XML config converted, deprecations fixed, support for Symfony below 7.3 removed. - Symfony 8 is not supported. The constraint
^7.3excludes 8.0, and the pull request "Add support for Symfony 8" (#3090) has been open since December 11, 2025. If you plan to leave the 7.4 LTS one day, FOSUserBundle is on the critical path. - The README says it in bold: "New projects should not use this bundle." The package "only receives minimal maintenance to allow existing projects to upgrade. Existing projects are expected to plan a migration away from this bundle."
- It still has a large installed base, which is why it keeps getting those maintenance releases. That is a reason it will keep installing on 7.4, not a reason to start with it.
So the question is no longer "is FOSUserBundle dead?". It is "what do I replace each piece with?". The README answers most of it itself.
What FOSUserBundle did, and what replaces it
- Base
Usermodel: your own Doctrine entity implementingUserInterfaceandPasswordAuthenticatedUserInterface(make:user). - User provider: Symfony's built-in
entityprovider. - Login form: Symfony's
form_login(make:security:form-login). - Registration: a plain controller and form (
make:registration-form). - Email confirmation:
symfonycasts/verify-email-bundle(v1.18.0, Symfony 5.4 to 8). - Password reset ("resetting"):
symfonycasts/reset-password-bundle(v1.25.0, Symfony 5.4 to 8). - Change password: a form with Symfony's
UserPasswordconstraint. - Profile page: your own controller; it was always app-specific.
- The
enabledflag: aUserCheckeron the firewall.
All three make: commands ship with symfony/maker-bundle (v1.68.0,
September 12, 2026). Both SymfonyCasts bundles declare Symfony 8 support in
their composer.json.
What you gain is not only Symfony 8. The replacements are safer by default:
verify-email-bundle confirms addresses with signed URLs instead of a
confirmationToken column, and reset-password-bundle stores a
selector and a hashed token in its own table, with throttling of
repeated requests, instead of a plain token on the user row.
What about a drop-in fork?
nucleos/user-bundle is a fork of FOSUserBundle. Version 4.1.0 (April
9, 2026) requires Symfony ^7.4 || ^8.0, so it does install on Symfony 8.
But it is not a drop-in: it deliberately dropped registration and profile
management (those moved to a separate nucleos/profile-bundle), and its
README still says "Only symfony 4.4 / 5.x support", which its current
composer.json contradicts. Useful if you want to keep a bundle-shaped user
model; registration means a second bundle or your own controller anyway.
sonata-project/user-bundle (5.19.0, December 2025) makes sense only
if you already run Sonata Admin: it pulls symfony/security-acl and is
built around that admin.
For a new project, neither beats an entity you own plus the two SymfonyCasts bundles.
Migrating an existing users table
The code side is quick: generate or write the new entity, map it to your existing table, delete the FOS configuration, routes and templates, and wire the two SymfonyCasts bundles. Four details decide whether anyone notices.
1. Keep the old password hashes working. Old FOSUserBundle setups often
hashed with a salted legacy algorithm (the 1.x documentation used
sha512), and the salt column is still filled. Do not force a reset.
Declare the old hasher and let Symfony migrate on the next login:
# config/packages/security.yaml
security:
password_hashers:
legacy:
algorithm: sha512 # whatever your FOS config used
App\Entity\User:
algorithm: auto
migrate_from: [legacy]
The entity implements LegacyPasswordAuthenticatedUserInterface as long as
it still has salted hashes (getSalt() returns the column), and the
repository implements PasswordUpgraderInterface: on every successful
login, Symfony rehashes with the current algorithm and calls
upgradePassword(). When no salt is left, drop the column.
2. Replace the canonical columns. FOS kept usernameCanonical and
emailCanonical to make lookups case-insensitive. Lowercase the email on
write, put a unique constraint on it, lowercase the lookup, and drop the
canonical columns after one migration that copies them.
3. Move enabled into a UserChecker. Register it with user_checker:
on the firewall and throw an account status exception for disabled users.
One subtlety: the checker runs when someone signs in, not when a user is
reloaded from an existing session. If disabling an account must end open
sessions too, re-check on each request.
4. Recreate lastLogin if you used it. Listen to LoginSuccessEvent
and set the date yourself.
Test the four with functional tests before switching production: log in with an old hash, check it was rehashed, log in with a disabled account, reset a password end to end.
If you are starting a new SaaS instead
Then there is nothing to migrate, and the real work is everything around the user: email verification that actually blocks sign-in, rate limits on every endpoint that sends an email, sessions that end when an account is disabled, 2FA, and the organization the user belongs to.
ShipAnvil is not a bundle: it is a complete Symfony 7.4 application you start your SaaS from, and its user system is plain application code built on exactly the replacements above. What it contains:
- A
Userentity you own, email as identifier (lowercased, unique), with the repository implementingPasswordUpgraderInterface. - Registration with mandatory email verification
(
verify-email-bundle): aUserCheckerrefuses sign-in until the address is confirmed, and resending the link never reveals whether an account exists. - Password reset with
reset-password-bundle, form login with remember-me and login throttling, and passwordless magic links on Symfony'slogin_link. - Rate limits on sign-up, magic-link, verification and reset requests.
- Open sessions re-checked on every request against the same
UserChecker, so an account that stops passing it is logged out on its next request, remember-me cookie included. - TOTP two-factor authentication with single-use backup codes (how it is built).
- Every user gets an organization at sign-up, with owner, admin and member roles and email invitations, and tenant-scoped queries through a Doctrine filter.
- Functional tests for login, registration, reset, magic links, 2FA, account status and rate limits, shipped with the code.
It does not include social login (OAuth): add
knpuniversity/oauth2-client-bundle or hwi/oauth-bundle if you need it.
The live demo lets you sign in and look around. Price and license terms are on the pricing page. If you are comparing whole kits, the Symfony SaaS boilerplate comparison checks every living option the same way.