Browser SDK

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

❗️

Breaking changes in 2.0 and 2.1

If you are about to upgrade Castle.js from a 1.x or 2.0.x version, please see the chapter on how to deal with the breaking changes that were introduced for these versions.

Introduction

The Browser SDK is a central component of the Castle integration, and provides device fingerprinting, behavioral analysis, and client-side event monitoring.

All the client-side SDKs, have two main purposes:

The script is designed to work in most known browser starting from Internet Explorer 5 and up. Please see the Minimal Requirements section in the NPM package docs for more details.

Installation

The recommended way of installing Castle.js in your app is via the NPM package and using either npm or yarn:

npm install @castleio/castle-js
yarn add @castleio/castle-js
"dependencies": {
    "@castleio/castle-js": "^2.1.12",
  …
}
# Check this file for the latest version (dist/tarball)
https://registry.npmjs.org/@castleio/castle-js/latest

#  And then pull the tgz file:
https://registry.npmjs.org/@castleio/castle-js/-/castle-js-2.1.12.tgz

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'

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

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

option

description

default

pk

Required. Publishable Key

null

window

Overrides the browser window object. Useful e.g. when running in a test environment with a mocked DOM, like JSDOM.

window

avoidCookies

Disables the usage of cookies whenever possible when set to true

false

cookieDomain

When cookies are used, set the cookie domain scope

top level domain

timeout

request timeout for page, form, custom events.

1000 ms

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 solution 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 to 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. It's recommended that you implement the token generation as a client-side middleware which generates a new request token with each request to your backend.

castle.createRequestToken().then( (requestToken) => {
  // `token` contains a unique, one-time use, string that should be passed
  // in the request to the server, eg. in a form post.
});

// or
var requestToken = await castle.createRequestToken();

The createRequestToken method doesn't accept any options, and it generates tokens immediately in a non-blocking way under normal conditions. In the edge case where the method is called immediately after page load, it can take up to a maximum of 300 ms to collect all the device data, but this edge-case should only happen in bot scenarios.

Examples on how to pass the request token

var myForm = document.getElementById('loginForm');

myForm.addEventListener(
  'submit',
  function (e) {
    e.preventDefault();

    // Insert a hidden field with the Castle request token
    var hiddenField = document.createElement('input');
    myForm.appendChild(hiddenField);
    hiddenField.setAttribute('type', 'hidden');
    hiddenField.setAttribute('name', 'castle_request_token');
    
    castle.createRequestToken().then(function (token) {
      hiddenField.value = token;
      myForm.submit();
    });
  },
  return false;
);
var myForm = document.getElementById('loginForm');

myForm.addEventListener(
  'submit',
  function (e) { castle.injectTokenOnSubmit(e) }
);
var myHeaders = new Headers();
var token = await castle.createRequestToken();

myHeaders.append('X-Castle-Request-Token', token);

fetch('https://example.com/login', {
  method: 'POST',
  mode: 'no-cors',
  headers: myHeaders,
  body: JSON.stringify(loginData)
})

Tracking client-side events

Building the user object

The Browser SDK offer three methods of sending data: page, form and custom (described below) depending on which type of action the user performs. Common for these calls is that a user object needs to be provided. The contents of the user object are the same as what you'll send to the Risk and Filter APIs.

var user = {
  id: 'ca1242f498', // required
  email: '[email protected]', // required
  phone: '+1415232183', // required if email is not present
  name: 'Michael Brown',
  registered_at: '2012-12-02T00:30:08.276Z',
  traits: {
    plan: 'premium'
  }
}

// The `user` object is required for all event tracking methods
castle.page({user: user});

Securing the user object

In order to prevent user information from being spoofed by a bad actor, it is recommended to send the user information as a signed JWT when Castle.js is used in production.

From your backend code, you need to encode the user as a JWT and sign it using your API secret key. Then, when Castle receives the JWT, the integrity of the user data will be verified to ensure that the data isn't being tampered with. Below is an example of how to generate a JWT using the Ruby language:

var userJwt = "<%= JWT.encode({ id: current_user.id, email: current_user.email, registered_at: current_user.registered_at }, ENV.fetch('CASTLE_API_SECRET'), 'HS256') %>";

// Then use the `userJwt` argument instead of `user` when using any of the tracking methods
castle.page({userJwt: userJwt});

castle.page()

Usage

// The page method needs to be called with the currently logged in user
castle.page(options);

// Fully customizable example:
castle.page({
  userJwt: userJwt, // or `user` if you're not using secure mode
  url: 'http://castle.io',
  name: 'User Page',
  referrer: null
}).then( (response) => {
   console.log(response)
})

option

description

default

user

Required unless userJwt is used. The user object, as described above

none

userJwt

Required unless user is used. JWT string containing user information and a signature

none

name

Optional. Name of the page that the user is viewing.

Extracted automatically from document.title

url

Optional. URL of the page that the user is viewing

Extracted automatically from document.location.href

referrer

Optional. URL of the referrer from the user came from

Extracted automatically from document.referrer

page function is async and returns the promise which is resolved in max 1000ms (timeout can be configured in the configure method)

Additionally, all the page events on the page are debounced with 300ms (to avoid quickly changed urls for SPA frameworks)

Event promise can be resolved with one of the values:

response

descriptin

true

event sent successfully

false

wrong configuration or data is invalid

null

timeout or debounce appeared

castle.form()

Usage

castle.form({
  userJwt: userJwt, // or `user` if you're not using secure mode
  name: 'Update Profile',
  values: {
    first_name: 'John',
    newsletter: false
  }
});

option

description

default

user

Required unless userJwt is used. The user object, as described above

none

userJwt

Required unless user is used. JWT string containing user information and a signature

none

name

Required. Name of the form that the user is submitting.

none

values

Optional. Object with the input values of the form that the user is submitting

none

form function is async and returns the promise which is resolved in max 1000ms (timeout can be configured in the configure method)

Event promise can be resolved with one of the values:

response

description

true

event sent successfully

false

wrong configuration or data is invalid

null

timeout appeared

castle.custom()

Usage

castle.custom({
  userJwt: userJwt, // or `user` if you're not using secure mode
  name: 'Added to cart',
  properties: {
    product: 'iPhone 13 Pro',
    price: 1099.99
  }
});

option

description

default

user

Required unless userJwt is used. The user object, as described above

none

userJwt

Required unless user is used. JWT string containing user information and a signature

none

name

Required. Name of the event that is tracked.

none

properties

Optional. Object with extra properties to attach to the event.

none

custom function is async and returns the promise which is resolved in max 1000ms (timeout can be configured in the configure method)

Event promise can be resolved with one of the values:

response

description

true

event sent successfully

false

wrong configuration or data is invalid

null

timeout appeared

Helper methods

Castle.injectTokenOnSubmit()

Global helper method that can be used directly on a form to inject the Castle request token as a hidden parameter called castle_request_token

Usage

<form onsubmit="Castle.injectTokenOnSubmit(event)">
  <input type="email" name="email">
  <input type="password" name="password">
</form>

parameters

description

default

  1. event

Required form SubmitEvent

  1. onDone(event)

a function called after injecting the token

function(event) {
formElement.submit()
}

Castle.formEventOnSubmit()

Global helper method that can be used directly on a form to generate form events based on the data-castle- attributes

Usage

<form data-castle-name="Update Profile" onsubmit="Castle.formEventOnSubmit(event, user)" action="/">
  <input type="text" name="email" placeholder="email" data-castle-value="email">
</form>

This will trigger form event like below before the submit:

castle.form({
  user: user,
  name: 'Update Profile',
  values: {
    email: '<email>',
  }
});

parameters

description

default

event

Required form SubmitEvent

user

Required user object

options

{}

onDone(event)

a function called after sending the form event

function(event) {
formElement.submit()
}

HTML data attributes

attribute

description

element

data-castle-name

attribute describing name of the form event

on the form element

data-caste-value

attribute describing name of the form values names

on the input, select or textarea element

Breaking changes

Upgrading from 1.x to 2.0.x

1.x

Legacy package: https://www.npmjs.com/package/castle.js.

require "castle.js"  
_castle('setAppId', "YOUR_APP_ID")
_castle('getClientId')

2.0.x

New package:

import * as Castle from "@castleio/castle-js"
  1. Renamed the main object from _castle to Castle in the new module, but kept the _castle in the CDN version as well as the browser-specific module @castleio/castle-js/dist/castle.browser.js
  2. Introduced the concept of request tokens that need to be generated for each call to Castle's server-side API.
Castle.createRequestToken().then( (requestToken) => {
});

// or

const token = await Castle.createRequestToken();
  1. Request tokens can no longer be retrieved from the cookie string.
  2. All the following methods were removed:

autoForwardClientId, autoTrack, catchHistoryErrors, identify, setUserId, setAccount, setKey, setAccount, sessionId, reset, page, trackPageView, setTrackerUrl

📘

Page views (and more) returned in 2.1

The old way of sending page views were deprecated with 2.0, but recently returned in a more powerful version were you can track any events from the frontend.

Upgrading from 2.0.x to 2.1.x or later

2.0.x

Castle.configure(YOUR_CASTLE_APP_ID);

2.1.x or later

  1. Removed the _castle object for all versions and now only relying on Castle.

  2. Switched to use the Publishable Key that can be found in the same place as the now deprecated App ID.

Castle.configure({pk: YOUR_PUBLISHABLE_KEY});
  1. onFormSubmit has been renamed to injectTokenOnSubmit

  2. _castle global method is no longer supported and has been replaced with Castle (for the CDN and castle.browser.js versions) check docs

  3. CDN version no longer needs appID in the url and requires <script>Castle.configure({pk: YOUR_CASTLE_PUBLISHABLE_KEY});</script> to be added. The CDN version can't be used for generating request tokens, but only for tracking client-side events.

  4. Introduced page, form, and custom methods for client-side event tracking


Did this page help you?