webstack/user-bundle
A user management bundle for PHP applications, providing user entities, authentication, registration, and profile handling to speed up common account features.
Check the stack first. webstack/user-bundle is a Symfony bundle from WebstackNL, not a Laravel package. If you are working in a Laravel app, there is no day-to-day here — stop reading and reach for Breeze/Fortify plus a users migration. The value to a Laravel developer is as a reference implementation for entity design and Doctrine-style user provisioning, not as a dependency. The rest of this assumes a Symfony host app.
Minimal install path (verify before you commit):
composer show webstacknl/user-bundle --all or read the name field in the repo's composer.json. Package directory names and Composer names frequently drift, and with a single star you cannot assume a Packagist listing resolves.composer require webstacknl/user-bundle (or the VCS path: composer require webstacknl/user-bundle:dev-main with a repositories entry, since archived repos often have stale tags).config/bundles.php unless Flex gave you a recipe (unlikely at this adoption level — expect to do it by hand).bin/console debug:config <alias> to see the actual config tree. The alias is defined in the bundle's DependencyInjection/Configuration.php; that file is the single source of truth and should be your first read, before the README.bin/console make:migration && bin/console doctrine:migrations:migrate.security.yaml at the bundle's user class as your provider, and verify with bin/console debug:container --tag=security.user_providers.First use case: replace a hand-rolled user table with a Doctrine-managed user + repository that satisfies Symfony's UserInterface / PasswordAuthenticatedUserInterface, so security.yaml can authenticate against it without custom code. Do this in a scratch branch on a throwaway app before touching anything real — you are evaluating, not adopting.
Where to look first, in order: composer.json (name, supported Symfony versions, require constraints), DependencyInjection/Configuration.php, the base Model/ or Entity/ directory, then Resources/config/ for service definitions and route imports.
Entity inheritance, not modification. Never edit files under vendor/. Extend the bundle's mapped superclass in src/Entity/User.php, add your own fields, and let the bundle own the auth-relevant columns. Map the inheritance with Doctrine's MappedSuperclass (already done if the bundle is structured conventionally) so no migration conflict arises on upgrade.
Repository access. Inject the bundle's user repository by interface, not by concrete class, and resolve users through it rather than through raw DQL — the bundle likely scopes queries (enabled/disabled, soft-delete) in a way you will silently bypass otherwise. Wrap it in your own UserManager service if you need transaction boundaries around create + profile + role assignment.
Service decoration over forking. If you need to change registration behaviour, decorate the relevant service:
services:
App\Service\DecoratingUserManager:
decorates: webstack_user.manager # confirm ID with debug:container
arguments: ['@.inner']
Same for password hashing and mailers. bin/console debug:container | grep -i user is how you find the IDs — do not guess them from the README.
Event subscribers for hooks. Symfony user bundles tend to dispatch lifecycle events (registered, password reset requested, profile updated). Subscribe to them for side effects (welcome mail, audit log, queue dispatch) instead of overriding controllers. If no events exist, that absence tells you a lot about the bundle's maturity — plan for decoration everywhere.
Overriding routes and templates. Import the bundle's routing with a prefix in config/routes.yaml, then take over individual paths by declaring your own route with the same name (later definitions win in Symfony's route collection — verify with debug:router). Templates go in templates/bundles/<BundleName>/. Controllers can be replaced via controller service aliasing.
Forms. Extend the bundle's registration/profile form types with getParent() and add your fields. Register the child type as a service and alias it over the parent, so bundle-internal code that requests the parent gets yours. Keep validation in validation.yaml, not in the form, so it survives a controller swap.
Password handling. Use UserPasswordHasherInterface and the configured encoder in security.yaml. If you find a md5/sha1 anywhere in the bundle's code or fixtures, treat that as a hard stop — a 2024 bundle with a single star may still carry legacy baggage.
Testing. Boot a minimal test kernel with the bundle enabled and use WebTestCase + Doctrine fixtures; do not rely on the bundle shipping its own tests. Add a smoke test that asserts your security.yaml firewall accepts a real encoded password, because that wiring is the piece most likely to break on a host-app upgrade.
It is archived. No security patches, no Symfony 7 compatibility work, no PR review. Pin the exact version in composer.json, and if you adopt it, plan from day one to vendor the source into src/ or fork it. An auth bundle with no maintainer is a supply-chain liability, not just a stale dependency.
Adoption signal is essentially zero. One star, zero dependents, and a single release. Assume there are no battle-tested edge cases: email case-sensitivity on lookup, concurrent registration races on a unique index, token expiry handling, and locale-aware normalization are all likely untested. Read every source file before it touches production — this is a few hours, not a few minutes.
Verify Symfony version constraints. A March 2024 release targets Symfony 6.x at best. Check composer.json's require block for the actual symfony/* ranges; blindly installing into Symfony 7 will "succeed" via loose constraints and then fail at runtime on removed interfaces (getUsername(), PasswordAuthenticatedUserInterface migrations, TokenInterface changes). Run the full test suite and click through login/register/reset manually.
Config defaults are traps. Bundles of this size usually default to a %kernel.project_dir%/src/Entity/User.php path or a fixed table name. Run debug:config and diff it against what you expect, especially the entity class, table name, and firewall name. A wrong firewall name fails silently — you get a provider that never matches and a login form that just re-renders.
Duplicate-user errors surface late. Unique constraints on email/username are usually enforced only at the DB level. Catch UniqueConstraintViolationException in your registration handler rather than trusting a pre-check, which races.
Debugging cheatsheet:
bin/console debug:config <alias> — resolved config after your overridesbin/console debug:container --parameter=<name> — defaultsbin/console debug:router — which controller actually won your overridebin/console lint:container — catches decoration/argument mistakes before runtimebin/console doctrine:schema:validate — after extending the entityExtension points, ranked by safety: (1) events/subscribers, (2) service decoration, (3) route/template overrides, (4) form type inheritance, (5) forking. Anything beyond (4) with an archived bundle means you now own that code — budget accordingly.
Migration path. Keep the bundle behind a narrow seam: your own User entity, your own UserManager, your own controllers. If the only thing the bundle supplies is a base entity and a repository, replace it with ~150 lines of your own code and delete the dependency. That is almost certainly the right call here — the bundle's whole surface is smaller than the risk it carries, and an archived auth bundle is the one dependency you cannot afford to have rotting in composer.lock.
How can I help you explore Laravel packages today?