Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

665
Views
How to verify discord endpoint using php?

I am having some issues in creating a Discord bot. I want it to be able to respond to slash commands, but to do so I need to verify an endpoint. I am using PHP 7.4, and can't use any external libraries (hosting on a server that does not allow them). I have found documents for PHP, but they do require libraries to work. I tried taking the documents from Node.JS and "converting" them to PHP. Here's my code:

<?php

$public_key = "shh-it's-a-seekrit";
$headers = getallheaders();
$signature = $headers["X-Signature-Ed25519"];
$timestamp = $headers["X-Signature-Timestamp"];
$raw_body = file_get_contents('php://input');

/* To compute the signature, we need the following
 * 1. Message ($timestamp + $body)
 * 2. $signature
 * 3. $public_key
 * The algorythm is SHA-512
 */
$message = $timestamp . $raw_body;
$hash_signature = hash_hmac('sha512', $message, $public_key);
if (!hash_equals($signature, $hash_signature)) {
    header("HTTP/1.1 401 Unauthorized", true, 401);
    die("Request is not properly authorized!");
}
$return_array = [
    'type' => 1,
];
echo json_encode($return_array);
?>

When I put the address the file is uploaded to and try to save the changes, Discord says the following:

Validation errors: interactions_endpoint_url: The specified interactions endpoint URL could not be verified.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

This is a method that works for me on PHP 8.1.

Pass in the headers and raw JSON body, and it returns an array with the response code and payload to send back through whatever you are using to handle the response. Note the response must be JSON encoded.

$discord_public is the public key from the Discord app.

public function authorize(array $headers, string $body, string $discord_public): array
{
    $res = [
        'code' => 200,
        'payload' => []
    ];

    if (!isset($headers['x-signature-ed25519']) || !isset($headers['x-signature-timestamp'])) {
        $res['code'] = 401;
        return $res;
    }

    $signature = $headers['x-signature-ed25519'];
    $timestamp = $headers['x-signature-timestamp'];

    if (!trim($signature, '0..9A..Fa..f') == '') {
        $res['code'] = 401;
        return $res;
    }

    $message = $timestamp . $body;
    $binary_signature = sodium_hex2bin($signature);
    $binary_key = sodium_hex2bin($discord_public);

    if (!sodium_crypto_sign_verify_detached($binary_signature, $message, $binary_key)) {
        $res['code'] = 401;
        return $res;
    }

    $payload = json_decode($body, true);
    switch ($payload['type']) {
        case 1:
            $res['payload']['type'] = 1;
            break;

        case 2:
            $res['payload']['type'] = 2;
            break;

        default:
            $res['code'] = 400;
            return $res;
    }

    return $res;
}
over 4 years ago · Santiago Trujillo Report

0

For completeness, adding the missing code to @Coder1 answer:

<?php

$payload = file_get_contents('php://input');
$result = endpointVerify($_SERVER, $payload, 'discord app public key');
http_response_code($result['code']);
echo json_encode($result['payload']);

function endpointVerify(array $headers, string $payload, string $publicKey): array
{
    if (
        !isset($headers['HTTP_X_SIGNATURE_ED25519'])
        || !isset($headers['HTTP_X_SIGNATURE_TIMESTAMP'])
    )
        return ['code' => 401, 'payload' => null];

    $signature = $headers['HTTP_X_SIGNATURE_ED25519'];
    $timestamp = $headers['HTTP_X_SIGNATURE_TIMESTAMP'];

    if (!trim($signature, '0..9A..Fa..f') == '')
        return ['code' => 401, 'payload' => null];

    $message = $timestamp . $payload;
    $binarySignature = sodium_hex2bin($signature);
    $binaryKey = sodium_hex2bin($publicKey);

    if (!sodium_crypto_sign_verify_detached($binarySignature, $message, $binaryKey))
        return ['code' => 401, 'payload' => null];

    $payload = json_decode($payload, true);
    switch ($payload['type']) {
        case 1:
            return ['code' => 200, 'payload' => ['type' => 1]];
        case 2:
            return ['code' => 200, 'payload' => ['type' => 2]];
        default:
            return ['code' => 400, 'payload' => null];
    }
}
over 4 years ago · Santiago Trujillo Report

0

According to Discord documentation, interactions endpoint must do two things:

  • Your endpoint must be prepared to ACK a PING message
  • Your endpoint must be set up to properly handle signature headers--more on that in Security and Authorization

The first part is very simple:

So, to properly ACK the payload, return a 200 response with a payload of type: 1:

Thus, you need to return JSON object and not an array (maybe that's one of the problems with your code).

Additionally, endpoint must be able to respond to invalid requests as:

We will also do automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your interactions URL in the future and alert you via email and System DM.

One thing I noticed is that Discord sends their headers lowercased! Another problem is that you use:

$raw_body = file_get_contents('php://input'); that doesn't look right.

Finally, if you know node.JS take a look at my working example: https://github.com/iaforek/discord-interactions

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!