Skip to content

Commit 77b4c8a

Browse files
committed
adding an extra security mechanism to remove previous user tokens
This is to imitate what Auth0 does, which only honors the "latest" magic link request - any previous do not work, even if they have not yet been used and should not have expired.
1 parent a73e87b commit 77b4c8a

File tree

5 files changed

+22
-3
lines changed

5 files changed

+22
-3
lines changed

src/Symfony/Bridge/Doctrine/Security/MagicLink/MagicLoginLinkTokenRepositoryTrait.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*
2525
* private function createMagicToken(string $selector, string $hashedVerifier, UserInterface $user, \DateTimeInterface $expiresAt): StoredMagicLinkTokenInterface
2626
*
27-
* B) Your entity needs "selector" and "expiresAtTimestamp" properties.
27+
* B) Your entity needs "selector", "expiresAtTimestamp" and "user" properties.
2828
*
2929
* @author Ryan Weaver <ryan@symfonycasts.com>
3030
*/
@@ -57,6 +57,16 @@ public function invalidateToken(string $selector): void
5757
->execute();
5858
}
5959

60+
public function invalidateAllUserTokens(UserInterface $user): void
61+
{
62+
$this->createQueryBuilder('mlt')
63+
->delete()
64+
->where('mlt.user = :user')
65+
->setParameter('user', $user)
66+
->getQuery()
67+
->execute();
68+
}
69+
6070
public function removeExpiredTokens(): void
6171
{
6272
// keep very-recently expired tokens so that you

src/Symfony/Bundle/SecurityBundle/Tests/Functional/MagicLinkLoginTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
1313

14-
use Symfony\Component\HttpFoundation\JsonResponse;
1514
use Symfony\Component\HttpFoundation\Request;
1615
use Symfony\Component\HttpFoundation\RequestStack;
1716
use Symfony\Component\Security\Core\User\User;

src/Symfony/Component/Security/Http/Authenticator/MagicLinkAuthenticator.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\Security\Http\Authenticator;
1313

14-
use Symfony\Component\HttpFoundation\RedirectResponse;
1514
use Symfony\Component\HttpFoundation\Request;
1615
use Symfony\Component\HttpFoundation\Response;
1716
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

src/Symfony/Component/Security/Http/MagicLink/MagicLinkTokenStorageInterface.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ public function findToken(string $selector): ?StoredMagicLinkTokenInterface;
3838
*/
3939
public function invalidateToken(string $selector): void;
4040

41+
/**
42+
* Invalidate all tokens for the given user.
43+
*
44+
* This is called whenever a new token is generated to guarantee
45+
* that only one valid token is ever active. This is an extra
46+
* security mechanism.
47+
*/
48+
public function invalidateAllUserTokens(UserInterface $user): void;
49+
4150
/**
4251
* Remove all tokens that have expired from storage.
4352
*

src/Symfony/Component/Security/Http/MagicLink/MagicLoginLinker.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public function createMagicLink(UserInterface $user): MagicLoginLinkDetails
4444
{
4545
$this->removeExpiredLinks();
4646

47+
$this->storage->invalidateAllUserTokens($user);
48+
4749
// length will be 20
4850
$selector = $this->generateRandomString(15);
4951
$verifier = $this->generateRandomString(18);

0 commit comments

Comments
 (0)