sessionless-1.x-dev/src/SessionlessOnlySign.php
src/SessionlessOnlySign.php
<?php
namespace Drupal\sessionless;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\sessionless\KeyStorage\KeyStorageInterface;
use Drupal\sessionless\Serialization\JsonSafeCompressingObjectAwareSerializationInterface;
use Drupal\sessionless\Utility\CacheTool;
use ParagonIE\Paseto\Builder;
use ParagonIE\Paseto\Exception\PasetoException;
use ParagonIE\Paseto\Keys\AsymmetricSecretKey;
use ParagonIE\Paseto\Parser;
/**
* Only-signed-token service.
*
* Makes the token a bit smaller, but all payload data is public.
*/
final class SessionlessOnlySign implements SessionlessInterface {
public function __construct(
protected KeyStorageInterface $keyStorage,
protected JsonSafeCompressingObjectAwareSerializationInterface $serializer,
protected CacheBackendInterface $cache,
) {}
public function encode(mixed $data): string {
$signingSecretKey = $this->keyStorage->getSigningSecretKey();
$cid = serialize([__METHOD__, $signingSecretKey, $data]);
return CacheTool::getOrCompute($this->cache, $cid,
fn() => $this->doEncode($signingSecretKey, $data));
}
private function doEncode(AsymmetricSecretKey $signingSecretKey, mixed $data): string {
$token = Builder::getPublic($signingSecretKey)
->set('data', $this->serializer->encode($data));
return $token->toString();
}
public function decode(string $token): mixed {
$signingSecretKey = $this->keyStorage->getSigningSecretKey();
$cid = serialize([__METHOD__, $signingSecretKey, $token]);
return CacheTool::getOrCompute($this->cache, $cid,
fn() => $this->doDecode($signingSecretKey, $token));
}
private function doDecode(AsymmetricSecretKey $signingSecretKey, string $token): mixed {
$parser = Parser::getPublic($signingSecretKey->getPublicKey());
try {
$jsonToken = $parser->parse($token);
$serialized = $jsonToken->get('data');
// Unserializing is no attack vector, as at this point the cryptographic
// data signature is validated, and whatever classes are unserialized, it
// is us, that put it in there.
$data = $this->serializer->decode($serialized);
}
catch (PasetoException) {
$data = NULL;
}
return $data;
}
}
