Symfony Security needs a user class before a login form, API authenticator, or access rule can identify an account. The class gives the security system a stable user identifier, roles, and password-hash support, while Doctrine gives the application a table that can store and reload the account.
Symfony Maker generates the default Doctrine-backed User entity and updates /config/packages/security.yaml with the matching provider. Using the default email identifier and password support creates a starting point that works with later registration and login features without hand-writing the security contracts.
The database schema still has to be created after the class is generated. A small local database and a disposable user row are enough to prove the entity, provider configuration, password hasher, and Doctrine mapping all agree before browser login or registration is added.
Related: How to create a Symfony registration form
Related: How to create a Symfony login form
Use the directory that contains composer.json, bin/console, and the src directory.
DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db"
Use the application's existing PostgreSQL, MySQL, or SQLite URL when one is already configured. The SQLite value above is only a local proof database.
Related: How to configure a SQLite database in Symfony
$ php bin/console make:user User Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]: > Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]: > Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server). Does this app need to hash/check user passwords? (yes/no) [yes]: > created: src/Entity/User.php created: src/Repository/UserRepository.php updated: src/Entity/User.php updated: config/packages/security.yaml Success!
The default answers create App\Entity\User with email as the identifier and password-hash support through PasswordAuthenticatedUserInterface.
$ php bin/console debug:config security providers
Current configuration for "security.providers"
==============================================
app_user_provider:
entity:
class: App\Entity\User
property: email
manager_name: null
$ php bin/console debug:config security password_hashers
Current configuration for "security.password_hashers"
=====================================================
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
migrate_from: {}
hash_algorithm: sha512
key_length: 40
ignore_case: false
encode_as_base64: true
iterations: 5000
cost: null
memory_cost: null
time_cost: null
The auto hasher lets Symfony choose and migrate the password algorithm for the user class.
$ php bin/console make:migration created: migrations/Version20260625075108.php Success! Review the new migration then run it with php bin/console doctrine:migrations:migrate See https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
The version filename uses the current timestamp, so the filename will differ on each project. Review the migration before applying it to a database with existing data.
Related: How to create a Doctrine migration in Symfony
$ php bin/console doctrine:migrations:migrate --no-interaction [notice] Migrating up to DoctrineMigrations\Version20260625075108 [notice] finished in 17.2ms, used 24M memory, 1 migrations executed, 4 sql queries [OK] Successfully migrated to version: DoctrineMigrations\Version20260625075108
Run migrations through the application's normal deployment process outside local development.
Related: How to run Doctrine migrations in Symfony
$ php bin/console security:hash-password '' 'App\Entity\User' Symfony Password Hash Utility ============================= Type in your password to be hashed: > --------------- ----------------------------------------------------------------- Key Value --------------- ----------------------------------------------------------------- Hasher used Symfony\Component\PasswordHasher\Hasher\MigratingPasswordHasher Password hash $2y$13$WZdqvmi27fmPDOn0oZv.WeO5z5JqCOtPbSYBqBUb0Z53k1sYVcd6i --------------- ----------------------------------------------------------------- ! [NOTE] Self-salting hasher used: the hasher generated its own built-in salt. [OK] Password hashing succeeded
Use a disposable password for this smoke test. Do not pass a real password as a shell argument because it can appear in shell history and process lists.
$ php bin/console dbal:run-sql "INSERT INTO \"user\" (email, roles, password) VALUES ('reader@example.com', '[]', '$2y$13$WZdqvmi27fmPDOn0oZv.WeO5z5JqCOtPbSYBqBUb0Z53k1sYVcd6i')"
[OK] 1 rows affected.
Replace the hash with the value printed by security:hash-password. Use an application registration form or an admin workflow for real accounts instead of manual SQL.
Related: How to create a Symfony registration form
$ php bin/console doctrine:query:dql 'SELECT u.email, u.roles FROM App\Entity\User u'
array(1) {
[0]=>
array(2) {
["email"]=>
string(18) "reader@example.com"
["roles"]=>
array(0) {
}
}
}
The stored roles array can be empty because the generated getRoles() method adds ROLE_USER at runtime.
$ php bin/console dbal:run-sql "DELETE FROM \"user\" WHERE email = 'reader@example.com'" [OK] 1 rows affected.