Repository navigation
Release v1.31.0
v1.31.0
Changelog
Security release: look-alike identifier account takeover (H1), cross-profile social sign-in (H2), social sign-up/linking, reset-code lockout, link lifetimes, CSRF fail-closed; attribute routes, NowoAuthKitBundle validator domain, real deprecation gate See UPGRADING.
Security hardening (SEC-004 2026-10-27), single translation domain and attribute-declared routes. Several defaults and behaviours change: read UPGRADING before updating.
Security
- High — look-alike identifiers (CWE-178/640), affects password reset >= 1.1.0, magic login >= 1.6.0, social login e-mail linking >= 1.9.0, up to 1.30.0. With an accent/case-insensitive database collation (MySQL
utf8mb4_0900_ai_ci, MariaDB*_unicode_ci)victim@exámple.comresolved tovictim@example.com, and the typed address was handed to the notifier: the victim's reset / magic link went to an IDN look-alike the attacker owns. Lookups now accept only an account whose stored identifier equals the input after NFC + lower-casing (Security\UserIdentifierMatcher); notification contexts carry the stored identifier and theuser; e-mail identifiers must be plain ASCII addresses on the request forms. - High — cross-profile social sign-in (CWE-639), affects 1.9.0 to 1.30.0 with several profiles. A social link was found by provider subject only and loaded with the current profile's user class: a member's identity signed in to the admin profile as the admin with the same id. Links are now scoped to the user class (unique
provider + provider_user_id + user_class, schema change) and the loaded row must be of that class. - Medium: social login created accounts regardless of
registration_mode(create_user_if_missingnow defaults tofalseand honoursRegistrationGate); a social identity was linked to a pre-registered local account and kept its password (social_login.link_existing_account, defaultreset_password); the reset-code attempt counter was keyed by the typed identifier, so spelling variants bypassedmax_code_attempts(now keyed by the account, plus a per-IPcode_rate_limit). - Low:
configure-securitywrote alogin_link.lifetimeshorter than administrator links and no password insignature_properties; magic links were signed / consumed by themainfirewall handler and QR challenges were not bound to a profile; a request locale from input could reach generated paths (//hostredirect); new passwords needed only 6 characters; registration auto-login built the token by hand; a pending reset credential survived an identifier or password change. - CSRF fails closed: QR approval and the magic-login confirm interstitial refuse to compile (
CsrfProtectionPass) and to act (Form\FormCsrf) without the form CSRF extension;symfony/security-csrfis now required.
Added
- Config
password_policy.min_length(default 12) andpassword_policy.not_compromised(default false);password_reset.code_rate_limit(10) /code_rate_window(900);social_login.link_existing_account(reset_password|deny|keep). MagicLoginNotificationContext::$userandPasswordResetNotificationContext::$user.Security\LoginLinkHandlerLocator(+LoginLinkHandlerLocatorPass): the login link handler of each firewall.Doctrine\PasswordResetTokenInvalidationListener(DoctrineonFlush).- Translations
validation.identifier_emailandvalidation.password_compromised(7 locales). - Every controller action declares its
#[Route](default path and name, HTTP methods, requirements);AuthKitRouteLoadercopies the attribute once per profile with the configuredroutes.<key>.path/.nameand locale variants, so the generated routes are identical to 1.30.0 (REQ-SF-004). DependencyInjection\Compiler\ControllerRouteAttributesPassremoves the autoconfiguredrouting.controllertag from the AuthKit controllers, so an application importingresource: routing.controllersdoes not register un-profiled copies.Form\Extension\ConstraintMessageTranslationExtension: AuthKit constraint messages are translated in the form translation domain (NowoAuthKitBundle) after validation; a key the application defines in itsvalidatorscatalogue keeps its translation (REQ-I18N-003).
Changed
social_login.create_user_if_missingdefaults tofalse(wastrue).MagicLoginNotificationContext::$identifier/PasswordResetNotificationContext::$identifierhold the identifier stored on the account (was the typed input) andmaskedIdentifieris derived from it.- New-password minimum length 12 (was 6) unless
password_policy.min_lengthsays otherwise. nowo:auth-kit:configure-securitywriteslogin_link.signature_properties: [<identifier>, password]andlifetime: max(magic_login.lifetime, magic_login.admin_link.lifetime).SocialLoginAccountunique constraintuniq_auth_kit_social_account_provider_subject_classonprovider, provider_user_id, user_class(wasuniq_auth_kit_social_account_provider_subjectonprovider, provider_user_id);SocialLoginAccountRepository::findOneByProviderSubject()takes an optional user class.- The registration page signs the new account in with
Security::login($user, 'form_login', <firewall>). QrLoginChallengeManager::verifyDesktopCookie()takes the profile name; challenges created before the update stop completing (their 90 s lifetime makes this invisible in practice).- Requires
symfony/security-csrf,symfony/polyfill-intl-normalizerandsymfony/polyfill-mbstring. - The AuthKit form constraint messages live only in the
NowoAuthKitBundlecatalogues;make validate-translationsfails on any catalogue outside the bundle domain (REQ-I18N-003). - PHPUnit fails on any deprecation (
failOnDeprecation,ignoreSuppressionOfDeprecations,restrictDeprecations="false"):SYMFONY_DEPRECATIONS_HELPERis ignored by PHPUnit >= 10. Tests that exercise deprecated configuration on purpose are marked#[IgnoreDeprecations];phpunit-baseline.xmllists the two indirect Twig deprecations raised by the UiKitPageContractTestCase(REQ-SF-005). - CI pins the transitive Symfony components (event-dispatcher, error-handler, …) to the cell minor too, so the 7.4 cells no longer run 8.x components; the test kernels set
framework.property_info.with_constructor_extractoron Symfony 7.3/7.4. - Spec Kit baseline (
specs/001-baseline) covers the 1.29/1.30 units: 186/186 production files mapped (REQ-SPECKIT-003). - The test bootstrap clears the integration kernels' cache directory, so repeated local runs do not hit the previous run's rate limits.
Removed
src/Resources/translations/validators.<locale>.yaml(7 locales): same keys and texts as theNowoAuthKitBundlecatalogues.
What's Changed
- feat: single translation domain, attribute routes and a real deprecation gate (REQ-I18N-003, REQ-SF-004, REQ-SF-005) by @HecFranco in #17
- fix(security): SEC-004 hardening (look-alike identifiers, cross-profile social login, CSRF fail-closed) by @HecFranco in #18
Full Changelog: v1.30.0...v1.31.0