first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
# token-auth
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Computes BunnyCDN Pull Zone Token Authentication query strings — `?token=<urlSafeBase64(sha256(key + path + expires))>&expires=<unix>`, optionally with `&token_path=<prefix>` 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 `<pullZoneHostname>/<key>` 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=<urlSafeBase64>&expires=<unix>"
```
## Usage: Advanced
```ts
import { buildTokenAuthQuery } from "@suaveplan/storage-cdn-bunnycdn";
async function signedFolderUrl(hostname: string, folder: string, ttl: number): Promise<string> {
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<string>`
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.