New in Symfony 8.2 (in development). With sudo mode, a denied IS_AUTHENTICATED_RECENTLY sends the user, through the firewall's re-authentication entry point, to a new authentication instead of a 403. Symfony 8.2 opens this mechanism to your attributes: a voter can tell the firewall that a new proof would change its answer.
The voter notifies the firewall
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { if (/* the expected proof is there */) { return true; } $vote?->requestReAuthentication($attribute); return false; }
The firewall remembers the requested page, notes the denied attribute in the request, then calls its re-authentication entry point, the same one sudo mode uses:
$denied = $request->attributes->get(SecurityRequestAttributes::RE_AUTHENTICATION_ATTRIBUTE);
The entry point can thus ask for the right proof: a code for a transfer, the password for everything else. This split is our choice, not a Symfony rule: IS_AUTHENTICATED_RECENTLY looks at the most recent proof, whatever its method.
Two limits. Only ask for a new proof if it can change the answer: for someone who can never provide it, a 403 is more honest than a loop. And the mechanism only kicks in when a single attribute is denied: a check that combines several always gives a 403.
Your task
The bank from the previous exercise: Ada (ada / lovelace) and Linus (linus / torvalds) receive their codes by SMS, Grace (grace / hopper) gave no phone number. Without a recent code, /transfers answers 403 to all three. /contact-details requires IS_AUTHENTICATED_RECENTLY.
CodeSmsRecentVoterasks for a new authentication when it deniesRECENT_SMS_CODE, but only to those who can receive a code. Grace keeps her 403.DemanderUnePreuvesends to the code form whenRECENT_SMS_CODEwas the denied attribute, to the login page otherwise.- Declare it as the re-authentication entry point of the
mainfirewall.
Once the code is entered, Ada comes back to her transfer.