Skip to content

Commit

Permalink
Merge pull request #101 from fingerprintjs/feature/add-webhook-signin…
Browse files Browse the repository at this point in the history
…g-feature

Add webhook sign validating feature inter-768
  • Loading branch information
Orkuncakilkaya authored Aug 12, 2024
2 parents de05eb7 + bb27cfc commit 698f0a0
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 1 deletion.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,32 @@ All URIs are relative to your region's base URL.
| Europe | https://eu.api.fpjs.io |
| Asia | https://ap.api.fpjs.io |

## Webhook Signing

This SDK provides utility method for verifying the HMAC signature of the incoming webhook request.
You can use below code to verify signature:

```php
<?php

// Your webhook signing secret.
$webhookSecret = "secret";

// Request data. In real life scenerio this will be the body of incoming request
$webhookData = "data";

// Value of the "fpjs-event-signature" header.
$webhookHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";

$isValidWebhookSign = \Fingerprint\ServerAPI\Webhook\WebhookVerifier::IsValidWebhookSignature($webhookHeader, $webhookData, $webhookSecret);

if(!$isValidWebhookSign) {
fwrite(STDERR, sprintf("Webhook signature verification failed\n"));
exit(1);
}

```

## Endpoints


Expand Down Expand Up @@ -278,6 +304,10 @@ Class | Method | HTTP request | Description
- [Sealed](docs/Sealed/Sealed.md)
- [DecryptionKey](docs/Sealed/DecryptionKey.md)

## Documentation for webhooks

- [Webhook](docs/Webhook.md)

## Tests

To run the unit tests:
Expand Down
16 changes: 16 additions & 0 deletions docs/Webhook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Webhook

## **IsValidWebhookSignature**

> Fingerprint\ServerAPI\Webhook\WebhookVerifier::IsValidWebhookSignature(string $header, string $data, string $secret): bool
Verifies the HMAC signature extracted from the "fpjs-event-signature" header of the incoming request. This is a part of the webhook signing process, which is available only for enterprise customers.
If you wish to enable it, please [contact our support](https://fingerprint.com/support).

### Required Parameters

| Name | Type | Description | Notes |
|------------|------------|-----------------------------------------------------------|-------|
| **header** | **string** | Value of the `fpjs-event-signature` header. | |
| **data** | **string** | Body of the request from which above header was extracted. | |
| **secret** | **string** | Your generated secret used to sign the request. | |
11 changes: 11 additions & 0 deletions run_checks.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@
exit(1);
}

$webhookSecret = "secret";
$webhookData = "data";
$webhookHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";
$isValidWebhookSign = \Fingerprint\ServerAPI\Webhook\WebhookVerifier::IsValidWebhookSignature($webhookHeader, $webhookData, $webhookSecret);
if($isValidWebhookSign) {
fwrite(STDOUT, sprintf("\n\nVerified webhook signature\n"));
} else {
fwrite(STDERR, sprintf("\n\nWebhook signature verification failed\n"));
exit(1);
}

// Enable the deprecated ArrayAccess return type warning again if needed
error_reporting(error_reporting() | E_DEPRECATED);

Expand Down
2 changes: 1 addition & 1 deletion scripts/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ java -jar ./bin/swagger-codegen-cli.jar generate -t ./template -l php -i ./res/f

mv -f src/README.md ./README.md
mv -f src/composer.json composer.json
find ./docs -type f ! -name "DecryptionKey.md" ! -name "Sealed.md" -exec rm {} +
find ./docs -type f ! -name "DecryptionKey.md" ! -name "Sealed.md" ! -name "Webhook.md" -exec rm {} +
mv -f src/docs/* ./docs

if [ -z "$GITHUB_ACTIONS" ]; then
Expand Down
29 changes: 29 additions & 0 deletions src/Webhook/WebhookVerifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Fingerprint\ServerAPI\Webhook;

final class WebhookVerifier
{
public static function IsValidWebhookSignature(string $header, string $data, string $secret): bool
{
$signatures = explode(',', $header);
foreach ($signatures as $signature) {
$parts = explode('=', $signature);
if (2 === count($parts) && 'v1' === $parts[0]) {
$hash = $parts[1];
if (self::checkSignature($hash, $data, $secret)) {
return true;
}
}
}

return false;
}

private static function checkSignature(string $signature, string $data, string $secret): bool
{
$hash = hash_hmac('sha256', $data, $secret);

return hash_equals($hash, $signature);
}
}
30 changes: 30 additions & 0 deletions template/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,32 @@ All URIs are relative to your region's base URL.
| Europe | https://eu.api.fpjs.io |
| Asia | https://ap.api.fpjs.io |
## Webhook Signing
This SDK provides utility method for verifying the HMAC signature of the incoming webhook request.
You can use below code to verify signature:
```php
<?php
// Your webhook signing secret.
$webhookSecret = "secret";
// Request data. In real life scenerio this will be the body of incoming request
$webhookData = "data";
// Value of the "fpjs-event-signature" header.
$webhookHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";
$isValidWebhookSign = \Fingerprint\ServerAPI\Webhook\WebhookVerifier::IsValidWebhookSignature($webhookHeader, $webhookData, $webhookSecret);
if(!$isValidWebhookSign) {
fwrite(STDERR, sprintf("Webhook signature verification failed\n"));
exit(1);
}

```

## Endpoints


Expand Down Expand Up @@ -226,6 +252,10 @@ Class | Method | HTTP request | Description
- [Sealed](docs/Sealed/Sealed.md)
- [DecryptionKey](docs/Sealed/DecryptionKey.md)

## Documentation for webhooks

- [Webhook](docs/Webhook.md)

## Tests

To run the unit tests:
Expand Down
51 changes: 51 additions & 0 deletions test/WebhookVerifierTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Fingerprint\ServerAPI;

use Fingerprint\ServerAPI\Webhook\WebhookVerifier;
use PHPUnit\Framework\TestCase;

class WebhookVerifierTest extends TestCase
{
private $secret = "secret";
private $data = "data";

public function testWithValidSignature()
{
$validHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";
$result = WebhookVerifier::IsValidWebhookSignature($validHeader, $this->data, $this->secret);
$this->assertTrue($result, "With valid signature");
}

public function testWithInvalidHeader()
{
$result = WebhookVerifier::IsValidWebhookSignature("v2=invalid", $this->data, $this->secret);
$this->assertFalse($result, "With invalid header");
}

public function testWithHeaderWithoutVersion()
{
$result = WebhookVerifier::IsValidWebhookSignature("invalid", $this->data, $this->secret);
$this->assertFalse($result, "With header without version");
}

public function testWithEmptyHeader()
{
$result = WebhookVerifier::IsValidWebhookSignature("", $this->data, $this->secret);
$this->assertFalse($result, "With empty header");
}

public function testWithEmptySecret()
{
$validHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";
$result = WebhookVerifier::IsValidWebhookSignature($validHeader, $this->data, "");
$this->assertFalse($result, "With empty secret");
}

public function testWithEmptyData()
{
$validHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db";
$result = WebhookVerifier::IsValidWebhookSignature($validHeader, "", $this->secret);
$this->assertFalse($result, "With empty data");
}
}

0 comments on commit 698f0a0

Please sign in to comment.