How to do basic authentication with Cloudflare Workers
Hello!
My name is Hide, the ramen king of Beyond Osaka Office.
This is my 13th post.
Last time, I wrote about setting a policy that allows you to view only specific hosted zones using AWS IAM!
The setting method is relatively easy, but if you make a mistake in the settings, you may not be able to create it, or you may end up showing parts that should not be shown.
If you are interested, please check it out as we will explain how to set it up in an easy-to-understand manner.
[AWS IAM] Policy settings that allow viewing only in specific hosted zones
overview
I would like to restrict access using Basic Authentication. . . .
I'm using Cloudflare, and I don't want to put a load on the origin, so
I'd like to implement basic authentication with Cloudflare. . . .
picture? But what should I do with this?
Have you ever thought about the above?
Normally, when implementing Basic authentication, you would probably implement it using Apache, Nginx, etc.
Even though I am using Cloudflare to reduce the load on the origin, I was wondering if there was anything I could do about this with Cloudflare,
but by using Cloudflare Workers, I was able to achieve basic authentication.
It's very convenient because it doesn't put any load on the origin with Cloudflare in the first stage!
The steps are very simple, so let's try it! !
Prerequisite knowledge
What is Cloudflare Workers?
Cloudflare is one of the largest networks of CDN services in the world. Cloudflare's CDN service is used to speed up web content from all over the world.
Cloudflare Workers is a serverless service developed by Cloudflare.
Build web features and applications without configuring or maintaining infrastructure when deployed globally across the Cloudflare network's 275+ data centers worldwide.
Regarding the language, it will be implemented using JavaScript, TypeScript, etc.
It is a very convenient service because you can use SSL, WAF, CDN, etc. while building an application with Cloudflare.
What is Basic Authentication?
Access restrictions can be implemented using middleware such as Apache and Nginx.
You can authenticate with a username and password, which can be used when you want to restrict access to only some users.
Our engineers are introducing how to implement basic authentication with Nginx, so
if you are interested, please take a look!
Setting procedure
*This assumes that Cloudflare initial setup and record registration have been completed.
① Click Home > Workers > Overview
②Enter any subdomain name and click [Settings]
③Select a price plan
*Since this is a demo, we have selected Free, but please select according to your situation.Cloudflare
Workers pricing plan
④Click [Create service]
⑤Enter the service name and click [Create service]
⑥Click [Quick Edit]
⑦Paste the basic authentication code and click [Save and Deploy]
*Please copy and paste the basic authentication code
*When editing, please refer to
sample code *You can change the user and password using the constants below.
const BASIC_USER = ''; const BASIC_PASS = '';
・Basic authentication code
/** * 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 * * A user-id containing a colon (":") character is invalid, as the * first colon in a user-pass string separates user and password. */ const BASIC_USER = ''; const BASIC_PASS = ''; /** * Receives a HTTP request and replies with a response. * @param {Request} request * @returns {Promise<Response> } */ async function handleRequest(request) { const { protocol, pathname } = new URL(request.url); // In the case of a Basic authentication, the exchange // MUST happen over an HTTPS (TLS) connection to be secure. if ('https:' !== protocol || 'https' !== request.headers.get('x-forwarded-proto')) { throw new BadRequestException('Please connect using Https communication') ; } // The "Authorization" header is sent when authenticated. if (request.headers.has('Authorization')) { // Throws exception when authorization fails. const { user, pass } = basicAuthentication(request); verifyCredentials( user, pass); return fetch(request) } // Not authenticated. return new Response('You need a user and password to log in', { status: 401, headers: { // Prompts the user for credentials. ' WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"', }, }); } /** * Throws exception on verification failure. * @param {string} user * @param { string} pass * @throws {UnauthorizedException} */ function verifyCredentials(user, pass) { if (BASIC_USER !== user) { throw new UnauthorizedException('Username or password is incorrect'); } if (BASIC_PASS !== pass) { throw new UnauthorizedException('Username or password is incorrect'); } } /** * Parse HTTP Basic Authorization value. * @param {Request} request * @throws {BadRequestException} * @returns {{ user: string, pass: string }} */ function basicAuthentication(request) { const Authorization = request.headers.get('Authorization'); const [scheme, encoded] = Authorization.split(' '); // The Authorization header must start with Basic, followed by a space. if (!encoded || scheme !== 'Basic') { throw new BadRequestException('Bad authentication header'); } // Decodes the base64 value and performs unicode normalization. / / @see https://datatracker.ietf.org/doc/html/rfc7613#section-3.3.2 (and #section-4.2.2) // @see https://dev.mozilla.org/docs/Web /JavaScript/Reference/Global_Objects/String/normalize const buffer = Uint8Array.from(atob(encoded), character => character.charCodeAt(0)); const decoded = new TextDecoder().decode(buffer).normalize(); // The username & password are split by the first colon. //=> example: "username:password" const index = decoded.indexOf(':'); // The user & password are split by the first colon and MUST NOT contain control characters. // @see https://tools.ietf.org/html/rfc5234#appendix-B.1 (=> "CTL = %x00-1F / %x7F") if (index === - 1 || /[\0-\x1F\x7F]/.test(decoded)) { throw new BadRequestException('The authentication value is invalid. '); } return { user: decoded.substring(0, index), pass: decoded.substring(index + 1), }; } function UnauthorizedException(reason) { this.status = 401; this.statusText = 'Unauthorized' ; this.reason = reason; } function BadRequestException(reason) { this.status = 400; this.statusText = 'Bad Request'; this.reason = reason; } addEventListener('fetch', event => { event.respondWith( handleRequest(event.request).catch(err => { const message = err.reason || err.stack || 'Unknown Error'; return new Response(message, { status: err.status || 500, statusText: err .statusText || null, headers: { 'Content-Type': 'text/plain;charset=UTF-8', // Disables caching by default. 'Cache-Control': 'no-store', // Returns the "Content-Length" header for HTTP HEAD requests. 'Content-Length': message.length, }, }); }) ); });
*The following pop-up will be displayed, so click [Save and Deploy].
*You can preview it if you want to check the operation.
⑧ Click [Add route] in Home > Verified Domains > Workers Routes
⑨Enter the following information and click [Save]
Root: examle-test.com/*
*Specify the domain (FQDN) for which you want to set up basic authentication
*You can also specify wildcards
Service: basic-test
Environment: produciton
*Service and environment were added in step 7 You can check it on the code screen
*Once saving is complete, it will be displayed in a list.
⑩Operation confirmation
・Login screen
*Basic authentication is not valid for domains other than the specified domain (FQDN).
summary
How was it?
By using Cloudflare Workers, we were able to easily implement basic authentication even in the free tier!
By using this, you can prevent content interruptions due to changes in Apache/Nginx settings and accidents caused by writing errors.
You can also take full advantage of Cloudflare without putting any load on your origin.
If you are using Cloudflare and want to implement basic authentication, please refer to this!