HOW TO: Use Cloudflare Workers to enable Basic Auth on a subdirectory

March 4, 2020

Click on the Workers tab and then Add Route:

Enter the path to the subdirectory you want to protect, including a * at the end to ensure all recursive content is protected (ie. 403page.com/private_directory/*). Click Save.

Next, click Manage Workers and then Create a Worker. In the script editor on the left, paste the following code - changing the constants on the top 2 lines (YOUR_USER_NAME and YOUR_PASSWORD) to whatever you please:

const NAME = "YOUR_USER_NAME"
const PASS = "YOUR_PASSWORD"

/**
 * RegExp for basic auth credentials
 *
 * credentials = auth-scheme 1*SP token68
 * auth-scheme = "Basic" ; case insensitive
 * token68     = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
 */

const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/

/**
 * RegExp for basic auth user/pass
 *
 * user-pass   = userid ":" password
 * userid      = *<TEXT excluding ":">
 * password    = *TEXT
 */

const USER_PASS_REGEXP = /^([^:]*):(.*)$/

/**
 * Object to represent user credentials.
 */

const Credentials = function(name, pass) {
  this.name = name
  this.pass = pass
}

/**
 * Parse basic auth to object.
 */

const parseAuthHeader = function(string) {
  if (typeof string !== 'string') {
    return undefined
  }

  // parse header
  const match = CREDENTIALS_REGEXP.exec(string)

  if (!match) {
    return undefined
  }

  // decode user pass
  const userPass = USER_PASS_REGEXP.exec(atob(match[1]))

  if (!userPass) {
    return undefined
  }

  // return credentials object
  return new Credentials(userPass[1], userPass[2])
}


const unauthorizedResponse = function(body) {
  return new Response(
    null, {
      status: 401,
      statusText: "'Authentication required.'",
      body: body,
      headers: {
        "WWW-Authenticate": 'Basic realm="User Visible Realm"'
      }
    }
  )
}

/**
 * Handle request
 */

async function handle(request) {
  const credentials = parseAuthHeader(request.headers.get("Authorization"))
  if ( !credentials || credentials.name !== NAME ||  credentials.pass !== PASS) {
    return unauthorizedResponse("Unauthorized")
  } else {
    return fetch(request)
  }
}

addEventListener('fetch', event => {
  event.respondWith(handle(event.request))
})

Click Save & Deploy.

Then head back to the main Workers page where you set the route before. You'll notice the route you added earlier (403page.com/private_directory/* in my case) shows the following message:

Workers are disabled on this route

Click Edit and select the worker you just made from the dropdown to assign it to that route and click Save.

Within about 10-30 seconds, you'll see a Basic Auth challenge on the route path you specified:

Sorted!

Credit to dommmel & JonasJasas for putting the rule together here.