# token-auth _(no submodules)_ ## Purpose Computes BunnyCDN Pull Zone Token Authentication query strings — `?token=&expires=`, optionally with `&token_path=` for path-scoped tokens. The signing scheme is BunnyCDN's documented Pull Zone protection: concatenate the per-Pull-Zone signing key with the path and expiry, SHA-256 hash, and URL-safe-base64 encode (no padding). The adapter calls this from `signUrl`; consumers never invoke it directly unless they need to sign a URL outside the adapter's standard `/` pattern. ## Features - SHA-256 via `crypto.subtle` — runs in Node 18+, Bun, Deno, browsers, Workers. - URL-safe base64 output: `+` → `-`, `/` → `_`, no `=` padding (matches BunnyCDN's verifier). - Optional `tokenPath` query parameter for prefix-scoped tokens. - Throws `STORAGE_CDN_BUNNYCDN.TOKEN_AUTH_KEY_MISSING` when the key is empty or missing. - Pure async function — no state, no side effects. ## Usage: Basic ```ts import { buildTokenAuthQuery } from "@suaveplan/storage-cdn-bunnycdn"; const query = await buildTokenAuthQuery({ tokenAuthKey: "per-zone-secret", path: "/photos/a.jpg", expires: Math.floor(Date.now() / 1000) + 60, }); // query === "?token=&expires=" ``` ## Usage: Advanced ```ts import { buildTokenAuthQuery } from "@suaveplan/storage-cdn-bunnycdn"; async function signedFolderUrl(hostname: string, folder: string, ttl: number): Promise { const expires = Math.floor(Date.now() / 1000) + ttl; const tokenPath = `/${folder}/`; const query = await buildTokenAuthQuery({ tokenAuthKey: process.env["BUNNY_TOKEN_KEY"]!, path: tokenPath, expires, tokenPath, }); return `https://${hostname}${tokenPath}${query}`; } ``` ## API Reference ### `interface TokenAuthInput` `{ tokenAuthKey, path, expires, tokenPath? }`. `path` is the absolute path on the Pull Zone (with leading `/`). `expires` is Unix seconds. `tokenPath` enables prefix-scoped tokens (the token authorises every URL under the prefix until expiry). ### `buildTokenAuthQuery(input): Promise` Returns the `?token=...&expires=...` query string to append to the absolute edge URL. Throws `STORAGE_CDN_BUNNYCDN.TOKEN_AUTH_KEY_MISSING` on empty `tokenAuthKey`. ## Implementation Notes - BunnyCDN's signing scheme is SHA-256 (not HMAC). The secret is concatenated directly with `path + expires` and hashed; the URL-safe-base64 encoding makes the result safe for query-parameter inclusion. - Padding is stripped because BunnyCDN's verifier rejects tokens with `=` characters; this is a documented quirk of their implementation. - `expires` is Unix seconds — BunnyCDN's standard everywhere. Passing milliseconds produces verifier rejections that read like "token expired" but are actually "token format wrong". - The function is async because `crypto.subtle.digest` is Promise-returning. The underlying SHA-256 is fast (≪1ms). - The shipped tests assert determinism (same inputs → identical output), format correctness, and difference under varied inputs; byte-exact reference vectors are the responsibility of integration tests against a live BunnyCDN account. - `btoa` is part of the universal runtime baseline (Node 18+, Bun, Deno, browsers, Workers); a Node-only `Buffer` fallback is not needed.