Signing an API call

Prerequisites

The what? And the why?

This workflow is a derivation of the Webpage Forsensic API workflow. It allows a company to sign data flows coming from single-page applications (SPA), native applications, or IoT (Internet of Things) devices. In this workflow, the signatures happen within a forensic environment using a proprietary and patented methodology following the standard ISO/IEC 27037:2012. The key difference between this workflow and the Webpage Forensic API is that in this case the context that produces the signature is not acquired forensically.

So here there is no prerendered HTML, but just the oURL.

The payload

A minimal payload for this workflow should be something like this:

{
  "oURL": "https://company.com/api/call",
  "variation": "no_html",
  "actAsProxy": true
}

As you can see, this payload specifies variation: โ€œno_htmlโ€œ, to tell Web Trust that this signature uses the Data Forensic API workflow.

We are also using actAsProxy: true, as explained here.

The mechanism here is fairly similar to the one in click-to-sign signatures; the only difference is that there is no context, therefore no prerendered HTML. Thus, in this case, the man-in-the-middle is done to intercept the API call to oURL, without any user interaction.

Since the call to oURL departs from the userโ€™s device, forensic data is still gathered to create the proof. Just that this proof is weaker with respect to the Webpage Forensic API workflow because it lacks the context (i.e., what the user sees).

Making the call

Assume you have an API used by an SPA, and you want to sign one of the calls to an endpoint. For instance,

Example
import express from "express"; import {join} from "path"; const app = express(); const port = 3000; ... app.post("/important/stuff", (req, res) => { // Authorisation part of the endpoint // Stuff you do when this endpoint is called }); ... app.listen(port, () => { console.log(`Example app listening on port ${port}`) });

In this example, you have an endpoint that is called from your SPA, being POST /important/stuff .

To do so, we need to split this endpoint into two parts:

  1. One that creates the interaction and redirects to the fUrl

  2. And one that will do the stuff you originally did, the oURL.

Example
import express from "express"; import {join} from "path"; const app = express(); const port = 3000; ... app.post("/important/stuff", (req, res) => { // Authorisation part of the endpoint const res = await fetch("https://api.webtrust.kopjra.com/v1/interactions", { headers: { "Content-Type": "application/json", "Accept": "application/json", "Authorization": "Basic NjhiOTkxMjEtYWIxNC00YzUwLWFlMzItNDgzZmQ4MWVkNWJkOnlBRG1YTDZ6IzNjVm4tWURPNmJsTWkybGY0MEpNMEdUcFpLVH5+Ujc1eUNEN3ZZVGU2fmJTdTJEOUZwNHFEVjl4RWhtVmRZSw==", }, method: "POST", body: JSON.stringify({ "oURL": "https://company.com/internal/important/stuff", "actAsProxy": true, "variation": "full", }), }); // Avoiding error management res.redirect((await res.json()).fUrl); }); app.post("/internal/important/stuff", (req, res) => { // Authorisation part of the endpoint // Stuff you do when this endpoint is called }); ... app.listen(port, () => { console.log(`Example app listening on port ${port}`) });

In this example, when the SPA calls POST /important/stuff, this will happen:

  • Verification of authorisation as you did before;

  • Creations of a Web Trust interaction ;

  • Redirection of the request to the fUrl;

  • The SPA goes to the fUrl, that lands in the forensic environment;

  • Since actAsProxy is true, the forensic environment will proxy the request to the oURL , in this case: https://company.com/internal/important/stuff ;

  • The response of the proxied request is sent to the SPA.

Protecting oURL

Remember to protect the endpoint you use for oURL. In both actAsProxy: true and actAsProxy: false we will forward the payload, query parameters, and headers.

That way your authorisation mechanism can be used at the endpoint in oURL.