Skip to content

Commit

Permalink
feat: add IsValidWebhookSignature function for validating webhook s…
Browse files Browse the repository at this point in the history
…ignature
  • Loading branch information
TheUnderScorer committed Jun 19, 2024
1 parent 5e78297 commit a5bf13d
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 1 deletion.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,43 @@ func main() {
// Do something with unsealed response, e.g: send it back to the frontend.
fmt.Println(unsealedResponse)
}
```

## Webhook signing

This SDK provides utility method for verifing the HMAC signature of the incoming webhook request.
Install the webhook dependency as below:
```shell
go get github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook
```

Then you can use below code to verify signature:
```go
package main

import (
"github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook"
)

func main() {
// Your webhook signing secret.
secret := "secret"

// Request data. In real life scenerio this will be the body of incoming request
data := []byte("data")

// Value of the "fpjs-event-signature" header.
header := "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db"

isValid := webhook.CheckHeader(header, data, secret)

if !isValid {
panic("Invalid signature")
}
}
```


To learn more, refer to example located in [example/sealedResults.go](./example/sealedResults.go).

## Documentation for API Endpoints
Expand Down Expand Up @@ -269,6 +304,10 @@ Class | Method | HTTP request | Description
- [SealedResults](docs/SealedResults.md)
- [DecryptionKey](docs/DecryptionKey.md)

## Documentation for webhooks

- [DecryptionKey](docs/Webhook.md)

## Author

support@fingerprint.com
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

## **CheckHeader**

> bool CheckHeader(header string, data []byte, secret string)
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** | **[]byte** | Body of the request from which above header was extracted. | |
| **secret** | **secret** | Your generated secret used to sign the request. | |
22 changes: 22 additions & 0 deletions example/webhookSignature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook"
)

func main() {
// Your webhook signing secret.
secret := "secret"

// Request data. In real life scenerio this will be the body of incoming request
data := []byte("data")

// Value of the "fpjs-event-signature" header.
header := "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db"

isValid := webhook.CheckHeader(header, data, secret)

if !isValid {
panic("Invalid signature")
}
}
2 changes: 1 addition & 1 deletion generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

var files = []string{"README.md", "docs", ".swagger-codegen"}
var filesToKeep = []string{"docs/DecryptionKey.md", "docs/SealedResults.md"}
var filesToKeep = []string{"docs/DecryptionKey.md", "docs/SealedResults.md", "docs/Webhook.md"}
var pathPrefix = "sdk"

func main() {
Expand Down
33 changes: 33 additions & 0 deletions sdk/webhook/signing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package webhook

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strings"
)

func checkSignature(signature string, data []byte, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(data)
computedSignature := hex.EncodeToString(h.Sum(nil))
return signature == computedSignature
}

// CheckHeader 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
func CheckHeader(header string, data []byte, secret string) bool {
signatures := strings.Split(header, ",")

for _, signature := range signatures {
parts := strings.Split(signature, "=")
if len(parts) == 2 && parts[0] == "v1" {
hash := parts[1]
if checkSignature(hash, data, secret) {
return true
}
}
}

return false
}
39 changes: 39 additions & 0 deletions template/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,43 @@ func main() {
// Do something with unsealed response, e.g: send it back to the frontend.
fmt.Println(unsealedResponse)
}
```

## Webhook signing

This SDK provides utility method for verifing the HMAC signature of the incoming webhook request.
Install the webhook dependency as below:
```shell
go get github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook
```

Then you can use below code to verify signature:
```go
package main

import (
"github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook"
)

func main() {
// Your webhook signing secret.
secret := "secret"
// Request data. In real life scenerio this will be the body of incoming request
data := []byte("data")
// Value of the "fpjs-event-signature" header.
header := "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db"
isValid := webhook.CheckHeader(header, data, secret)
if !isValid {
panic("Invalid signature")
}
}
```


To learn more, refer to example located in [example/sealedResults.go](./example/sealedResults.go).

## Documentation for API Endpoints
Expand Down Expand Up @@ -211,6 +246,10 @@ Class | Method | HTTP request | Description
- [SealedResults](docs/SealedResults.md)
- [DecryptionKey](docs/DecryptionKey.md)

## Documentation for webhooks

- [DecryptionKey](docs/Webhook.md)

## Author

{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}}
Expand Down
50 changes: 50 additions & 0 deletions test/webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package test

import (
"github.com/fingerprintjs/fingerprint-pro-server-api-go-sdk/v5/sdk/webhook"
"github.com/stretchr/testify/assert"
"testing"
)

const secret = "secret"
const data = "data"

func TestCheckHeader(t *testing.T) {
const validHeader = "v1=1b2c16b75bd2a870c114153ccda5bcfca63314bc722fa160d690de133ccbb9db"

t.Run("With valid signature", func(t *testing.T) {
isEqual := webhook.CheckHeader(validHeader, []byte(data), secret)

assert.Equal(t, true, isEqual)
})

t.Run("With invalid header", func(t *testing.T) {
isEqual := webhook.CheckHeader("v2=invalid", []byte(data), secret)

assert.Equal(t, false, isEqual)
})

t.Run("With header without version", func(t *testing.T) {
isEqual := webhook.CheckHeader("invalid", []byte(data), secret)

assert.Equal(t, false, isEqual)
})

t.Run("With empty header", func(t *testing.T) {
isEqual := webhook.CheckHeader("invalid", []byte(data), secret)

assert.Equal(t, false, isEqual)
})

t.Run("With empty secret", func(t *testing.T) {
isEqual := webhook.CheckHeader(validHeader, []byte(data), "")

assert.Equal(t, false, isEqual)
})

t.Run("With empty data", func(t *testing.T) {
isEqual := webhook.CheckHeader(validHeader, []byte(""), secret)

assert.Equal(t, false, isEqual)
})
}

0 comments on commit a5bf13d

Please # to comment.