/**
* Cloudflare Worker: RSA-OAEP decrypting API, token gated
*/
export interface Env {
ALLOWED_ORIGIN: string;
PRIVATE_KEY: string;
CLIENT_TOKEN: string;
}
const JSON_CT = { "Content-Type": "application/json" } as const;
const BASE_HEADERS = {
"Cache-Control": "no-store",
"X-Content-Type-Options": "nosniff",
"Cross-Origin-Resource-Policy": "same-site",
} as const;
function cors(allowed: string) {
return {
"Access-Control-Allow-Origin": allowed,
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, X-Client-Token",
};
}
async function importPrivateKey(pkcs8Pem: string): Promise<CryptoKey> {
// Convert PEM to ArrayBuffer and import via Web Crypto API
const pkcs8 = pemToPkcs8Bytes(pkcs8Pem);
return await crypto.subtle.importKey(
"pkcs8", pkcs8,
{ name: "RSA-OAEP", hash: "SHA-256" },
false, ["decrypt"]
);
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const allowedOrigin = env.ALLOWED_ORIGIN;
// 1. Strict Origin Check
const origin = request.headers.get("Origin");
if (!origin || origin !== allowedOrigin) return jerr(403, allowedOrigin);
// 2. Token Gate
const tok = request.headers.get("X-Client-Token");
if (!tok || tok !== env.CLIENT_TOKEN) return jerr(401, allowedOrigin);
// 3. Decrypt Payload
try {
const payload = await request.json();
const key = await importPrivateKey(env.PRIVATE_KEY);
const ctBytes = base64urlToBytes(payload.ciphertext);
const plainBuf = await crypto.subtle.decrypt(
{ name: "RSA-OAEP" }, key, ctBytes
);
// 4. Transform & Return
const plainJson = new TextDecoder().decode(plainBuf);
const body = JSON.parse(plainJson);
return new Response(JSON.stringify({
ok: true,
transformed: transform(body.text, body.mode)
}), {
headers: { ...BASE_HEADERS, ...cors(allowedOrigin), ...JSON_CT }
});
} catch (e) {
return jerr(400, allowedOrigin);
}
},
} satisfies ExportedHandler<Env>;