Contentful Introduces Request Verification for the App Framework

Published
December 17, 2020
Category

Developers

Building a complex app for Contentful using the App Framework doesn’t have to be complicated. Request Verification provides powerful functionality out of the box to further ensure the integrity of your data and app communications.

When you develop an app for Contentful using the App Framework, you are creating a single page application that runs inside an iframe in the Contentful web app. Apps are highly useful for extending the default Contentful experience and implementing your own business solutions. While many apps can be as simple as a button, some apps do more advanced things like modify user input or make network calls to backends which implement custom business logic.

Frontend apps that make network calls sometimes need a way to authenticate themselves with a backend. Authentication can be necessary for securing communication; however, it is hard to implement from scratch. To solve this problem, we’ve created an easy way for developers like you to make secure communication between your frontend and backend possible when working with apps. We call this “Request Verification.”

When a request is verified, the original message’s integrity is secured using a secret you provide. You may have heard of this practice before as “signing a request.” Signed requests are a security mechanism used throughout many software applications. When a request is signed, any backend that knows the signing secret can use it to verify that an incoming request came from your app while being displayed in the Contentful web app. This also means that your backend can reject any request made to it that isn’t verified, or whose signature is invalid.

Request verification has many immediate use cases. For example, backends can allow users to access private information in a secure fashion, restrict who is able to perform certain actions and ensure that only apps you’ve allowed are communicating with your backend.

Looking at an Example

Let’s say you build an app that presents a button in the Contentful web app that can trigger a flow to modify other data in Contentful via a backend. However, you want to make sure that the user who is initiating this request is indeed someone who is able to access your Contentful data. When you implement request verification in your frontend app, you are ensuring that when this request is sent to your backend, you can verify its authenticity and either allow the request to continue or block it. Below is a request being signed in JavaScript using our SDK.

javascript
// Define your request
const req = {
  method: 'POST',
  url: 'https://your-app-backend.com/event-handler',
  headers: {
    'Content-Type': 'application/json',
    'X-some-header': 'some-value',
  },
  body: JSON.stringify({ user: 'id' }),
};

// Get and apply the signed headers
const { additionalHeaders } = await sdk.space.signRequest({
  method: req.method,
  headers: req.headers,
  body: req.body,
  path: new URL(req.url).pathname,
});
Object.assign(req.headers, additionalHeaders);

// Send the request as you usually would
fetch(req.url, req);

In the code, we see that as we formulate our network request, we can use the SDK method called signRequest to create an object which contains all our data and will include additional headers which contain the signature and other useful security data. It is then up to you to ensure your backend properly reads the incoming request and either allows or denies it based on the authenticity of the signature. Let’s take a look at the following example:

javascript
const express = require('express');
const { verifyRequest } = require('@contentful/node-apps-toolkit');
const app = express();
app.use(
  express.json({
    type: 'application/vnd.contentful.management.v1+json',
  })
);

// This is the secret key that was generated by you or Contentful.
// Note it is not best practice to hardcode secret keys into code.
// We are doing it here for demonstration purposes only.
const secret = 'your-64-char-secret';

// This can be the endpoint configured to be called for app events
app.post('/event-handler', (req, res) => {
  const canonicalRequest = {
    path: req.path,
    headers: req.headers,
    method: req.method,
    body: JSON.stringify(req.body),
  };
  try {
    const isValid = verifyRequest(secret, canonicalRequest);
    if (!isValid) {
      res.status(403).send('Unauthorized');
    }
  } catch (e) {
    console.error(e);
    res.status(403).send('Unauthorized');
  }

  // ... rest of handler code
});

const port = 9000;
app.listen(port, () =>
  console.log(`App listening at http://localhost:${port}`)
);

Here we are using the popular Node.js framework Express to run a server and listen for data on an endpoint. When we receive an event, we use the node-apps-toolkit function verifyRequest to verify the incoming message. Keep in mind, you can accomplish request verification using any backend language that can receive HTTP requests. We have further technical documentation on how a backend app can verify that a request is authentic in any programming language.

Further Information

If you are writing an app that makes network calls to a backend you own, the App Framework and JavaScript SDK provide functions that do many things for you automatically. In the case of request verification, we provide the functions that do all the integrity securing for you so all you have to worry about is writing your business logic. 

Since request verification is usually used in more advanced cases, I am always open to answering questions or hearing about your specific situation. If you want to chat about what you're working on, feel free to ping me on our community Slack where I work with fellow developers as we take steps to make our App Framework more durable for everyone.

About the author

Don't miss the latest

Get updates in your inbox
A monthly newsletter to help you build better digital experiences with Contentful.
add-circle arrow-right remove style-two-pin-marker subtract-circle