import { timingSafeEqual } from "./crypto.js";
import { generateOpaqueToken } from "./tokens.js";
//#region src/csrf.ts
/**
* `@platform-modules/util/csrf` — CSRF defense primitives (pure, zero-dep).
*
* Two independent, composable mechanisms for protecting cookie-session state-changing requests:
*   - double-submit token (issue + constant-time verify)
*   - Origin / Sec-Fetch-Site same-origin check
*
* Bearer-token requests carry no ambient cookie credential → CSRF is N/A for them; the host
* SHOULD exempt token-authenticated requests BEFORE calling these guards.
*
* Predicates return `boolean` (util convention); fail-closed — any uncertainty yields `false`.
* The host responds 403 on `false`.
*/
/** Issue an opaque CSRF token for the double-submit pattern (32 random bytes, base64url). */
function issueCsrfToken() {
	return generateOpaqueToken(32);
}
/**
* Double-submit verify: the token stored in a cookie vs the token echoed back in a request
* header/body field. Constant-time compare (timingSafeEqual). Fail-closed: either side
* null/undefined/empty → `false` (before the compare — a present-vs-absent check is not a
* secret-timing leak). Equal-length, equal-value → `true`.
*/
function verifyDoubleSubmit(cookieToken, submittedToken) {
	if (!cookieToken || !submittedToken) return false;
	return timingSafeEqual(cookieToken, submittedToken);
}
/**
* Origin-based CSRF defense for state-changing requests. Returns `true` IFF same-origin:
*   - `Origin` header present → must be ∈ `allowedOrigins` (exact string match)
*   - `Origin` absent         → `Sec-Fetch-Site` ∈ {'same-origin','none'}
*   - neither present         → `false` (fail-closed)
*
* Bearer-token requests have no ambient cookie credential — CSRF is N/A; the host SHOULD exempt
* them before calling this.
*/
function isSameOrigin(headers, allowedOrigins) {
	const h = headers instanceof Headers ? headers : new Headers(headers);
	const origin = h.get("origin");
	if (origin !== null) return allowedOrigins.includes(origin);
	const site = h.get("sec-fetch-site");
	return site === "same-origin" || site === "none";
}
//#endregion
export { isSameOrigin, issueCsrfToken, verifyDoubleSubmit };
