1. Go to this page and download the library: Download zenstruck/signed-url-bundle library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
zenstruck / signed-url-bundle example snippets
use Zenstruck\SignedUrl\Generator;
public function sendPasswordResetEmail(User $user, Generator $generator)
{
$resetUrl = $generator->build('password_reset_route', ['id' => $user->getId()])
->expires('+1 day')
->singleUse($user->getPassword())
;
// create email with $resetUrl and send
}
use Zenstruck\SignedUrl\Verifier;
use Zenstruck\SignedUrl\Exception\UrlVerificationFailed;
public function resetPasswordAction(User $user, Verifier $urlVerifier)
{
try {
$urlVerifier->verifyCurrentRequest(singleUseToken: $user->getPassword());
} catch (UrlVerificationFailed $e) {
$this->flashError($e->messageKey()); // safe reason to show user
return $this->redirect(...);
}
// continue
}
/** @var Zenstruck\SignedUrl\Generator $generator */
(string) $generator->build('route1')->expires('+1 hour'); // http://example.com/route1?__expires=...&_hash=...
(string) $generator->build('route2', ['parameter1' => 'value'])->expires('+1 hour'); // http://example.com/route2/value?__expires=...&_hash=...
// use # of seconds
(string) $generator->build('route1')->expires(3600); // http://example.com/route2/value?__expires=...&_hash=...
// use an explicit \DateTime
(string) $generator->build('route1')->expires(new \DateTime('+1 hour')); // http://example.com/route2/value?__expires=...&_hash=...
use Zenstruck\SignedUrl\Exception\UrlVerificationFailed;
/** @var Zenstruck\SignedUrl\Verifier $verifier */
/** @var string $url */
/** @var Symfony\Component\HttpFoundation\Request $request */
// simple usage: return true if valid and non-expired (if applicable), false otherwise
$verifier->isVerified($url);
$verifier->isVerified($request); // can pass Symfony request object
$verifier->isCurrentRequestVerified(); // verifies the current request (fetched from RequestStack)
// try/catch usage: catch exceptions to provide better feedback to users
try {
$verifier->verify($url);
$verifier->verify($request); // alternative
$verifier->verifyCurrentRequest(); // alternative
} catch (UrlVerificationFailed $e) {
$e->url(); // the url used
$e->getMessage(); // Internal message (ie for logging)
$e->messageKey(); // Safe message with reason to show the user (or use with translator)
}
/** @var Zenstruck\SignedUrl\Generator $generator */
// !! This will be the single-use token that changes once "used" !!
$password = $user->getPassword();
$url = $generator->build('reset_password', ['id' => $user->getId()])
->singleUse($password)
->create()
;
use Zenstruck\SignedUrl\Exception\UrlVerificationFailed;
/** @var Zenstruck\SignedUrl\Verifier $verifier */
/** @var string $url */
/** @var Symfony\Component\HttpFoundation\Request $request */
// !! This is the single-use token. If the url was generated with a different password verification will fail !!
$password = $user->getPassword();
$verifier->isVerified($url, $password);
$verifier->isVerified($request, $password);
$verifier->isCurrentRequestVerified($password);
// try/catch usage: catch exceptions to provide better feedback to users
try {
$verifier->verify($url, $password);
$verifier->verify($request, $password); // alternative
$verifier->verifyCurrentRequest($password); // alternative
} catch (UrlVerificationFailed $e) {
$e->messageKey(); // "URL has already been used." (if failed for this reason)
}
final class ResetPasswordToken
{
public function __construct(private User $user) {}
public function __toString(): string
{
return $this->user->getPassword();
}
}
/** @var Zenstruck\SignedUrl\Verifier $verifier */
$verifier->isVerified($url, new ResetPasswordToken($user));
$verifier->verify($url, new ResetPasswordToken($user));
$verifier->isCurrentRequestVerified(new ResetPasswordToken($user));
$verifier->verifyCurrentRequest(new ResetPasswordToken($user));
use Zenstruck\SignedUrl\Attribute\Signed;
#[Signed]
#[Route(...)]
public function action1() {} // throws a 403 HttpException if verification fails
#[Signed(status: 404)]
#[Route(...)]
public function action1() {} // throw a 404 exception instead
use Zenstruck\SignedUrl\Exception\UrlVerificationFailed;
use Zenstruck\SignedUrl\Exception\UrlHasExpired;
use Zenstruck\SignedUrl\Exception\UrlAlreadyUsed;
/** @var Zenstruck\SignedUrl\Verifier $verifier */
try {
$verifier->verifyCurrentRequest($user->getPassword());
} catch (UrlHasExpired $e) {
// this exception makes the expiration available
$e->expiredAt(); // \DateTimeImmutable
$e->messageKey(); // "URL has expired."
$e->url(); // the URL that failed verification
} catch (UrlAlreadyUsed $e) {
$e->messageKey(); // "URL has already been used."
$e->url(); // the URL that failed verification
} catch (UrlVerificationFailed $e) {
// must be last as a "catch all"
$e->messageKey(); // "URL Verification failed."
$e->url(); // the URL that failed verification
}
/** @var \Zenstruck\SignedUrl\Generator $generator */
/** @var \Zenstruck\SignedUrl\Verifier $verifier */
// REQUEST PASSWORD RESET ACTION (GENERATE URL)
$url = $generator->build('reset_password', ['id' => $user->getId()])
->expires('+1 day')
->singleUse($user->getPassword()) // current password is the token that changes once "used"
->create()
;
// send email to user with $url
// PASSWORD RESET ACTION (VERIFY URL)
try {
$verifier->verifyCurrentRequest($user->getPassword()); // current password as the token
} catch (\Zenstruck\SignedUrl\Exception\UrlVerificationFailed $e) {
$this->flashError($e->messageKey());
return $this->redirect(...);
}
// proceed with the reset, once a new password will be set/saved, this URL will become invalid
final class VerifyToken
{
public function __construct(private User $user) {}
public function __toString(): string
{
return $this->user->isVerified() ? 'verified' : 'unverified';
}
}
/** @var \Zenstruck\SignedUrl\Generator $generator */
/** @var \Zenstruck\SignedUrl\Verifier $verifier */
// REGISTRATION CONTROLLER ACTION (GENERATE URL)
$url = $generator->build('verify_user', ['id' => $user->getId()])
->singleUse(new VerifyToken($user)) // this token's value will be "unverified"
->create()
;
// send email to user with $url
// VERIFICATION ACTION (VERIFY URL)
try {
$verifier->verifyCurrentRequest(new VerifyToken($user)); // this token's value should be "unverified" but if not, it is invalid
} catch (\Zenstruck\SignedUrl\Exception\UrlVerificationFailed $e) {
$this->flashError($e->messageKey());
return $this->redirect(...);
}
$user->verify(); // marks the user as verified and invalidates the URL
// save user & login user immediately or redirect to login page
/** @var \Zenstruck\SignedUrl\Generator $generator */
/** @var \Zenstruck\SignedUrl\Verifier $verifier */
// REQUEST EMAIL CHANGE ACTION (GENERATE URL)
$url = $generator->build('reset_password', ['id' => $user->getId(), 'new-email' => $newEmailRequested])
->expires('+1 day')
->singleUse($user->getEmail()) // the user's current email
->create()
;
// send verification email to $newEmailRequested with $url
// EMAIL CHANGE ACTION (VERIFY URL)
try {
$verifier->verify($request, $user->getEmail()); // the user's current email
} catch (\Zenstruck\SignedUrl\Exception\UrlVerificationFailed $e) {
$this->flashError($e->messageKey());
return $this->redirect(...);
}
$user->setEmail($request->query->get('new-email')); // changes the user email and invalidates the URL
// save user
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.