JFIF$        dd7 

Viewing File: /home/alphaedg/public_html/src/vendor/messagebird/php-rest-api/src/MessageBird/RequestValidator.php

<?php

namespace MessageBird;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\SignatureInvalidException;
use MessageBird\Exceptions\ValidationException;
use MessageBird\Objects\SignedRequest;

use function base64_decode;
use function hash;
use function hash_equals;
use function hash_hmac;
use function http_build_query;
use function implode;
use function ksort;
use function PHPUnit\Framework\throwException;
use function time;

/**
 * Class RequestValidator validates request signature signed by MessageBird services.
 *
 * @package MessageBird
 * @see https://developers.messagebird.com/docs/verify-http-requests
 */
class RequestValidator
{
    const BODY_HASH_ALGO = 'sha256';
    const HMAC_HASH_ALGO = 'sha256';
    const ALLOWED_ALGOS = array('HS256', 'HS384', 'HS512');

    /**
     * The key with which requests will be signed by MessageBird.
     *
     * @var string
     */
    private $signingKey;

    /**
     * This field instructs Validator to not validate url_hash claim.
     * It is recommended to not skip URL validation to ensure high security.
     * but the ability to skip URL validation is necessary in some cases, e.g.
     * your service is behind proxy or when you want to validate it yourself.
     * Note that when true, no query parameters should be trusted.
     * Defaults to false.
     *
     * @var bool
     */
    private $skipURLValidation;

    /**
     * RequestValidator constructor.
     *
     * @param string $signingKey customer signature key. Can be retrieved through <a href="https://dashboard.messagebird.com/developers/settings">Developer Settings</a>. This is NOT your API key.
     * @param bool $skipURLValidation whether url_hash claim validation should be skipped. Note that when true, no query parameters should be trusted.
     */
    public function __construct(string $signingKey, bool $skipURLValidation = false)
    {
        $this->signingKey = $signingKey;
        $this->skipURLValidation = $skipURLValidation;
    }

    /**
     * Verify that the signed request was submitted from MessageBird using the known key.
     *
     * @return bool
     * @deprecated Use {@link RequestValidator::validateSignature()} instead.
     */
    public function verify(SignedRequest $signedRequest): bool
    {
        $payload = $this->buildPayloadFromRequest($signedRequest);

        $calculatedSignature = hash_hmac(self::HMAC_HASH_ALGO, $payload, $this->signingKey, true);
        $expectedSignature = base64_decode($signedRequest->signature, true);

        return hash_equals($expectedSignature, $calculatedSignature);
    }

    /**
     * @deprecated Use {@link RequestValidator::validateSignature()} instead.
     */
    private function buildPayloadFromRequest(SignedRequest $signedRequest): string
    {
        $parts = [];

        // Add request timestamp
        $parts[] = $signedRequest->requestTimestamp;

        // Build sorted query string
        $query = $signedRequest->queryParameters;
        ksort($query, \SORT_STRING);
        $parts[] = http_build_query($query);

        // Calculate checksum for request body
        $parts[] = hash(self::BODY_HASH_ALGO, $signedRequest->body, true);

        return implode("\n", $parts);
    }

    /**
     * Check whether the request was made recently.
     *
     * @param SignedRequest $signedRequest The signed request object.
     * @param int $offset The maximum number of seconds that is allowed to consider the request recent
     * @return bool
     * @deprecated Use {@link RequestValidator::validateSignature()} instead.
     */
    public function isRecent(SignedRequest $signedRequest, int $offset = 10): bool
    {
        return (\time() - (int)$signedRequest->requestTimestamp) < $offset;
    }

    /**
     * Validate JWT signature.
     * This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and a specific account.
     * The JWT contains the following claims:
     *  - "url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.
     *  - "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.
     *  - "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).
     *  - "nbf" - the not before timestamp.
     *  - "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.
     *  - "iss" - the issuer name, always MessageBird.
     *
     * @param string $signature the actual signature taken from request header "MessageBird-Signature-JWT".
     * @param string $url the raw url including the protocol, hostname and query string, {@code https://example.com/?example=42}.
     * @param string $body the raw request body.
     * @return object JWT token payload
     * @throws ValidationException if signature validation fails.
     *
     * @see https://developers.messagebird.com/docs/verify-http-requests
     */
    public function validateSignature(string $signature, string $url, string $body)
    {
        if (empty($signature)) {
            throw new ValidationException("Signature cannot be empty.");
        }
        if (!$this->skipURLValidation && empty($url)) {
            throw new ValidationException("URL cannot be empty");
        }

        JWT::$leeway = 1;
        try {
            $headb64 = \explode('.', $signature)[0];
            $headerRaw = JWT::urlsafeB64Decode($headb64);
            $header = JWT::jsonDecode($headerRaw);

            $key = [];
            if ($header && property_exists($header, 'alg')) {
                if (!in_array(strtoupper($header->alg), self::ALLOWED_ALGOS, true)) {
                    throw new ValidationException('Algorithm not supported');
                }

                $key = new Key($this->signingKey, $header->alg);
            }

            $decoded = JWT::decode($signature, $key);
        } catch (\InvalidArgumentException | \UnexpectedValueException | SignatureInvalidException $e) {
            throw new ValidationException($e->getMessage(), $e->getCode(), $e);
        }

        if (empty($decoded->iss) || $decoded->iss !== 'MessageBird') {
            throw new ValidationException('invalid jwt: claim iss has wrong value');
        }

        if (!$this->skipURLValidation && !hash_equals(hash(self::HMAC_HASH_ALGO, $url), $decoded->url_hash)) {
            throw new ValidationException('invalid jwt: claim url_hash is invalid');
        }

        switch (true) {
            case empty($body) && !empty($decoded->payload_hash):
                throw new ValidationException('invalid jwt: claim payload_hash is set but actual payload is missing');
            case !empty($body) && empty($decoded->payload_hash):
                throw new ValidationException('invalid jwt: claim payload_hash is not set but payload is present');
            case !empty($body) && !hash_equals(hash(self::HMAC_HASH_ALGO, $body), $decoded->payload_hash):
                throw new ValidationException('invalid jwt: claim payload_hash is invalid');
        }

        return $decoded;
    }

    /**
     * Validate request signature from PHP globals.
     *
     * @return object JWT token payload
     * @throws ValidationException if signature validation fails.
     */
    public function validateRequestFromGlobals()
    {
        $signature = $_SERVER['MessageBird-Signature-JWT'] ?? null;
        $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        $body = file_get_contents('php://input');

        return $this->validateSignature($signature, $url, $body);
    }
}
Back to Directory  nL+D550H?Mx ,D"v]qv;6*Zqn)ZP0!1 A "#a$2Qr D8 a Ri[f\mIykIw0cuFcRı?lO7к_f˓[C$殷WF<_W ԣsKcëIzyQy/_LKℂ;C",pFA:/]=H  ~,ls/9ć:[=/#f;)x{ٛEQ )~ =𘙲r*2~ a _V=' kumFD}KYYC)({ *g&f`툪ry`=^cJ.I](*`wq1dđ#̩͑0;H]u搂@:~וKL Nsh}OIR*8:2 !lDJVo(3=M(zȰ+i*NAr6KnSl)!JJӁ* %݉?|D}d5:eP0R;{$X'xF@.ÊB {,WJuQɲRI;9QE琯62fT.DUJ;*cP A\ILNj!J۱+O\͔]ޒS߼Jȧc%ANolՎprULZԛerE2=XDXgVQeӓk yP7U*omQIs,K`)6\G3t?pgjrmۛجwluGtfh9uyP0D;Uڽ"OXlif$)&|ML0Zrm1[HXPlPR0'G=i2N+0e2]]9VTPO׮7h(F*癈'=QVZDF,d߬~TX G[`le69CR(!S2!P <0x<!1AQ "Raq02Br#SCTb ?Ζ"]mH5WR7k.ۛ!}Q~+yԏz|@T20S~Kek *zFf^2X*(@8r?CIuI|֓>^ExLgNUY+{.RѪ τV׸YTD I62'8Y27'\TP.6d&˦@Vqi|8-OΕ]ʔ U=TL8=;6c| !qfF3aů&~$l}'NWUs$Uk^SV:U# 6w++s&r+nڐ{@29 gL u"TÙM=6(^"7r}=6YݾlCuhquympǦ GjhsǜNlɻ}o7#S6aw4!OSrD57%|?x>L |/nD6?/8w#[)L7+6〼T ATg!%5MmZ/c-{1_Je"|^$'O&ޱմTrb$w)R$& N1EtdU3Uȉ1pM"N*(DNyd96.(jQ)X 5cQɎMyW?Q*!R>6=7)Xj5`J]e8%t!+'!1Q5 !1 AQaqё#2"0BRb?Gt^## .llQT $v,,m㵜5ubV =sY+@d{N! dnO<.-B;_wJt6;QJd.Qc%p{ 1,sNDdFHI0ГoXшe黅XۢF:)[FGXƹ/w_cMeD,ʡcc.WDtA$j@:) -# u c1<@ۗ9F)KJ-hpP]_x[qBlbpʖw q"LFGdƶ*s+ډ_Zc"?%t[IP 6J]#=ɺVvvCGsGh1 >)6|ey?Lӣm,4GWUi`]uJVoVDG< SB6ϏQ@ TiUlyOU0kfV~~}SZ@*WUUi##; s/[=!7}"WN]'(L! ~y5g9T̅JkbM' +s:S +B)v@Mj e Cf jE 0Y\QnzG1д~Wo{T9?`Rmyhsy3!HAD]mc1~2LSu7xT;j$`}4->L#vzŏILS ֭T{rjGKC;bpU=-`BsK.SFw4Mq]ZdHS0)tLg