/
LoginLinkHandler.php
109 lines (93 loc) · 4.05 KB
/
LoginLinkHandler.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\LoginLink;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException;
use Symfony\Component\Security\Core\Signature\Exception\InvalidSignatureException;
use Symfony\Component\Security\Core\Signature\SignatureHasher;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\LoginLink\Exception\ExpiredLoginLinkException;
use Symfony\Component\Security\Http\LoginLink\Exception\InvalidLoginLinkException;
/**
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
final class LoginLinkHandler implements LoginLinkHandlerInterface
{
private UrlGeneratorInterface $urlGenerator;
private UserProviderInterface $userProvider;
private array $options;
private SignatureHasher $signatureHasher;
public function __construct(UrlGeneratorInterface $urlGenerator, UserProviderInterface $userProvider, SignatureHasher $signatureHasher, array $options)
{
$this->urlGenerator = $urlGenerator;
$this->userProvider = $userProvider;
$this->signatureHasher = $signatureHasher;
$this->options = array_merge([
'route_name' => null,
'lifetime' => 600,
], $options);
}
public function createLoginLink(UserInterface $user, ?Request $request = null, ?int $lifetime = null): LoginLinkDetails
{
$expires = time() + ($lifetime ?: $this->options['lifetime']);
$expiresAt = new \DateTimeImmutable('@'.$expires);
$parameters = [
'user' => $user->getUserIdentifier(),
'expires' => $expires,
'hash' => $this->signatureHasher->computeSignatureHash($user, $expires),
];
if ($request) {
$currentRequestContext = $this->urlGenerator->getContext();
$this->urlGenerator->setContext(
(new RequestContext())
->fromRequest($request)
->setParameter('_locale', $request->getLocale())
);
}
try {
$url = $this->urlGenerator->generate(
$this->options['route_name'],
$parameters,
UrlGeneratorInterface::ABSOLUTE_URL
);
} finally {
if ($request) {
$this->urlGenerator->setContext($currentRequestContext);
}
}
return new LoginLinkDetails($url, $expiresAt);
}
public function consumeLoginLink(Request $request): UserInterface
{
$userIdentifier = $request->get('user');
if (!$hash = $request->get('hash')) {
throw new InvalidLoginLinkException('Missing "hash" parameter.');
}
if (!$expires = $request->get('expires')) {
throw new InvalidLoginLinkException('Missing "expires" parameter.');
}
try {
$this->signatureHasher->acceptSignatureHash($userIdentifier, $expires, $hash);
$user = $this->userProvider->loadUserByIdentifier($userIdentifier);
$this->signatureHasher->verifySignatureHash($user, $expires, $hash);
} catch (UserNotFoundException $e) {
throw new InvalidLoginLinkException('User not found.', 0, $e);
} catch (ExpiredSignatureException $e) {
throw new ExpiredLoginLinkException(ucfirst(str_ireplace('signature', 'login link', $e->getMessage())), 0, $e);
} catch (InvalidSignatureException $e) {
throw new InvalidLoginLinkException(ucfirst(str_ireplace('signature', 'login link', $e->getMessage())), 0, $e);
}
return $user;
}
}