Skip to content

Commit 80d6329

Browse files
committed
Actions to recreate and resend new email and verify email tokens and emails
1 parent 324fc22 commit 80d6329

4 files changed

Lines changed: 136 additions & 14 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Silverback API Components Bundle Project
5+
*
6+
* (c) Daniel West <daniel@silverback.is>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Silverback\ApiComponentsBundle\Action\User;
15+
16+
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
17+
use Silverback\ApiComponentsBundle\Helper\User\UserDataProcessor;
18+
use Silverback\ApiComponentsBundle\Helper\User\UserMailer;
19+
use Symfony\Bundle\SecurityBundle\Security;
20+
use Symfony\Component\HttpFoundation\Response;
21+
22+
/**
23+
* @author Daniel West <daniel@silverback.is>
24+
*/
25+
final readonly class ResendVerifyEmailAddressAction
26+
{
27+
public function __construct(
28+
private UserMailer $userMailer,
29+
private UserDataProcessor $userDataProcessor,
30+
private Security $security,
31+
) {
32+
}
33+
34+
public function __invoke(string $username, string $token): Response
35+
{
36+
$user = $this->security->getUser();
37+
if (!$user instanceof AbstractUser) {
38+
return new Response(null, Response::HTTP_UNAUTHORIZED);
39+
}
40+
$this->userDataProcessor->setEmailAddressVerifyToken($user);
41+
$emailSuccess = $this->userMailer->sendEmailVerifyEmail($user);
42+
43+
$response = new Response(null, $emailSuccess ? Response::HTTP_OK : Response::HTTP_SERVICE_UNAVAILABLE);
44+
$response->setCache([
45+
'private' => true,
46+
's_maxage' => 0,
47+
'max_age' => 0,
48+
]);
49+
50+
return $response;
51+
}
52+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Silverback API Components Bundle Project
5+
*
6+
* (c) Daniel West <daniel@silverback.is>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Silverback\ApiComponentsBundle\Action\User;
15+
16+
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
17+
use Silverback\ApiComponentsBundle\Helper\User\UserDataProcessor;
18+
use Silverback\ApiComponentsBundle\Helper\User\UserMailer;
19+
use Symfony\Bundle\SecurityBundle\Security;
20+
use Symfony\Component\HttpFoundation\Response;
21+
22+
/**
23+
* @author Daniel West <daniel@silverback.is>
24+
*/
25+
final readonly class ResendVerifyNewEmailAddressAction
26+
{
27+
public function __construct(
28+
private UserMailer $userMailer,
29+
private UserDataProcessor $userDataProcessor,
30+
private Security $security,
31+
) {
32+
}
33+
34+
public function __invoke(string $username, string $token): Response
35+
{
36+
$user = $this->security->getUser();
37+
if (!$user instanceof AbstractUser) {
38+
return new Response(null, Response::HTTP_UNAUTHORIZED);
39+
}
40+
$this->userDataProcessor->setNewEmailConfirmationToken($user);
41+
$emailSuccess = $this->userMailer->sendChangeEmailConfirmationEmail($user);
42+
43+
$response = new Response(null, $emailSuccess ? Response::HTTP_OK : Response::HTTP_SERVICE_UNAVAILABLE);
44+
$response->setCache([
45+
'private' => true,
46+
's_maxage' => 0,
47+
'max_age' => 0,
48+
]);
49+
50+
return $response;
51+
}
52+
}

src/Helper/User/UserDataProcessor.php

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@
2424
/**
2525
* @author Daniel West <daniel@silverback.is>
2626
*/
27-
class UserDataProcessor
27+
readonly class UserDataProcessor
2828
{
2929
public function __construct(
30-
private readonly UserPasswordHasherInterface $passwordHasher,
31-
private readonly UserRepositoryInterface $userRepository,
32-
private readonly PasswordHasherFactoryInterface $passwordHasherFactory,
33-
private readonly bool $initialEmailVerifiedState,
34-
private readonly bool $verifyEmailOnRegister,
35-
private readonly bool $verifyEmailOnChange,
36-
private readonly int $tokenTtl = 8600,
30+
private UserPasswordHasherInterface $passwordHasher,
31+
private UserRepositoryInterface $userRepository,
32+
private PasswordHasherFactoryInterface $passwordHasherFactory,
33+
private bool $initialEmailVerifiedState,
34+
private bool $verifyEmailOnRegister,
35+
private bool $verifyEmailOnChange,
36+
private int $tokenTtl = 8600,
3737
) {
3838
}
3939

@@ -83,23 +83,21 @@ public function processChanges(AbstractUser $user, ?AbstractUser $previousUser):
8383
if (!$previousUser) {
8484
$user->setEmailAddressVerified($this->initialEmailVerifiedState);
8585
if (!$this->initialEmailVerifiedState && $this->verifyEmailOnRegister) {
86-
$user->setEmailAddressVerifyToken($this->passwordHasher->hashPassword($user, $token = TokenGenerator::generateToken()));
87-
$user->plainEmailAddressVerifyToken = $token;
86+
$this->setEmailAddressVerifyToken($user);
8887
}
8988
}
9089

9190
if ($previousUser) {
9291
if ($this->verifyEmailOnChange && $user->getEmailAddress() !== $previousUser->getEmailAddress()) {
93-
$user->setEmailAddressVerifyToken($this->passwordHasher->hashPassword($user, $token = TokenGenerator::generateToken()));
94-
$user->plainEmailAddressVerifyToken = $token;
92+
$this->setEmailAddressVerifyToken($user);
9593
}
9694
if (($newEmail = $user->getNewEmailAddress()) !== $previousUser->getNewEmailAddress()) {
9795
if ($newEmail) {
98-
$user->setNewEmailConfirmationToken($this->passwordHasher->hashPassword($user, $token = TokenGenerator::generateToken()));
99-
$user->plainNewEmailConfirmationToken = $token;
96+
$this->setNewEmailConfirmationToken($user);
10097
} else {
10198
// invalidate any existing requests
10299
$user->setNewEmailConfirmationToken(null);
100+
// revert any tokens for the new primary saved email address as this was converted from the update
103101
if ($previousUser->getNewEmailAddress() === $user->getEmailAddress() && $user->plainEmailAddressVerifyToken) {
104102
$user->plainEmailAddressVerifyToken = null;
105103
$user->setEmailAddressVerifyToken(null);
@@ -109,6 +107,18 @@ public function processChanges(AbstractUser $user, ?AbstractUser $previousUser):
109107
}
110108
}
111109

110+
public function setNewEmailConfirmationToken(AbstractUser $user): void
111+
{
112+
$user->setNewEmailConfirmationToken($this->passwordHasher->hashPassword($user, $token = TokenGenerator::generateToken()));
113+
$user->plainNewEmailConfirmationToken = $token;
114+
}
115+
116+
public function setEmailAddressVerifyToken(AbstractUser $user): void
117+
{
118+
$user->setEmailAddressVerifyToken($this->passwordHasher->hashPassword($user, $token = TokenGenerator::generateToken()));
119+
$user->plainEmailAddressVerifyToken = $token;
120+
}
121+
112122
private function hashPassword(AbstractUser $entity): void
113123
{
114124
if (!$entity->getPlainPassword()) {

src/Resources/config/routing/security.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,12 @@
1919
<route id="api_components_verify_email" path="/verify-email/{username}/{token}"
2020
controller="Silverback\ApiComponentsBundle\Action\User\VerifyEmailAddressAction"
2121
methods="GET"/>
22+
23+
<route id="api_components_resend_email_verification" path="/resend-verify-email/{username}"
24+
controller="Silverback\ApiComponentsBundle\Action\User\ResendVerifyEmailAddressAction"
25+
methods="GET"/>
26+
27+
<route id="api_components_resend_new_email_verification" path="/resend-verify-new-email/{username}"
28+
controller="Silverback\ApiComponentsBundle\Action\User\ResendVerifyNewEmailAddressAction"
29+
methods="GET"/>
2230
</routes>

0 commit comments

Comments
 (0)