Browser SDK
Reference for the BlockAlt browser script: creating a client, getting tokens, preparing early, cancelling, progress, forms and errors.
On this page
Load the script #
<script src="https://blockalt.click/v1/assets/blockalt.c8b2d9539dbbc932999adb0f51a36bddd4e99899f543e1505818841878cf9813.js"
integrity="sha384-pblByFDqcJxRfhDh18GH3gRsWy9ZaS4AjDc6OpozzQqvkuYvyin+4BWcKXxA+bN8"
crossorigin="anonymous"></script>The script defines one global, BlockAlt. It sets no cookies, uses no browser storage and talks only to apiUrl, without credentials or referrer: one request for the signed configuration and its challenge, one to exchange the solved challenge for a token, with no CORS preflight. See installation for the integrity hash and keys.
Forms without code #
Configure the client on the script tag and mark each protected form with its action:
<script src="https://blockalt.click/v1/assets/blockalt.c8b2d9539dbbc932999adb0f51a36bddd4e99899f543e1505818841878cf9813.js"
integrity="sha384-pblByFDqcJxRfhDh18GH3gRsWy9ZaS4AjDc6OpozzQqvkuYvyin+4BWcKXxA+bN8" crossorigin="anonymous"
data-site-key="ba_pk_your_public_site_key"
data-api-url="https://blockalt.click"
data-config-public-keys="MCowBQYDK2VwAyEAXmYdcBh/jf+z+0H+hs2QvZ9UG4PBgkRvHX/JuXyjIL8= MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE34qdEtOeD21+CmmfTmFyRr/vySsY1TD6BJZFtwF37pkelvN2vRYWWHdLrUouzkQXEfnA7V50xK2QcmnBCGsxbA== MCowBQYDK2VwAyEAtkfghYcdvSRf2wA01gjQtgTqdYGjaDUZsOPXzgLnnT0= MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYYmQNAMnVKK7G3VVguAk3SC/RFT5SDjCoxcFMOPFx0vmsGycJWOK8hCdxuTCHlvShng1fo0y4Q9PJHPIGQ9EfA=="></script>
<form method="post" action="/signup" data-blockalt-action="signup">
<input name="email" type="email" required>
<button>Create account</button>
</form>- The work starts when the form first receives focus, so the click rarely waits.
- Just before the form submits, the token is written into a hidden
blockalt-tokeninput (created if missing). Rename it withdata-input-nameon the script tag ordata-blockalt-inputon the form. - While the token is being obtained the form carries
aria-busy="true", and a second submit is ignored. - If no token can be obtained, the form receives a cancelable, bubbling
blockalt:errorevent whosedetailis aBlockAltError. Cancel it to keep the visitor on the page and show a message; otherwise the form submits with an empty token and your backend refuses it. data-api-urldefaults to the script's own origin;data-config-public-keystakes the keys separated by spaces or commas. The configured client is available asBlockAlt.client.
document.addEventListener("blockalt:error", (event) => {
event.preventDefault(); // stay on the page
event.target.querySelector(".error").textContent = "Please try again in a moment.";
});BlockAlt.create(options) #
const protection = BlockAlt.create({
siteKey: "ba_pk_your_public_site_key",
apiUrl: "https://blockalt.click",
// Every key of GET /v1/keys (configPublicKeys), standby keys included.
configPublicKeys: ["MCowBQYDK2VwAyEAXmYdcBh/jf+z+0H+hs2QvZ9UG4PBgkRvHX/JuXyjIL8=","MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE34qdEtOeD21+CmmfTmFyRr/vySsY1TD6BJZFtwF37pkelvN2vRYWWHdLrUouzkQXEfnA7V50xK2QcmnBCGsxbA==","MCowBQYDK2VwAyEAtkfghYcdvSRf2wA01gjQtgTqdYGjaDUZsOPXzgLnnT0=","MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYYmQNAMnVKK7G3VVguAk3SC/RFT5SDjCoxcFMOPFx0vmsGycJWOK8hCdxuTCHlvShng1fo0y4Q9PJHPIGQ9EfA=="],
});Returns a client for one site. Create it once per page and reuse it.
| Option | Type | Meaning |
|---|---|---|
siteKey | string | Public site key, ba_pk_… |
apiUrl | string | https://blockalt.click; HTTPS required, plain HTTP only for localhost |
configPublicKeys | string[] | Every key listed by GET /v1/keys, standby keys included (a single configPublicKey is still accepted) |
timeoutMs | number | Deadline for each request to the API; default 5,000 ms |
workTimeoutMs | number | Deadline for the proof of work; default 60,000 ms, never beyond the challenge's lifetime |
execute(action, options) #
const token = await protection.execute("signup", {
signal: controller.signal, // optional AbortSignal
onProgress: ({ progress }) => (meter.value = progress), // optional, 0 to 1
});Fetches the signed configuration and a challenge, solves the proof of work and resolves with a single-use token for action. Send it to your backend with the request it protects and verify it there.
- Call it when the visitor submits, not when the page loads: a token expires after 120 seconds and works once. Each attempt needs a new token.
- It resolves with an empty string when the site is paused or unpaid. Forward it unchanged; siteverify answers
success: truewith the reasonpausedorunpaid. An empty answer prepared earlier is never reused blindly:executeasks for the configuration again, so a site resumed or topped up meanwhile gets a real token. Only when BlockAlt cannot be reached at that moment is the empty answer of the last 30 seconds used. signalcancels the work and the pending requests; the promise then rejects with the codeaborted.onProgressreceives{ progress, solved, puzzles }, withprogressfrom 0 to 1, so slow devices can show progress.- A refusal for a changed configuration or an expired or reused challenge is retried once with a fresh challenge, automatically.
prepare(action, options) #
form.addEventListener("focusin", () => protection.prepare("signup").catch(() => {}), { once: true });Obtains a token ahead of time, so the next execute(action) returns at once. One token per action is kept until 20 seconds before it expires and is handed out only once; a failed preparation is simply redone by execute. An empty answer (paused or unpaid site) is asked again by prepare after 5 seconds and by every execute. The returned promise settles when the preparation finishes.
Sending a token with fetch #
async function signup(data) {
const token = await protection.execute("signup");
const response = await fetch("/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...data, token }),
});
if (!response.ok) throw new Error("Sign-up failed");
}A header such as BlockAlt-Token works too; read it on your server instead of the body field.
Errors #
execute and prepare reject with a BlockAltError (also available as BlockAlt.BlockAltError). Check error.code; error.reason carries the server's reason when it gave one, and error.retryAfter the seconds to wait after a rate limit (from the Retry-After header).
try {
token = await protection.execute("login");
} catch (error) {
if (error instanceof BlockAlt.BlockAltError && ["network", "timeout", "rate_limited"].includes(error.code))
showRetry(error.retryAfter);
else showMessage("We could not verify this browser.");
return;
}| Code | Meaning and what to do |
|---|---|
unsupported_browser | No secure context, WebCrypto or MessageChannel (see browser support). Offer another way to complete the action |
network | Offline, DNS or TLS failure, or a 5xx answer. Let the visitor retry |
timeout | A request (5 s by default) or the work (60 s by default) took too long. Let the visitor retry |
rate_limited | Too many requests from this visitor's network; wait retryAfter seconds when given |
refused | BlockAlt refused issuance; reason says why, for example origin_denied, domain_unverified or rule_blocked |
aborted | Your signal cancelled the call |
invalid_config | Bad or unpinned configuration signature, an action the site does not have, or a malformed answer. Check your keys and actions |
clock | The server still found the challenge expired after a fresh retry |
Log error.code and error.reason (never the token) while you debug an installation: issuance refusals are counted per site, minute and reason, not logged one by one, so the page's own error is the quickest way to see why a browser was refused.
BlockAlt.supported() #
if (!BlockAlt.supported()) showAlternative();Returns true when the browser has what the script needs: a secure context, WebCrypto, fetch and MessageChannel. It makes no network request. When it returns false, execute rejects with unsupported_browser.
BlockAlt.createReader(options) #
Creates a helper for protected same-origin GET requests and event streams. See the read guard guide.
Something unclear or missing on this page? Write to support@blockalt.click.