AWS CloudFront Integration

Integration with AWS CloudFront uses Lambda@Edge to perform the filtering function. The below example is specific to AWS CloudFront but the general idea is the same regardless of the CDN you are using. You'll deploy and configure a lightweight client that talks to our "auth service" to handle the checks. The specifics of that client might change depending on your CDN, but the core process of verifying access stays consistent.

Prerequisites

  1. AWS account with permissions to deploy Lambda@Edge functions.
  2. Access to the paywalls.net API.
  3. Key values for paywalls.net:
    • PAYWALLS_CLOUD_API_KEY
    • PAYWALLS_PUBLISHER_ID

Steps

  1. Register as a Publisher: Sign up for an account at paywalls.net and receive your API key and publisher ID.

  2. Create a Lambda@Edge Function: In your AWS account, create a new Lambda@Edge function. This will be the filter that checks content access.

    • All Lambda@Edge Functions must be created in the us-east-1 region. AWS will automatically distribute these across regions.
    • The IAM execution role associated with the Lambda@Edge function must have a Policy that allows access to the log streams in all regions. Use a Resource arn like "arn:aws:logs:*:<YOUR-AWS-ACCOUNT-ID>:log-group:/aws/lambda/us-east-1.pw-filter:*"
    • The IAM execution role associated with the Lambda@Edge function must have a trust relationship with both the service principals lambda.amazonaws.com and edgelambda.amazonaws.com allowing those principles to assume the execution role. This trust relationship is different than an AWS policy.
    • CloudFront supports both 'CloudFront Functions' and 'Lambda@Edge Functions'. It is important to use the Lambda@Edge Function capability so that API access to the paywalls.net services is supported. (see: AWS Documentation)
  3. Deploy the Code:

    • Copy and adapt (if needed) the provided code examples into your project.
    • Configure API Keys and IDs. Add your PAYWALLS_API_KEY and PAYWALLS_PUBLISHER_ID within the supplied config.js file.
      • AWS Lambda@Edge does not support configuring environment variables. The common approach is to use a config.js file in your code package.
    • Install dependencies: Run npm install to install the required packages.
    • Deploy the code package to the Lambda@Edge function. A deploy script is provided in the example package.json.
  4. Configure CloudFront: Configure your CloudFront distribution to trigger the function on viewer requests. The function will intercept requests, check with the paywalls.net "auth service," and either allow or deny access based on the bot’s authorization.

    • For testing, you may want to use a path pattern that matches a sub-set of your content. Create a new Behavior with your desired settings, including the function association for viewer requests that specifies the Lambda@Edge Function you've created.
    • You must use a numbered version of the Lambda@Edge function, not $LATEST or aliases.
  5. Test the Integration: Make requests to your CloudFront distribution and verify that the Lambda@Edge function is correctly filtering bot-like requests.

    • Lambda@Edge logs are stored and accessed on a per-region basis. This can make troubleshooting more complex, as you may need to check logs in multiple regions. In addition, the log stream name will include the base us-east-1 and the Lambda function name. For example: us-east-1:xxxxxxxxxxxx:log-group:/aws/lambda/us-east-1.pw-filter.

Building and Deploying the Project

AWS Lambda@Edge does not automatically install configured npm packages; these must be built into an archive uploaded to AWS. The following package.json is an example that contains a build and deploy script to create a zip file with dependencies.

{
  "name": "cloudfront-lambda",
  "type": "module",
  "version": "1.0.0",
  "description": "AWS CloudFront Lambda@Edge function for filtering bot-like requests using paywalls.net authorization services.",
  "main": "index.mjs",
  "scripts": {
    "build": "zip -r pw-filter.zip .",
    "deploy": "npm run build && aws lambda update-function-code --region us-east-1 --function-name pw-filter --zip-file fileb://pw-filter.zip"
  },
  "dependencies": {
    "@paywalls-net/filter": "^1.2.0"
  },
  "license": "Apache-2.0"
}

Example Code

config.js

export const config = {
    PAYWALLS_API_KEY: '<YOUR-API-KEY>',
    PAYWALLS_PUBLISHER_ID: '<YOUR-PUBLISHER-ID>'
};

index.mjs

/**
 * Example publisher-hosted client code for AWS CloudFront that
 * filters bot-like requests by using paywalls.net authorization services.
 * Configuration variables:
 *  PAYWALLS_API_KEY
 *  PAYWALLS_PUBLISHER_ID
 * See AWS docs : https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html
 */
import { init } from '@paywalls-net/filter';
import { config } from './config.js';

// Initialize the paywalls.net handler for CloudFront
const handleRequest = await init('cloudfront', config);

// The main handler function for AWS Lambda@Edge
export const handler = async (event, context) => {
    // Authenticate and authorize the request
    let pw_response = await handleRequest(event, context);
    if (pw_response) {
        // If the handler returns a response, return it
        return pw_response;
    }

    // If no response is returned, proceed with the original request
    const request = event.Records[0].cf.request;
    return request;
};

Monitor Logs

Use AWS CloudWatch to monitor the function's logs and ensure proper operation.

  • Note: Logs are region-specific, so you may need to check multiple regions for troubleshooting.