Browser SDK

This page describes how to install and use the JavaScript client-side SDK

Introduction

The Browser SDK is a central component of the Castle integration, and provides device fingerprinting and behavioral analysis.

The primary purpose of the SDK is to create request tokens: unique, short-lived tokens that carry the device and behavioral signals Castle collected in the browser. Your frontend passes a token to your server, and your server includes it when calling Castle's Risk and Filter APIs.

A working integration has three parts:

  1. Castle.js runs on your pages and collects signals.
  2. Your frontend creates a request token and sends it to your backend with the user action (login, signup, and so on).
  3. Your backend calls the Castle API with that token. See the Integration guide for the server side.
👍

Castle and cookie types

Castle.js doesn't set third-party cookies and only sets first-party cookies. Eg. Google's Third-Party Cookies deprecation will also require no adjustments on your end.

Requirements

Castle.js requires a browser with native Promise support. This covers all current versions of Chrome, Safari, Firefox and Edge. Internet Explorer 11 and legacy embedded webviews without native Promise are not supported.

Installation

Install the Castle Browser SDK package using your preferred package manager. Setup instructions and credentials are available in the Castle Dashboard.

🚧

Keep the script up to date

Castle.js is updated regularly with improvements to device fingerprinting and bot detection. We recommend updating to the latest version frequently to ensure the best detection accuracy.

Configuration

Once installed, the SDK needs to be configured using your Publishable Key, which can be found in the Dashboard for users with administrator access.

🚧

Initialize the SDK as early as possible

The SDK should be initialized immediately at page load. The more time that passes from initialization to when you eventually create a request token (next section), the more data the agent can collect, which in turn improves accuracy.

import * as Castle from '@castleio/castle-js'

const castle = Castle.configure({ pk: '<YOUR_PUBLISHABLE_KEY>' });
import '@castleio/castle-js/dist/castle.browser.js'

const castle = Castle.configure({ pk: '<YOUR_PUBLISHABLE_KEY>' });

configure() returns the SDK instance. Keep a reference to it, as it's what you call createRequestToken() on later.

optiondescriptiondefault
pkRequired. Publishable Keynull
cookieDomainDomain the Castle cookie is written to. Set this to scope the cookie to a specific domain. The value must look like a domain (example.com); anything else is ignoredDerived from the current hostname, and shared across its subdomains
storageContainer for the namespace (name) and the expiration time in seconds (expireIn), used for storing Castle data in localStorage and/or cookies. Use name in case of conflicts with other vendors{"name":"__cuid", "expireIn": 34560000}
workerWorker URL overrides: collectorWorkerUrl, collectorSharedWorkerUrl, debugWorkerUrl. Only needed if you self-host the worker assetsundefined
cssCSS options: disableInlineCss (boolean). Set to true if your Content Security Policy forbids inline stylesundefined

Content Security Policy

Castle.js loads web worker assets and injects inline CSS. If you run a strict Content Security Policy, you need to allow:

directivewhy
worker-srcThe collector and shared collector workers
style-srcInline styles used for signal collection. Set css.disableInlineCss: true if you cannot allow 'unsafe-inline'

Creating request tokens

Once Castle.js is running on your web pages, you need to ensure that the request_token value generated by Castle.js is passed to your application server, where the Castle server-side SDK will be able to extract the request_token value.

👍

Adblock-safe

Many analytics solutions rely on the client-side SDK performing outgoing API requests, which results in them getting blocked by adblockers and privacy plugins. Castle.js won't make such requests, resulting in much higher accuracy.

🚧

Request tokens don't live forever

A new request token value should be generated for each request to your backend. A request token will expire after 120 seconds and should only be used during a single request to your backend. The reason for this is that Castle.js continuously monitors behavior in the user session and the data will need to be fresh when processed by the Castle APIs. A scalable approach is to implement the token generation as a client-side middleware which generates a new request token with each request to your backend.

const requestToken = await castle.createRequestToken();

The createRequestToken method doesn't accept any options.

Call configure() as early as possible in page load, and don't generate a token on the very first frame after it. Token generation is asynchronous: the SDK collects and encodes device signals at generation time, which is normally near-instant, but a token requested before the SDK has finished loading its external resources (workers, CSS) can take up to 1-2 seconds.

Examples on how to pass the request token

🚧

Always use the request body

The request token must be sent in the request body, not as an HTTP header. The token may exceed the size limits imposed by servers and proxies on individual HTTP headers.

const token = await castle.createRequestToken();

fetch('https://example.com/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    ...loginData,
    castle_request_token: token
  })
})
const myForm = document.getElementById('loginForm');

myForm.addEventListener('submit', async (e) => {
  e.preventDefault();

  let hiddenField = myForm.querySelector('input[name="castle_request_token"]');
  if (!hiddenField) {
    hiddenField = document.createElement('input');
    hiddenField.type = 'hidden';
    hiddenField.name = 'castle_request_token';
    myForm.appendChild(hiddenField);
  }

  hiddenField.value = await castle.createRequestToken();
  myForm.submit();
});

Verifying your integration

After deploying, confirm the three parts in order.

  1. The SDK initializes and returns a token. On a page where configure() has run, call await castle.createRequestToken() from the browser console. You should get back a long opaque string.
  2. The token reaches your backend. Inspect the outgoing request and confirm the token is present in the request body.
  3. Castle receives the event. Open Explore in the Castle Dashboard and look for the event your backend just sent.

Common problems

SymptomCauseWhat to do
castle: missing or wrong pk thrown by configure()No publishable key was passed on the first call to configure().Pass pk. The key is in the Castle Dashboard under Settings.
castle: missing configuration thrown by configure()configure() ran in an environment with no window, typically server-side rendering.Initialize the SDK in the browser only, for example inside a client-side lifecycle hook.
Castle API returns 422 with type invalid_request_tokenThe token is missing from the payload, older than 120 seconds, truncated in transit, or was generated with a publishable key from a different environment than the API key your backend is using.Generate a fresh token per request, send it in the request body rather than a header, and check that your pk and your API key belong to the same environment.
Console errors about a blocked worker or stylesheetYour Content Security Policy is blocking the SDK's resources.See Content Security Policy above.
Token generation takes noticeably longThe token was requested before the SDK finished loading its external resources.Call configure() earlier in page load, and don't generate a token in the same tick.

Upgrading from 2.x to 3.0

Configuration

The configure() surface has been reorganized from flat, abbreviated keys to a nested, descriptive shape.

Removed fields

2.x optionWhy it's gone
windowPointing the SDK at a mocked DOM such as JSDOM is no longer supported. No replacement.
timeoutApplied to the client-side event methods, which have been removed.
throttlingBatched client-side events sent in quick succession. Those events have been removed.

Undocumented options

2.x also accepted a handful of options that were never part of the documented configuration. If you were passing any of these, they now live under a nested name:

2.x field3.0 field
wUrlworker.collectorWorkerUrl
swUrlworker.collectorSharedWorkerUrl
dwUrlworker.debugWorkerUrl
dCsscss.disableInlineCss

cookieDomain is unchanged. Any other option that doesn't appear in the configuration table above is no longer supported.

Removed APIs

The public surface is now exactly two methods: configure() and createRequestToken(). Everything below has been removed.

2.x API3.0 replacement
form()createRequestToken()
custom()createRequestToken()
injectTokenOnSubmit()Call createRequestToken() in your own submit handler and set the value on a hidden field — see Examples on how to pass the request token
page()No direct equivalent
formEventOnSubmit()No direct equivalent
❗️

Client-side event tracking is no longer part of the Browser SDK

In 2.x, page(), form() and custom() did two things: they generated a request token and they sent a client-side event to Castle. Castle.js now only generates tokens. If you relied on these calls to send activity to Castle, that activity now has to be sent server-side via the Risk or Filter APIs.

Promises

The SDK now returns real Promise objects instead of thenable wrappers. Environments without native Promise support (such as IE11 or legacy embedded webviews) are no longer supported.

Request token transport

The request token must now be sent in the request body (not as an HTTP header). The token may exceed the size limits imposed by servers and proxies on individual HTTP headers.

Migration checklist

  1. Confirm every browser you support has native Promise (see Requirements)
  2. Remove window, timeout and throttling from your configure() call
  3. Move any worker URL or CSS overrides to their nested names
  4. Replace form() and custom() calls with createRequestToken()
  5. Remove usage of page(), injectTokenOnSubmit() and formEventOnSubmit()
  6. Check whether you were relying on page()/form()/custom() to send activity to Castle, not just to mint tokens. If so, move that activity server-side
  7. Ensure the request token is sent in the request body, not as a header
  8. Update any test setup that passed the window option, which has been removed
  9. Verify your Content Security Policy allows the worker and style directives (see Content Security Policy)

Did this page help you?