New in Symfony 8.2 (in development). A login link (login_link) logs you in without a password: it carries the identifier, an expiration date and a signature. Anything added to it afterwards — typically the page to bring the reader back to — stayed outside the signature. Anyone could therefore change it, and turn a genuine link, sent by you, into a redirect to another site.
Signed parameters
createLoginLink() now accepts parameters, which the signature covers:
$link = $loginLinks->createLoginLink($user, $request, null, ['return_to' => '/account/invoices']);
Change or remove one of them, and the link no longer logs anyone in. On arrival, the verified parameters wait in a request attribute:
$returnTo = $request->attributes->get('_login_link_parameters')['return_to'] ?? null;
Read them there, not in the query string: a parameter added afterwards — an email tracker, or worse — shows up there without having been signed. Symfony ignores it; your code must do the same.
The signature proves that return_to comes from you, not that it leads to you: anyone who can request a link can also request a signed link to another site. So only accept a path on your own site there.
Your task
LienController builds the link that would go out by email (the response shows it) and appends return_to to it by hand; RedirectionApresLien reads it back from the query string. The link is authentic, its destination isn't: replace /account/invoices with another site, and the reader ends up there, logged in.
- Have the link sign
return_to. - In
RedirectionApresLien, read only the signed parameters. - Only follow a path on the site there (
/…, but not//…); otherwise, redirect to/account.