-
Notifications
You must be signed in to change notification settings - Fork 22
Description
I was looking for a way to add access control using Cloudflare Zero Trust to restrict who can see my note via its Cloudflare Access Gateway.
I found a solution, but unfortunately, I am not very proficient in writing TypeScript or JavaScript. So I asked AI for help and developed my own implementation. The purpose of this issue is to see if someone can make a PR or to leave it here in case anyone else needs this solution.
However, this solution is not perfect for a workspace with many pages. If you don't include all the pages in the slugToPage section, there is a chance that your original Notion domain prefix might be revealed in the URL path. You could potentially use random strings or a UUID-like string to obscure the original Notion site domain, as it is still available to everyone by design.
Step 1
Follow the Readme instructions, using npx to generate your own code from the template.
Step 2
Continue to follow the instructions in the Readme to edit wrangler.toml and site-config.ts.
Step 3
Add the jose package to package.json to decode Cloudflare Access JWT tokens.
"dependencies": {
"notehost": "^1.0.33",
"jose": "^5.8.0"
},After adding this package to package.json, run npm install to install it.
Step 4
Modify src/index.ts and adapt the code. Here is an example. Replace YOUR_TEAM_DOMAIN_PREFIX with your own Cloudflare team domain prefix.
import { initializeReverseProxy, reverseProxy } from 'notehost'
import { jwtVerify } from 'jose';
import { SITE_CONFIG } from './site-config'
initializeReverseProxy(SITE_CONFIG)
async function getPublicKey(kid: string): Promise<Uint8Array | null> {
const response = await fetch('http://YOUR_TEAM_DOMAIN_PREFIX.cloudflareaccess.com/cdn-cgi/access/certs');
const { keys } = await response.json();
// Find the key that matches the "kid" (Key ID) in the JWT header
const key = keys.find((k: any) => k.kid === kid);
if (!key) return null;
return new Uint8Array(Buffer.from(key.x5c[0], 'base64'));
}
async function verifyJWT(jwt: string): Promise<boolean> {
try {
const { header } = jwtVerify(jwt, async (header) => {
const publicKey = await getPublicKey(header.kid);
if (!publicKey) throw new Error('Public key not found');
return publicKey;
});
// If no error is thrown, the JWT is valid
return true;
} catch (error) {
console.error('JWT verification failed:', error);
return false;
}
}
export default {
async fetch(request: Request): Promise<Response> {
const jwt = request.headers.get('Cf-Access-Jwt-Assertion');
if (!jwt) {
return new Response('Unauthorized', { status: 401 });
}
const isValid = await verifyJWT(jwt);
if (!isValid) {
return new Response('Unauthorized', { status: 401 });
}
return await reverseProxy(request)
},
}
Step 5
Deploy it to Cloudflare Workers, setup an self hosted application in zero trust using on the custom domain you deployed and verify if it works.
