Webhooks

This guide helps you set up webhooks to receive updates from Passbase

Why Do I Need A Webhook?

Once your user successfully completed a Verification, Passbase will verify and process the identity Resource(s) asynchronously, as the processing is happening at a later time and not directly in response to your code’s execution. Passbase will notify your system about changes in regards to status updates of the Verification so your integration can take subsequent steps.

This is where a webhook comes in place. We can notify you once we have finished processing all the Resources. You can set up webhooks in your dashboard under https://app.passbase.com/settings/webhooks

Configuration

To properly receive incoming webhooks, your server should provide an endpoint supporting:

  • TLS encryption version 1.2

  • POST requests as raw in text/plain or application/json format

You can use a service like e.g. https://webhook.site/ to try out and receive webhooks from your dashboard. For local testing, you can use a CLI tool such as ngrok to forward the requests to your local server.

Events

Our webhook service supports two events, which describe the status of the Verification once it has been processed by our system:

  • VERIFICATION_COMPLETED: The Verification has been processed and is ready to be reviewed in your dashboard

  • VERIFICATION_REVIEWED: The Verification has been approved or rejected in your dashboard. This can directly be triggered if you are using our auto-accept/reject feature based on a threshold.

Make sure you're not relying on the order of the events inside your application due to networking latency. It could happen that they arrive at the same time. Though you could assume that once you receive the VERIFICATION_REVIEWEDevent, VERIFICATION_COMPLETED has fired as well.

Security

Webhooks are a crucial node to ensure the functionality of the Passbase integration and, as such, they can be the target of a malicious user aiming to disrupt the service or impersonate your users. In order to protect your application from these threats, you must include a 32 bytes-long secret in your webhook configuration, which will be used to encrypt the request body.

You'll find examples below on how to decipher the incoming webhook request. We assume the variable request_body to be coming from your backend framework. The cipher initialization vector will be sent over in the first 16 bytes of the response.

Javascript
Python
Ruby
PHP
Go
Javascript
const crypto = require("crypto")
// Your response from the webhook.
const request_body = ""
// In the example here it is already a string. In prod it'll be a raw file format of
// text/plain, hence you gotta do request_body.toString() in the next line
const encrypted_result = Buffer.from(request_body, 'base64')
const iv = encrypted_result.slice(0,16)
const cipher = crypto.createDecipheriv('aes-256-cbc', "YOUR_WEBHOOK_SECRET", iv)
const decrypted_result_bytes = Buffer.concat([cipher.update(encrypted_result.slice(16)), cipher.final()])
const decrypted_result = decrypted_result_bytes.toString()
const json_result = JSON.parse(decrypted_result)
Python
from Crypto.Cipher import AES
import base64
import json
request_body = "" # Your response from the webhook
encrypted_result = base64.b64decode(encrypted_webhook)
iv = encrypted_result[:16]
cipher = AES.new("YOUR_WEBHOOK_SECRET".encode("utf8"), AES.MODE_CBC, iv)
decrypted_result_bytes = cipher.decrypt(encrypted_result[16:])
decrypted_result = bytes.decode(
decrypted_result_bytes[:len(decrypted_result_bytes)-16])
json_result = json.loads(decrypted_result)
Ruby
require 'openssl'
require 'base64'
require 'json'
request_body = "" # Your response from the webhook
encrypted_result = Base64.decode64(request_body)
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.decrypt
cipher.key = "YOUR_WEBHOOK_SECRET"
cipher.iv = encrypted_result[0..15]
decrypted_result = cipher.update(encrypted_result[16..-1]) + cipher.final
json_result = JSON.parse(decrypted_result)
PHP
// OpenSSL is already integrated in PHP
$request_body = ""; // Your response from the webhook
$key = "YOUR_WEBHOOK_SECRET";
$encrypted_result = base64_decode($request_body);
$iv = substr($encrypted_result, 0, 16);
$decrypted_result = openssl_decrypt(substr($encrypted_result, 16), 'AES-256-CBC', $key, $options=OPENSSL_RAW_DATA, $iv);
$json_result = json_decode($decrypted_result, true);
Go
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
)
func main() {
var webhook_response = "" // Your response from the webhook
key := []byte("YOUR_WEBHOOK_SECRET")
decrypted_webhook := decrypt(key, webhook_response)
fmt.Printf(decrypted_webhook)
}
func decrypt(key []byte, cryptoText string) string {
cipherText, err := base64.StdEncoding.DecodeString(cryptoText)
if err != nil {
panic(err)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
if len(cipherText) < aes.BlockSize {
panic("cipherText too short")
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(cipherText, cipherText)
return fmt.Sprintf("%s", cipherText)
}

Using Firebase as Backend

If you are using Google Firebase as your backend, you can also receive webhooks by using Google Cloud Functions. You can learn here how to set up your Firebase project for using Cloud Functions here.

After you have set it up, you can expose an express server as one cloud function endpoint. This sample project shows you the easiest way to set this up. But in the end, you need to configure a cloud function to listen to incoming post requests. In the example below we listen to widgets/webhooks:

import * as functions from "firebase-functions";
const crypto = require("crypto");
const express = require("express");
const cors = require("cors");
const app = express();
const admin = require("firebase-admin");
admin.initializeApp();
app.use(cors({ origin: true }));
// Receive post request with Express Endpoint
app.post("/webhooks", (req: any, res: any) => {
const incomingWebhook = req.body;
// Do what you want to do with the webhook
console.log(incomingWebhook);
// (Optional) - Decrypt webhook if you set up encryption
decryptWebhookTest(incomingWebhook)
.then((webhook) => {
// Do what you want to do with the webhook
console.log(webhook);
})
.catch((err: any) => console.log(err));
res.sendStatus(200);
});
const decryptWebhookTest = async (webhookResponse: string) => {
// If needed
};
// Expose Express API as a single Cloud Function:
exports.widgets = functions.https.onRequest(app);

And by this, you will get a webhook endpoint that looks like this:

https://us-central1-<your-project-id>.cloudfunctions.net/widgets/webhooks

If you set now in your Passbase project this as your endpoint under Settings/Webhooks and trigger a function by e.g. finishing a Verification or rejecting/approving one, it will fire the Google Cloud Function like you can see in the Logs. Now you can do what you want with it, e.g. ping our API or update your user.

If you are struggling, just use our sample project from Github and adjust your own project code to it. This article from the official Firebase documentation provides additional details.

Result

Verification is completed

The VERIFICATION_COMPLETED event will be triggered when a Verification is completed and awaits to be reviewed (accepted or rejected) or will now be automatically accepted/rejected by our automation feature.

{
"event": "VERIFICATION_COMPLETED",
"key": "b76e244e-26a3-49ef-9c72-3e599bf0b5f2",
"status": "pending",
"created": 1582628711,
"updated": 1582628999,
"processed": 1582628999,
}

Verification reviewed

The VERIFICATION_REVIEWED event will be triggered when a Verification status has changed. This will happen if the Verification has been approved or rejected in your dashboard, or the automation has automatically accepted or rejected based on a threshold set by you (e.g. >90%).

After this event, it is the time to use our API now, to retrieve the details of this verification with the Identity key. Please take a look at the API section for this and also compare our best practice implementation.

{
"event": "VERIFICATION_REVIEWED",
"key": "b76e244e-26a3-49ef-9c72-3e599bf0b5f2",
"status": "pending",
"created": 1582628711,
"updated": 1582628999,
"processed": 1582628999,
}

You can find a further description of the response values below:

Key

Data type

Description

event

string

The type of event that triggered this webhook.

key

string

The UUID of the Identity which triggered this webhook. This will help your to link it back to your user as well as query our backend API about the details of the Identity.

status

string

Whether the Verification was approved or declined. This is triggered by you accepting/rejecting the Verification in the dashboard or Passbase automation does it for you.

created

integer

a UNIX timestamp when the Verification was created

updated

integer

a UNIX timestamp when the Verification was updated.

processed

integer

a UNIX timestamp when the Verification was processed by Passbase.