smartassert/users-security-bundle
Symfony bundle for adding user security to your application, offering a foundation for authentication, user management, and related security services. It provides reusable components and integration points to implement protected user flows with less boilerplate and configuration.
This bundle wires the smartassert user/security stack into Symfony: it gives you a Doctrine-mapped User entity, password hashing/verification via symfony/security-bundle, and user lookup services you can drop into controllers and auth flows. It is small, actively released, and has essentially no downstream adoption, so treat the source on GitHub as the real documentation.
Version 12.0 (current) is a Symfony 8.0 compatibility release only — no new features, no new APIs. The only change in the changelog is "Use symfony 8.0", which means the major bump exists solely to move the framework constraint. Practically:
| Bundle version | Symfony constraint | PHP |
|---|---|---|
| 12.x | Symfony 8.0 | 8.4+ (Symfony 8 floor) |
| 11.1.x | Symfony 6/7 | 8.1+ |
Laravel note up front: this is a Symfony bundle and will not run inside Laravel. If you're a Laravel developer, you consume the framework-agnostic smartassert/user-service / smartassert/security-service packages directly and wire them into your own service provider — none of the bundle registration, security.yaml, or Doctrine bundle mapping below applies. Everything from here on assumes Symfony.
Minimal steps:
composer require smartassert/users-security-bundle
config/bundles.php (Symfony Flex usually handles this automatically).doctrine/doctrine-bundle, doctrine/orm, symfony/security-bundle, and symfony/password-hasher are installed — the bundle won't work without them.DATABASE_URL and generate/apply the schema:
php bin/console doctrine:schema:update --force
# or, if the bundle ships mapping metadata:
php bin/console doctrine:migrations:diff && php bin/console doctrine:migrations:migrate
SmartAssert\UsersSecurityBundle\Services\UserFactory or the underlying smartassert/user-service factory) to create a User with an email + raw password, then verify the stored password hash and that the user is retrievable by email.Where to look first:
vendor/smartassert/users-security-bundle/src/ — entity, repository, service definitions.vendor/smartassert/users-security-bundle/config/ — service wiring and Doctrine mapping (this is where the real config surface lives).composer.json of the bundle — it lists the exact smartassert/* sub-packages (user-service, security-service, etc.) whose APIs you'll actually be calling, plus the now-hard symfony/*: ^8.0 constraint.Inject the user service, not the repository. The Symfony-idiomatic pattern for this stack is to depend on the bundle's user service interface (e.g. SmartAssert\UserService\UserService) rather than reach into EntityManager directly. That keeps you compatible if the entity/mapping changes underneath you.
public function __construct(private UserService $users) {}
public function register(string $email, string $password): User
{
return $this->users->create($email, $password);
}
Create users via the factory + password hasher. Raw passwords must never be persisted. The bundle integrates with symfony/password-hasher; check which PasswordHasherInterface alias it consumes so your security.yaml password_hashers config matches.
Wire authentication in security.yaml. Typical shape for this bundle: a form_login or a custom authenticator that loads a user through the bundle's repository by email. If you use form_login, your User class must implement UserInterface and PasswordAuthenticatedUserInterface — and its getPassword() must return the stored hash exactly as the bundle writes it.
Override the entity via inheritance. To add fields (roles, display name, timestamps), extend the bundle's User class and point the bundle's entity mapping at your subclass in config/packages/doctrine.yaml (or by overriding the bundle's service definitions in config/services.yaml with !php/const class arguments). Verify the repository's query methods don't hard-code the base class name before doing this.
Testing. Because dependents are zero, don't rely on examples. Write integration tests that boot the kernel with a sqlite:///:memory: connection, run a migration/schema update, and then exercise create/lookup/verify-password. This gives you a regression net against the fast-moving upstream — and was the only thing that would have caught the 11.x → 12.0 framework drop.
Composition with other smartassert bundles. The UsersSecurityBundle is designed to sit alongside smartassert/user-service and smartassert/security-service. If you find yourself needing an API-key flow, check whether a sibling bundle (.../api-keys-bundle or similar) provides it rather than hand-rolling it — this ecosystem is built to compose.
"smartassert/users-security-bundle": "^11.1" explicitly. A bare composer update will try to pull 12.x and then fail resolving against your symfony/framework-bundle constraint, or force an unwanted Symfony 8 upgrade. Symfony 8 also raises the PHP floor to 8.4, so check your runtime and CI matrix before bumping.config/ — Symfony 8 removed a pile of deprecated security/Doctrine wiring, and the bundle may have silently changed service definitions to accommodate that.src/ and config/ — especially the Doctrine mapping XML/YAML and the service tags, which dictate autoconfiguration.User entity, Doctrine will try to map both. Override cleanly or disable the bundle's mapping (doctrine: orm: mappings: ... enable: false) and map the base class to your subclass.security.password_hashers for your User class with the same algorithm/config the bundle expects; a mismatch looks like "invalid credentials" with no error, which is an easy hour to lose. Re-verify this after the Symfony 8 upgrade — default hasher settings move between majors.maker:user, this bundle doesn't run a migration for you. Confirm whether it ships migrations (check src/Migrations/) or expects doctrine:schema:update."^12.0" or "^11.1") and read the changelog on every upgrade.php bin/console debug:container | grep -i smartassert to see what services are actually registered, and bin/console debug:router to confirm login routes if the bundle registers any.EntityManager use — the bundle likely aliases UserRepository as a service you can inject. If you need custom behavior, decorate that service rather than subclassing the repository, since the internals may be final.composer.json before adopting into a legacy app. As of 12.0 that means PHP 8.4+ and Symfony 8.0 — a legacy Symfony 6 app is now locked out of the current branch entirely.Overall: still a reasonable building block for a small Symfony app that wants a user model without writing it, but the cost of ownership remains high because you're an early adopter. The 12.0 release adds no value on its own — it just raises the floor — so upgrade only when you're moving to Symfony 8 anyway. Treat it as internal code you're vendoring, keep tests around it, and pin versions aggressively.
How can I help you explore Laravel packages today?