CloudFront Paywall

Server side solution for paywalling

How it works

In the request to Naviga Web there will possibly be one of two headers. One header provides the authenticated users products. The other header lets you know if something went wrong when the paywall executed.

The absence of these headers indicates that the user is unauthenticated.

The name of the first header can be configured but the default value is p (short for products).

The name of the second header is Paywall-Error.

The reason behind doing this in CloudFront is that we now can use these headers as cache keys. By normalising the individual users requests to be in a subset of the p-header we get a small amount of cache keys and the end result is a faster response to authenticated users.

Developing in Naviga Web php

The decisions you need to consider:

  1. Does the incoming request have the Paywall-Error header?

    • No: Proceed to next step

    • Yes: Is the error a broken token or a paywall execution error?

      • Broken token: The user is to be considered as unauthenticated

      • Paywall execution error: How should I treat this request?

  2. Does the incoming request have the p-header?

    • No: The user is to be considered as unauthenticated

    • Yes: Check if the value grants access to the requested content

      • No: The customer may want to upgrade subscription to take part of the content?

      • Yes: Render premium content

The result of each branch in the tree will leave to a response that can be cached in CloudFront so other users with this set of header-values will get a fast response - whatever you decide to do.

Usage example

Here is an example on how you could use HTTP_P and HTTP_PAYWALL_ERROR in php.

function isLoggedIn(): bool
{
    if (isset($_SERVER['HTTP_PAYWALL_ERROR'])) {

        /** @todo Be sure to catch this exception and decide if you still want to show the content or not. */
        throw new RuntimeException("Paywall error: " . $_SERVER['HTTP_PAYWALL_ERROR']);
    }

    return isset($_SERVER['HTTP_P']);
}

function isAuthorizedFor(string $product): bool
{
    if (!isLoggedIn()) {
        return false;
    }

    /**
     * @todo Replace this with your own logic.
     *       The value of $_SERVER['HTTP_P'] depends entirely on the auth provider.
     *       It could be a string, an array, or something else.
     */
    return ($_SERVER['HTTP_P'] === $product) ? true : false;
}
single-article.php
try {
    if (isAuthorizedFor('my-subscription-name')) {
        /* @todo Show content */
    }
    else {
        if (!isLoggedIn()) {
            /* @todo Show "log in" link */
        }
        /* @todo Show paywall */
    }
} catch (RuntimeException $e) {
    /* @todo Log and notify yourselves about this ASAP! */
    /* @todo Maybe still show the content? */
}

Paywall execution errors

The reasons why you would get this error could be:

  • Execution time is exceeded

  • Configuration is missing

  • JWKS is non responsive or have no keys

In the event of any of these issues, you can't really blame the visitor to your site. We or the service has messed up in some way and your paying customers should not take the hit for that.

Customize cache headers

You are also in charge of setting the cache headers for these responses so CloudFront knows how long to keep the result until Naviga Web will get a new request with these headers. (There are defaults that you can stick with.)

Last updated