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:
- Castle.js runs on your pages and collects signals.
- Your frontend creates a request token and sends it to your backend with the user action (login, signup, and so on).
- Your backend calls the Castle API with that token. See the Integration guide for the server side.
Castle and cookie typesCastle.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 dateCastle.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 possibleThe 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.
| option | description | default |
|---|---|---|
pk | Required. Publishable Key | null |
cookieDomain | Domain 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 ignored | Derived from the current hostname, and shared across its subdomains |
storage | Container 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} |
worker | Worker URL overrides: collectorWorkerUrl, collectorSharedWorkerUrl, debugWorkerUrl. Only needed if you self-host the worker assets | undefined |
css | CSS options: disableInlineCss (boolean). Set to true if your Content Security Policy forbids inline styles | undefined |
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:
| directive | why |
|---|---|
worker-src | The collector and shared collector workers |
style-src | Inline 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-safeMany 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 foreverA 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 bodyThe 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.
- The SDK initializes and returns a token. On a page where
configure()has run, callawait castle.createRequestToken()from the browser console. You should get back a long opaque string. - The token reaches your backend. Inspect the outgoing request and confirm the token is present in the request body.
- Castle receives the event. Open Explore in the Castle Dashboard and look for the event your backend just sent.
Common problems
| Symptom | Cause | What 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_token | The 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 stylesheet | Your Content Security Policy is blocking the SDK's resources. | See Content Security Policy above. |
| Token generation takes noticeably long | The 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 option | Why it's gone |
|---|---|
window | Pointing the SDK at a mocked DOM such as JSDOM is no longer supported. No replacement. |
timeout | Applied to the client-side event methods, which have been removed. |
throttling | Batched 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 field | 3.0 field |
|---|---|
wUrl | worker.collectorWorkerUrl |
swUrl | worker.collectorSharedWorkerUrl |
dwUrl | worker.debugWorkerUrl |
dCss | css.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 API | 3.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 SDKIn 2.x,
page(),form()andcustom()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
- Confirm every browser you support has native
Promise(see Requirements) - Remove
window,timeoutandthrottlingfrom yourconfigure()call - Move any worker URL or CSS overrides to their nested names
- Replace
form()andcustom()calls withcreateRequestToken() - Remove usage of
page(),injectTokenOnSubmit()andformEventOnSubmit() - 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 - Ensure the request token is sent in the request body, not as a header
- Update any test setup that passed the
windowoption, which has been removed - Verify your Content Security Policy allows the worker and style directives (see Content Security Policy)
Updated 12 days ago