DÉMO
dev only

Authentication

Registration, login, email verification and password reset.

Available flows

Authentication is built on Symfony Security. Registration, login, email verification and password reset are ready to use.

Feature Detail
Email / password loginsrc/Controller/Auth/LoginController.php
Registration with email verificationsrc/Controller/Auth/RegistrationController.php
Email address confirmationsrc/Controller/Auth/EmailVerificationController.php
Password resetsrc/Controller/Auth/PasswordResetController.php
Registration servicesrc/Service/Auth/RegistrationService.php
Reset servicesrc/Service/Auth/PasswordResetService.php
Verification emailsrc/Mail/Auth/VerificationEmailMailer.php
Reset emailsrc/Mail/Auth/PasswordResetEmailMailer.php
Email event subscribersrc/EventSubscriber/Auth/SendVerificationEmailSubscriber.php
Reset event subscribersrc/EventSubscriber/Auth/SendPasswordResetEmailSubscriber.php
Login rate limitersrc/Security/LoginRateLimiterSubscriber.php
Reset tokensrc/Entity/Auth/PasswordResetToken.php

Login

Login relies on Symfony Security's form_login. The form submits _email and _password fields; the firewall verifies credentials, manages the remember me cookie and the session. No custom controller required.

Form fields

Field Constraint
Email addressRequired, email autocomplete
PasswordRequired, visibility toggle
Remember me30-day cookie (kernel.secret)

Rate limiting

Two independent counters protect the login route: one per IP and one per email (5 attempts / 15 min each). The subscriber intercepts CheckPassportEvent before credential verification, consumes a token on LoginFailureEvent, and resets both counters on LoginSuccessEvent.

src/Security/LoginRateLimiterSubscriber.php

Remember me

The remember me cookie is set to 30 days and signed with kernel.secret. It is cleared on logout.

config/packages/security.yaml

Registration

The registration form collects first name, last name, email and password. Data is validated server-side, the password is hashed, the user is stored, then a signed verification email is dispatched via an event.

Form fields

Field Constraint
First nameRequired, 2–100 chars
Last nameRequired, 2–100 chars
Email addressValid format, max 180 chars
Password (repeated)Min. 8 characters

Anti-enumeration: the same redirect is returned whether or not the email already exists in the database.

Rate limiting

Registration is protected by a token bucket limiter: 5 attempts per 15 minutes per IP.

config/packages/framework.yaml

Password strength

A Stimulus controller evaluates password strength client-side (length ≥ 8/12, uppercase, digit, special char). Labels are passed via data-* attributes for full i18n support.

assets/controllers/password_strength_controller.js

Email verification

After registration, a time-limited signed URL is sent by email. The signature uses HMAC-SHA256 keyed with APP_SECRET and includes a 24-hour expiry timestamp — no database token required.

The signature is compared with hash_equals() to prevent timing attacks.

After successful verification, the user is automatically logged in via Security::login() — no second login step needed.

Events

Feature Detail
UserRegisteredEvent Dispatched after registration — triggers the verification email via SendVerificationEmailSubscriber.
UserEmailVerifiedEvent Dispatched after email verification — hook here to start onboarding flows.

Translations

Auth strings live in a dedicated translation domain, separate from the boilerplate UI strings. Validator constraint messages use the standard validators domain:

File Language
translations/auth.fr.yamlFrançais
translations/auth.en.yamlEnglish
translations/validators.fr.yamlFrançais
translations/validators.en.yamlEnglish

Password reset

The flow relies on a 64-character random token stored in the database as a SHA-256 hash. The link sent by email contains the raw token, which expires after 1 hour. After use, the token is soft-deleted (deleted_at field).

Full flow

  1. The user submits their email on /auth/forgot-password
  2. Rate limiting checked (3 req / 1h per IP + per email)
  3. Token generated (bin2hex(random_bytes(32))), SHA-256 hash stored in the database, email dispatched via PasswordResetRequestedEvent
  4. The user clicks the link → new password form
  5. Token validated (hash + expiry + deleted_at IS NULL), password updated, token soft-deleted, auto-login via Security::login()

PasswordResetToken entity

The primary key is the email — only one active token per address at a time. The deleted_at field ensures soft-delete after use. Expired tokens are cleaned up by a scheduled task (deleteExpired()).

src/Entity/Auth/PasswordResetToken.php

Rate limiting

Two independent counters on /auth/forgot-password: 3 requests per hour per IP and per email. Both must pass.

config/packages/framework.yaml

Anti-enumeration: the same check-reset-email page is returned whether or not the email exists in the database.

Symfony Security configuration

Firewalls, encoders and access controls are configured in:

config/packages/security.yaml

The dev firewall allows access to assets, the profiler and the documentation without authentication.

User entity

The User entity implements UserInterface and PasswordAuthenticatedUserInterface. The preferredLocale field stores the user's locale at registration time, used to send transactional emails in the correct language:

src/Entity/Auth/User.php
Loading…
Loading the web debug toolbar…
Attempt #