Upgrading to Version 6
  • 05 Mar 2024
  • Dark
    Light

Upgrading to Version 6

  • Dark
    Light

Article Summary

Benefits

Installable NPM Package

Prior to version 6, the enforcer was provided as a pre-bundled worker rather than an installable library, which meant it was difficult to distinguish between the enforcer (HUMAN code) and the Cloudflare worker (your code). These were separated within the bundle provided, but differentiating between these components was not always intuitive. This made customizations and updates complicated and cumbersome.

Version 6 of the HUMAN Cloudflare Enforcer provides the enforcer as an installable NPM library rather than a pre-bundled worker. This allows for a cleaner separation between HUMAN code and your Cloudflare worker code, more flexibility in how you write and deploy your Cloudflare worker, and easier upgrades going forward.

TypeScript Support

For those who prefer TypeScript, version 6 of the HUMAN Cloudflare Enforcer also provides TypeScript definition files for the Cloudflare library. Adding TypeScript support reduces the likelihood of errors when configuring and integrating the enforcer into your Cloudflare worker.

Upgrade Process

Updating the Configuration

If you're upgrading from version 4.x or 5.x of the Cloudflare worker, many of the configuration fields have remained the same.

The following configuration fields have been modified:

  • px_identifier_header_name has been renamed px_client_uuid_header_name.
  • px_additional_activity_handler has been changed. The function signature has been modified from (pxCtx, config) => Promise<void> to (config, context, request) => Promise<void>.
  • px_enrich_custom_parameters has been changed. The function signature has been modified from (pxCtx, request) => Promise<CustomParameters> to (config, request) => Promise<CustomParameters>.
  • px_cors_custom_preflight_handler has been changed. The function signature has been modified from (request) => Promise<Response> to (request) => Promise<{ body: string, headers: Record<string, string[]>, status: number }>.
  • px_custom_cors_block_response_headers has been renamed to px_cors_create_custom_block_response_headers. The function signature has also been modified from (request) => Promise<Record<string, string>> to (request) => Promise<Record<string, string[]>>.

The following configuration fields have been removed:

  • px_send_async_activities_enabled
  • px_first_party_xhr_enabled
  • px_deferred_activities
  • px_snippet_provider_enabled
  • px_snippet_provider_cache_ttl_seconds
  • px_csp_enabled
  • px_csp_no_updates_max_interval_minutes
  • px_bot_enforcer
  • px_code_enforcer
  • px_login_credentials_http_body_size_limit
  • px_filter_by_header
  • px_custom_verification_handler
  • px_pre_enforce_handler

Updating the Cloudflare Worker Integration

In prior versions of the Cloudflare Enforcer, a default main function was provided that called three separate functions in the px module. See the code sample below for the v5.x integration main function.

const customConfig = {
    "px_app_id": "APP_ID",
    "px_cookie_secret": "COOKIE_SECRET",
    "px_auth_token": "AUTH_TOKEN"
};

const customFunctions = (function() {
    return {
        //px_additional_activity_handler: (pxCtx, config) => { },
        //px_custom_verification_handler: (pxCtx, request) => { },
        //px_enrich_custom_parameters: async (pxCtx, request) => { },
        //px_pre_enforce_handler: (pxCtx, request) => { },
        //px_custom_cors_block_response_headers: (request) => { },
        //px_cors_custom_preflight_handler: (request) => { },
        //px_custom_is_sensitive_request: async (request) => { }
    };
})();

// service worker syntax
(function() {
    async function handleFetchEvent(event) {
        // Initialize PX module by setting the configuration
        px.setConfig(customConfig, customFunctions);
        
        // Call to PerimeterX to receive request or response
        let { request, response } = await px.middleware(event.request, event);

        // PX first party or block response
        if (response) {
            return response;
        }

        // Sending the request returned from px.middleware (not original req) since
        // it includes additional headers that the origin server may require
        response = await fetch(request);

        // Send reporting activities to PerimeterX asynchronously,
        // only awaits if code defender functionality enabled
        response = await px.postFetch(request, response, event);
        return response;
    }

    addEventListener('fetch', event => {
        event.respondWith(handleFetchEvent(event));
    });
})();

Notice the three px functions that are invoked:

  • setConfig() to set the HUMAN enforcer configuration,
  • middleware() to invoke the HUMAN enforcement logic, and
  • postFetch() to perform any response-based logic.

In principle, the integration for version 6 remains the same, only with the functions provided by the HumanSecurityEnforcer class:

  • initialize() to set the HUMAN enforcer configuration and initialize the enforcer
  • enforce() to invoke the HUMAN enforcement logic, and
  • postEnforce() perform any response-based logic.

Note that the parameters and return values for these functions have changed from previous versions. They differ slightly between Service Worker and ES Modules syntaxes as well.

See the code samples and explanatory comments below.

Service Worker Syntax

import { HumanSecurityEnforcer } from '@humansecurity/cloudflare-enforcer';

// define an enforcer configuration however you see fit
const config = {
    px_app_id: '<APP_ID>',
    px_auth_token: '<AUTH_TOKEN>',
    px_cookie_secret: '<COOKIE_SECRET>',
    // ...
};

async function handleEvent(event) {
    // provide the enforcer configuration as an argument and await the returned Promise
    // to receive an instance of the HumanSecurityEnforcer
    const enforcer = await HumanSecurityEnforcer.initialize(config);

    // call enforce
    const retVal = await enforcer.enforce(event);

    // if enforce returned a response, return that response
    if (retVal instanceof Response) {
        return retVal;
    }

    // retrieve the resource from the cache or origin server
    // make sure to use the value returned from enforce
    const response = await fetch(retVal);

    // call postEnforce and return the resulting response
    return await enforcer.postEnforce(event, response);
}

addEventListener('fetch', (event) => {
    event.respondWith(handleEvent(event));
});

ES Modules Syntax

import { HumanSecurityEnforcer } from "@humansecurity/cloudflare-enforcer";

const config = {
    px_app_id: '<APP_ID>',
    px_auth_token: '<AUTH_TOKEN>',
    px_cookie_secret: '<COOKIE_SECRET>',
    // ...
};

export default {
    async fetch(request, env, ctx) {
        // create a new enforcer
        const enforcer = await HumanSecurityEnforcer.initialize(config, env);

        // call enforce
        const retVal = await enforcer.enforce(ctx, request);

        // if enforce returned a response, return that response
        if (retVal instanceof Response) {
            return retVal;
        }

        // retrieve the resource from the cache or origin server
        // make sure to use the value returned from enforce
        const response = await fetch(retVal);

        // call postEnforce and return the resulting response
        return await enforcer.postEnforce(ctx, response);
    },
};

Was this article helpful?

What's Next