HMAC implementation for signature validation

This commit is contained in:
Juan Pablo Vial
2025-06-03 21:53:49 -04:00
parent 8c0bd450ef
commit 981858f251
3 changed files with 39 additions and 8 deletions

View File

@ -45,7 +45,8 @@ return [
'/api/external' => [ '/api/external' => [
'/toku/success' => [ '/toku/success' => [
'validator' => Incoviba\Service\Venta\MediosPago\Toku::class, 'validator' => Incoviba\Service\Venta\MediosPago\Toku::class,
'token' => $container->get('TOKU_TOKEN') 'token' => $container->get('TOKU_TOKEN'),
'secret' => $container->get('TOKU_WEBHOOK_SECRET'),
] ]
], ],
] ]

17
app/src/Service/HMAC.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace Incoviba\Service;
use Incoviba\Common\Ideal;
class HMAC extends Ideal\Service
{
public static function validate(string $timestamp, string $requestSignature, string $requestId, string $secret): bool
{
$message = "{$timestamp}.{$requestId}";
$encodedSecret = mb_convert_encoding($secret, 'UTF-8');
$encodedMessage = mb_convert_encoding($message, 'UTF-8');
$hmacObject = hash_hmac('sha256', $encodedMessage, $encodedSecret);
$computedSignature = base64_encode($hmacObject);
return hash_equals($computedSignature, $requestSignature);
}
}

View File

@ -1,6 +1,7 @@
<?php <?php
namespace Incoviba\Service\Venta\MediosPago; namespace Incoviba\Service\Venta\MediosPago;
use Incoviba\Service\HMAC;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Incoviba\Service\Venta\MediosPago\Toku\{Customer,Subscription,Invoice}; use Incoviba\Service\Venta\MediosPago\Toku\{Customer,Subscription,Invoice};
@ -11,6 +12,7 @@ use Incoviba\Exception\ServiceAction\Read;
use Incoviba\Model; use Incoviba\Model;
use Incoviba\Model\Persona; use Incoviba\Model\Persona;
use Incoviba\Model\Venta\Propietario; use Incoviba\Model\Venta\Propietario;
use Throwable;
class Toku extends Ideal\Service class Toku extends Ideal\Service
{ {
@ -407,12 +409,23 @@ class Toku extends Ideal\Service
return false; return false;
} }
$tracestate = explode(';', substr($request->getHeaderLine('Tracestate'), strlen('dd='))); $tokuSignature = $request->getHeaderLine('Toku-Signature');
$ptid = substr(array_find($tracestate, fn($item) => str_starts_with($item, 't.tid:')), strlen('t.tid:')); try {
$datadogTags = explode(',', $request->getHeaderLine('X-Datadog-Tags')); list($timestamp, $signature) = array_map(function($elem) {
$tid = array_find($datadogTags, fn($item) => str_contains($item, 'p.tid=')); return explode('=', $elem)[1];
$tid = substr($tid, strpos($tid, 'p.tid=') + strlen('p.tid=')); }, explode(',', $tokuSignature));
$body = $request->getBody()->getContents();
return $tid === $ptid; $json = json_decode($body, true);
if (!is_array($json)) {
return false;
}
if (!array_key_exists('id', $json)) {
return false;
}
$eventId = $json['id'];
return HMAC::validate($timestamp, $signature, $eventId, $tokenConfig['secret']);
} catch (Throwable $throwable) {
return false;
}
} }
} }