HTTP Basic Authentication
Shows how to restrict access using the HTTP Basic schema.
/*** Shows how to restrict access using the HTTP Basic schema.* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication* @see https://tools.ietf.org/html/rfc7617**/import { Buffer } from "node:buffer";const encoder = new TextEncoder();/*** Protect against timing attacks by safely comparing values using `timingSafeEqual`.* Refer to https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#timingsafeequal for more details* @param {string} a* @param {string} b* @returns {boolean}*/function timingSafeEqual(a, b) {const aBytes = encoder.encode(a);const bBytes = encoder.encode(b);if (aBytes.byteLength !== bBytes.byteLength) {// Strings must be the same length in order to compare// with crypto.subtle.timingSafeEqualreturn false;}return crypto.subtle.timingSafeEqual(aBytes, bBytes);}export default {/**** @param {Request} request* @param {{PASSWORD: string}} env* @returns*/async fetch(request, env) {const BASIC_USER = "admin";// You will need an admin password. This should be// attached to your Worker as an encrypted secret.// Refer to https://developers.cloudflare.com/workers/configuration/secrets/const BASIC_PASS = env.PASSWORD ?? "password";const url = new URL(request.url);switch (url.pathname) {case "/":return new Response("Anyone can access the homepage.");case "/logout":// Invalidate the "Authorization" header by returning a HTTP 401.// We do not send a "WWW-Authenticate" header, as this would trigger// a popup in the browser, immediately asking for credentials again.return new Response("Logged out.", { status: 401 });case "/admin": {// The "Authorization" header is sent when authenticated.const authorization = request.headers.get("Authorization");if (!authorization) {return new Response("You need to login.", {status: 401,headers: {// Prompts the user for credentials."WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',},});}const [scheme, encoded] = authorization.split(" ");// The Authorization header must start with Basic, followed by a space.if (!encoded || scheme !== "Basic") {return new Response("Malformed authorization header.", {status: 400,});}const credentials = Buffer.from(encoded, "base64").toString();// The username & password are split by the first colon.//=> example: "username:password"const index = credentials.indexOf(":");const user = credentials.substring(0, index);const pass = credentials.substring(index + 1);if (!timingSafeEqual(BASIC_USER, user) ||!timingSafeEqual(BASIC_PASS, pass)) {return new Response("You need to login.", {status: 401,headers: {// Prompts the user for credentials."WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',},});}return new Response("🎉 You have private access!", {status: 200,headers: {"Cache-Control": "no-store",},});}}return new Response("Not Found.", { status: 404 });},};
/*** Shows how to restrict access using the HTTP Basic schema.* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication* @see https://tools.ietf.org/html/rfc7617**/import { Buffer } from "node:buffer";const encoder = new TextEncoder();/*** Protect against timing attacks by safely comparing values using `timingSafeEqual`.* Refer to https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#timingsafeequal for more details*/function timingSafeEqual(a: string, b: string) {const aBytes = encoder.encode(a);const bBytes = encoder.encode(b);if (aBytes.byteLength !== bBytes.byteLength) {// Strings must be the same length in order to compare// with crypto.subtle.timingSafeEqualreturn false;}return crypto.subtle.timingSafeEqual(aBytes, bBytes);}export default <ExportedHandler<{ PASSWORD: string }>>{async fetch(request, env) {const BASIC_USER = "admin";// You will need an admin password. This should be// attached to your Worker as an encrypted secret.// Refer to https://developers.cloudflare.com/workers/configuration/secrets/const BASIC_PASS = env.PASSWORD ?? "password";const url = new URL(request.url);switch (url.pathname) {case "/":return new Response("Anyone can access the homepage.");case "/logout":// Invalidate the "Authorization" header by returning a HTTP 401.// We do not send a "WWW-Authenticate" header, as this would trigger// a popup in the browser, immediately asking for credentials again.return new Response("Logged out.", { status: 401 });case "/admin": {// The "Authorization" header is sent when authenticated.const authorization = request.headers.get("Authorization");if (!authorization) {return new Response("You need to login.", {status: 401,headers: {// Prompts the user for credentials."WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',},});}const [scheme, encoded] = authorization.split(" ");// The Authorization header must start with Basic, followed by a space.if (!encoded || scheme !== "Basic") {return new Response("Malformed authorization header.", {status: 400,});}const credentials = Buffer.from(encoded, "base64").toString();// The username and password are split by the first colon.//=> example: "username:password"const index = credentials.indexOf(":");const user = credentials.substring(0, index);const pass = credentials.substring(index + 1);if (!timingSafeEqual(BASIC_USER, user) ||!timingSafeEqual(BASIC_PASS, pass)) {return new Response("You need to login.", {status: 401,headers: {// Prompts the user for credentials."WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',},});}return new Response("🎉 You have private access!", {status: 200,headers: {"Cache-Control": "no-store",},});}}return new Response("Not Found.", { status: 404 });},};