Once your user successfully completes 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. Passbase can notify you once we have finished processing all the Resources. You can set up webhooks in your developer dashboard's webhook section.
You can find more information about Passbase's integration best-practice in the Overview & Integration Guide section. Below is a graph that summarizes and displays which stage of the processing flow the Passbase system will send a webhook to you.
You can either follow the integration guide or watch the video tutorial that shows how to setup a webhook endpoint in NodeJS. Please be aware that some property or function names might have slightly changed with newer versions. Make sure to compare your implementation with the latest code snippets here in the documentation.
To properly receive incoming webhooks, your server should provide an endpoint supporting:
TLS encryption version 1.2
POST requests are either raw in text/plain if encrypted or application/json if no encryption is activated
You can use a service like e.g. https://webhook.site/ to test receiving webhooks from your developer dashboard. For local testing, you can use a CLI tool such as ngrok
to forward the requests to your local development server.
Our webhook service supports two events, which describe the status of a Verification. Those are:
VERIFICATION_COMPLETED
: The Verification has been completed processing and is ready to be reviewed in your developer dashboard.
VERIFICATION_REVIEWED
: The Verification has been approved
or declined
in your developer dashboard.
Make sure you're not relying on the order of the events inside your application due to networking latency. It is possible that both events can arrive at the same time. Though you can assume that once you receive the VERIFICATION_REVIEWED
event, VERIFICATION_COMPLETED
has fired as well.
pending
, this is the default status of a VERIFICATION_COMPLETED
event, the Verification is now displayed in your developer dashboard.
approved
, this is the status when you approve a Verification manually in your developer dashboard or when it passed the auto-review threshold.
declined
, this is the status when you decline a Verification manually in your developer dashboard or when it failed to pass the auto-review threshold.
If your project has automation thresholds enabled, it is possible that a VERIFICATION_COMPLETED
event contains an approved
or declined
status based on the threshold.
The VERIFICATION_COMPLETED
event will be triggered when a Verification has been completed and awaits to be reviewed (accepted or declined) or was automatically accepted/declined by our automation feature. The value of the status property can either be pending
if the verification lands in manual review, or approved
or, declined
if you have automation on.
{"event": "VERIFICATION_COMPLETED","key": "b76e244e-26a3-49ef-9c72-3e599bf0b5f2","status": "pending","created": 1582628711,"updated": 1582628999,"processed": 1582628999,}
The VERIFICATION_REVIEWED
event will be triggered when a Verification status has changed. This will happen if the Verification has been approved or declined in your dashboard, or the automation has automatically approved or declined based on a threshold set by you (e.g. >80%). The value of the status property can be approved
or declined
.
After this event, it is time to use the Passbase API, to retrieve the details of this verification with the identity access key
. Please take a look at the API section and integration best practices for more information.
{"event": "VERIFICATION_REVIEWED","key": "b76e244e-26a3-49ef-9c72-3e599bf0b5f2","status": "approved","created": 1582628711,"updated": 1582628999,"processed": 1582628999,}
You can find a further description of the response values below:
Key | Data type | Description |
|
| The type of event that triggered this webhook. |
|
| The UUID of the Identity which triggered this webhook. This will help you link back to your user as well as query our backend API for the details of the Identity. |
|
| Whether the Verification was approved or declined. This is triggered by you accepting/declining the Verification in the dashboard or Passbase automation does it for you. |
|
| UNIX timestamp, when the Verification was created |
|
| UNIX timestamp, when the Verification was updated. |
| integer | UNIX timestamp, when the Verification was processed by Passbase. |
Webhooks are a crucial node to ensure the functionality of your integration, 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.
Below you'll find an example of how to decipher an incoming webhook request. We assume the variable request_body
is the response you received via the webhook. The cipher initialization vector will be sent over in the first 16 bytes of the response.
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 lineconst 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)
from Crypto.Cipher import AESimport base64import 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)​json_result = json.loads(decrypted_result)
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.decryptcipher.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)
// 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);
package mainimport ("crypto/aes""crypto/cipher""encoding/base64""fmt")​func main() {var webhook_response = "" // Your response from the webhookkey := []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)}
The example server in the next section also shows a working version how to decrypt webhooks.
You can find an example where we set up a webhook endpoint using NodeJS on our Github here.
But a webhook endpoint is nothing else than a simple POST
endpoint of a server, like in the example below:
app.post("/passbase-webhooks", (req, res) => {processWebhook(req);res.status(200).send("Success");});​const processWebhook = (request) => {const webhook = webhookHelper.decryptWebhookIfNeeded(request);console.log(webhook);​switch (webhook.event) {case "VERIFICATION_COMPLETED":// Do logic here for VERIFICATION_COMPLETED eventbreak;case "VERIFICATION_REVIEWED":// Do logic here for VERIFICATION_REVIEWED eventbreak;default:console.log("Couldn't process webhook event");}};
If you are using Google Firebase as your backend, you can also receive webhooks by using Google Cloud Functions. You can learn how to set up your Firebase project for using Cloud Functions here.
After you have set it up, you can expose cloud function as an express server endpoint. This sample project shows you the easiest way to set this up. To sum up, you need to configure a cloud function to listen to incoming post requests. In the example below, we listen to an endpoint called 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 Endpointapp.post("/webhooks", (req: any, res: any) => {const incomingWebhook = req.body;// Do what you want to do with the webhookconsole.log(incomingWebhook);​// (Optional) - Decrypt webhook if you set up encryptiondecryptWebhookTest(incomingWebhook).then((webhook) => {// Do what you want to do with the webhookconsole.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 this as your wehook endpoint under your Passbase Webhook Settings and trigger a function by e.g. finishing a Verification or approving/declining one, it will fire a webhook to the Google Cloud Function and you can see it in the Firebase 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.