Introduction

Descope enables you to enrich your Descope flows by allowing you to integrate with 3rd party vendors to fetch information or push data to the vendor during the execution of the flow utilizing connectors. Within the Descope console, connectors are configured here. This area of our knowledge base contains guides around how to configure and utilize Descope connectors within your flows.

This article will cover how to test connectors against an API running on localhost as well as how to implement HMAC Validation.

Testing Connectors using localhost

For developers, there might be scenarios where they wish to test an API running on their localhost from an external source. This is particularly useful for preliminary testing of Descope's Connectors using a locally hosted API. This guide will walk you through the steps on how to achieve this.

Prerequisites:

  1. A working REST API running on your localhost.
  2. A service/tool that can temporarily expose your localhost API to the internet. Some popular options are:

Step-by-step Guide:

Deploy your API using a chosen service

Choose any of the above-mentioned services to expose your local API to the internet. Once exposed, note down the public URL as it will be used in the next step.

Configuring the HTTP Connector

Navigate to the Descope's HTTP Connector configuration page and fill in the required parameters:

  • Connector Name: Provide a unique name for your connector. This assists in distinguishing it, especially when multiple connectors are derived from the same template.
  • Connector Description: Briefly explain the purpose of this connector.
  • Base URL: Input the consistent section, or the root, of your API's URL. This should start with either http:// or https://. Use the public URL from Step 1.
  • Authentication Type: Descope supports various methods to authenticate with your service. Choose the method that suits your API:
    • Bearer Token: Used for access keys such as JWTs.
    • API Key: This usually involves a key-value pair.
    • Basic Authentication: The traditional username and password method.
    • None: Select this if your API doesn't require any authentication.
  • Headers (Optional): Some APIs need specific headers, usually key-value pairs, to provide more details about the impending action.
  • HMAC Secret (Optional): HMAC is a symmetric key method for message signing. The provided secret will be used to sign the payload. The outcome signature will be sent in the x-descope-webhook-s256 header. The recipient service should use this secret to validate the payload's integrity and authenticity by verifying the supplied signature.
  • Trust Any Certificate: By default, this option is turned off. If enabled, the client will overlook any certificate errors. While convenient for testing, it's crucial to remember that this is an insecure choice for production.
Twilio connector widget input

Running the Flow

Once you've configured the HTTP Connector and added it in the Flow Editor, you can run your flow locally. During this process, the conector should be integrated seamlessly.

Final Steps

After you've completed your tests, consider deploying your API to a more stable service. Once done, revisit the HTTP Connector configuration page to update it with the new parameters from your permanent API deployment.

Note: The HTTP Connector being tested in this manner is a particular example of using tunnels to test locally deployed services. This method can be used to test any connector, or API, that requires a public URL.

Hash-Based Message Authentication Code (HMAC) Authentication Type

HMAC is a specific type of authentication code involving a cryptographic hash function and a secret key. It may be used to simultaneously verify both the data integrity and the authentication of a message, as with any MAC. Descope allows you to use HMAC to sign the payload of your HTTP Connector. The outcome signature will be sent in the x-descope-webhook-s256 header. The recipient service should use this secret to validate the payload's integrity and authenticity by verifying the supplied signature.

Validating the HMAC Signature

To validate the HMAC signature, the code could look something like this:

import { RawBodyRequest } from '@nestjs/common';
import crypto from 'crypto';
import { Request } from 'express';

function validate(raw: RawBodyRequest<Request>): boolean {
    const hmac = crypto.createHmac('sha256', process.env.HMAC_SECRET_KEY);
    hmac.update(Buffer.from(raw.rawBody)); // Ensure rawBody is a Buffer
    hmac.end();
    const calculated = hmac.read().toString('base64');
    const signature = raw.get('x-descope-webhook-s256') ?? 'invalid or nonexisting signature';

    // Convert both the calculated signature and the received signature to Buffers
    const signatureBuffer = Buffer.from(signature, 'base64');
    const calculatedBuffer = Buffer.from(calculated, 'base64');

    // Ensure both Buffers are of the same length
    if (signatureBuffer.length !== calculatedBuffer.length) {
        return false;
    }

    return crypto.timingSafeEqual(signatureBuffer, calculatedBuffer);
}

Or this:

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

const app = express();
const PORT = 3000;

// Middleware to parse JSON payloads
app.use(bodyParser.json());

function verifyHmacSignature(payload, secret, sentHmac) {
 const computedHmac = crypto
   .createHmac('sha256', secret)
   .update(JSON.stringify(payload))
   .digest('base64');

 return sentHmac === computedHmac;
}

app.post('/webhook-endpoint', (req, res) => {
 const payload = req.body; // This is the parsed body
 const headers = req.headers; // This contains all headers

 // Assuming the sent HMAC is transmitted in the header 'x-hmac-signature'
 const sentHmac = headers['x-descope-webhook-s256'];

 // Use your secret here
 const secret = 'YOUR_SECRET';

 if (!verifyHmacSignature(payload, secret, sentHmac)) {
     res.status(403).send('Invalid HMAC');
 }

 // Serve request
});

app.listen(PORT, () => {
 console.log(`Server is listening on port ${PORT}`);
});

Mocking an HMAC Signature

If you wish to test the HMAC signature validation, you can use the following code to generate a valid signature for a given payload and secret, then include it in the headers. Note that Descope creates the HMAC signature from a JSON string, not the raw post body.

import fetch from 'node-fetch';
import crypto from 'crypto';

    it('HMAC Signature', async () => {
      fetch.mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({}),
      });

      const hmacSecret = 'YOUR_SECRET';
      const payload = {
        p1: 'v1',
      };

      await handler({
        command: 'post',
        configuration: {
          baseUrl: 'https://example.com',
          hmacSecret,
        },
        args: {
          endpoint: '/api',
          payload,
        },
      });

      // ensure that the signature is correct
      const expectedSignature = crypto
        .createHmac('sha256', hmacSecret)
        .update(JSON.stringify(payload))
        .digest('base64');

      expect(fetch).toHaveBeenCalledWith(
        'https:/example.com/api',
        expect.objectContaining({
          method: 'POST',
          body: JSON.stringify({
            p1: 'v1',
          }),
          headers: expect.objectContaining({
            'x-descope-webhook-s256': expectedSignature,
          }),
        }),
      );
    });

Connector JSON Array Handling

Connectors may return their payload as an array of items. In the example below, we showcase configuring the connector action and utilizing the returned array data to update a user's properties, such as their phone number.

This flow takes the user's email and then queries an API to verify if the email exists as a user in a 3rd party system through the HTTP Connector, and if so, update their user information to include their phone number.

Configure Connector Action

The first step is to configure your connector action. You will later need to use the Context Key of the connector's data; in this example, the data is stored under connectors.httpResult.

HTTP Connector Configuration Retrieving Custom Data

Get Data from Array

Next, we can access the data from the returned array from other Descope actions or conditionals, before we try to access it though, here is an example of it's contents.

{
  "connectors": {
    "httpResult": {
      "body": {
        "data": [
          {
            "user@email.com": {
              "email": "user@email.com",
              "email_verified": true,
              "name": {
                "first": "John",
                "last": "Doe"
              },
              "phone": {
                "number": "+12223334455",
                "verified": true
              }
            }
          }
        ]
      }
}
Now we will access the array; in this example, we will use the Update User/ Properties action to get data from the array and update the user's phone number and verification status.Based on the above returned data under the connectors.httpResult context key, we will need to parse out the phone details per the below paths using jquery.
  • jq:.connectors.httpResult.body.data[0][].phone.verified
  • jq:.connectors.httpResult.body.data[0][].phone.number

Update User Based on HTTP Connector JSON Array Data

Connecting the Flow

Once you have configured the connector action and the condition or other action to parse the data via jquery, you can now connect the flow. Here is the flow in this example once completed.

Example Flow Overview Showing User Property Update From Returned JSON Array Data

Post Flow Execution

Below you can see the user's details are updated after the flow execution.

User Properties Without Phone Number Prior To Update From Returned JSON Array Data

User Properties Without Phone Number After Update From Returned JSON Array Data