Keystroke DNA Integration Guide
v3.0.1

Keystroke DNA analyzes individual typing patterns for information security use. As Keystroke DNA is available as a service, it can be easily integrated into any web form with just a few lines of code.

This guide explains how to use KeystrokeDNA.js and HTML to integrate Keystroke DNA into your web application. Our process supports Retrospective Integration, which does not affect or detract from the user experience in any way and can be added to already existing login forms.
Getting Started
There are a few basic steps that are required to start using Keystroke DNA.
First Steps
Identifying Input Fields
You can choose as many fields as you would like to be validated by Keystroke DNA and any text input field is acceptable. Initially, however, we recommend that either the username or email input field on the home login screen is used. This input field also serves as the user identifier for Keystroke DNA's validation process. The most important criterion is that the text input field is sufficiently long and contains at least 8 characters.

Using a phone number or other numeric identifier is also possible.
Password input fields should not be used for Keystroke DNA validation.
What is a UserID?
Any unique identifier of the user can be used as the UserID with Keystroke DNA. It can be a username, email, phone number or any anything else, including GUID.
Including KeystrokeDNA.js
To start using Keystroke DNA, you will need to include the loading script at the bottom of the body section of the webpage to asynchronously load the plugin:
You will require your API id, that can be obtained by Joining Beta Program.
script
!function(a,b,c,d){a.KSDNA=a.KSDNA||{f:[],ready:function(b){a.KSDNA.loaded?b():this.f.push(b)}};var e=b.createElement(c),f=b.getElementsByTagName(c)[0];e.ksdna=1,e.async=1,e.src=d,f.parentNode.insertBefore(e,f)}(window,document,"script","https://api.keystrokedna.com/static/v3/ksdna.js?apiId={YOUR_API_ID}");
/script
Setting Up Input Fields
There can be one or more fields that are being validated with Keystroke DNA and each of these input fields should be marked with the ksdna attribute. The user identifier can be one of the input fields and should be marked with the ksdna-uid attribute.
input typetext idemail nameemail placeholderEmail ksdna-uid ksdna/
Finalizing Setup
After all the input fields are set up, the following script should be added, to signal that all the necessary fields have been created and are set up with Keystroke DNA tags:
KSDNA.ready(function () {
KSDNA.init();
form.addEventListener('submit', function (e) {
e.preventDefault();
KSDNA.validate()
.then(function (ksdnaResponse) {
proceedToNextStepsOfYourFormSubmit(ksdnaResponse);
})
.catch(function (ksdnaInternalError) {
handleInternalKsdnaError(ksdnaInternalError);
});
});
});
Where proceedToNextStepsOfYourFormSubmit(ksdnaResponse) is the method that will be called after successful validation, and would typically be the submission of the credentials for the server side validation; the handleInternalKsdnaError(ksdnaInternalError) method will be called if the Keystroke DNA server responds with an error.
Validating user identity
The response object passed to a successful callback function will contain the following data:
response = {
score: undefined | 0..1,
sequences: Array[Number, ...]
}
Where 0.8 is an arbitrary threshold for successful validation.
Handling Errors
The error object passed to an error callback function will contain the following data:
error: {
key: '{ERROR_KEY}',
message: '{READABLE_ERROR_MESSAGE}',
}
Further Steps
Advanced Validation
With Keystroke DNA any other form, different from login, can also be validated, however the UserID must be passed as a parameter to the KSDNA.validate() method.
KSDNA.ready(function () {
KSDNA.init();
form.addEventListener('submit', function (e) {
e.preventDefault();
KSDNA.validate({YOUR_UID_STRING})
.then(function (response) {
proceedToNextStepsOfYourFormSubmit(response);
})
.catch(function (error) {
handleInternalKsdnaError(error);
});
});
});
Technical Support
Please feel free to reach out to us with technical questions, suggestions or possible issue reports by e-mail.
KSDNA Integration API
v3.0.1

JWT Token
This section describes how to obtain a token.
Response description
Name
Required
Type
Description
access_token
true
String
JWT Token to be used in the future
token_type
true
String
Bearer
expires_in
true
Integer
Expiration time in seconds
scope
true
String
Allowed scopes
jti
true
String
JWT identifier
sequence_id
true
String
Unique sequence identifier in our system.
Using a token
After successfully receiving a token, it must be used in every API request. A token has a 15 minute (900 seconds) expiration time. After expiration, a new token must be obtained again. You can verify what is hashed inside each token here: jwt.io.
External resources
More information about the Oauth2 protocol is available here: https://aaronparecki.com/oauth-2-simplified/.
Request a token
Post
https://oauth.keystrokedna.com/oauth/token
YXBwX2lkOmFwcF9zZWNyZXQ= used in an example it’s a standard Basic Auth combination of your {app_id}:{app_secret}
Headers
Content-Type: application/x-www-form-urlencoded
Authorization: Basic YXBwX2lkOmFwcF9zZWNyZXQ=

Body
grant_type=client_credentials
Headers
Content-Type: application/json

Body
{
"access_token": "eyJhbGci...",
"token_type": "bearer",
"expires_in": 899,
"scope": "read write",
"jti": "e06bea5f-e803-4090-9998-fac38335e3a9",
"sequence_id": "abd3673haa89..."
}
Headers
Content-Type: application/json

Body
{
"timestamp": 1497533579008,
"status": 401,
"error": "Unauthorized",
"message": "Bad credentials",
"path": "/oauth/token"
}
User
Authorization
Every request should contain the JWT token in an Authorization header.
Get all users
GET
https://oauth.keystrokedna.com/api/users/my
Get all users that belong to your app.
Headers
Authorization: Bearer {JWT_TOKEN}
Headers
Content-Type: application/json

Body
{
"content": [
{
"uuid": "370d199f-c560-41b6-a282-72ac23f5c762",
"username": "user1@yourdomain.com",
"enabled": true
},
{
"uuid": "8426bcae-b2e8-4216-a783-20d2651f2882",
"username": "user2@yourdomain.com",
"enabled": false
}
],
"hasNext": false,
"last": true,
"hasContent": true,
"totalPages": 1,
"totalElements": 2
}
Update a user
PUT
https://oauth.keystrokedna.com/api/users/username/{username}
Update a username or the availability of a user.
Name
Required
Type
Description
username
true
String
Username of a user.
Headers
Content-Type: application/json
Authorization: Bearer {JWT_TOKEN}

Body
{
"username": "updated",
"enabled": false
}
Headers
Content-Type: application/json

Body
{
"message": "User with {username} username not found."
}
Headers
Content-Type: application/json

Body
{
"error": "invalid_token",
"error_description": "Access token expired: eyJhbGciO..."
}
Headers
Content-Type: application/json

Body
{
"code": "InvalidRequest",
"message": "Invalid UserUpdate",
"fieldErrors": [
{
"resource": ,
"field": "username",
"code": ,
"message": "Username should not be blank."
}
]
}
Delete a user
DELETE
https://oauth.keystrokedna.com/api/users/username/{username}
Delete a user and all of its data.
Name
Required
Type
Description
username
true
String
Username of a user.
Headers
Authorization: Bearer {JWT_TOKEN}

Body
{
"username": "updated",
"enabled": false
}
Headers
Content-Type: application/json

Body
{
"message": "User with {username} username not found."
}
Headers
Content-Type: application/json

Body
{
"error": "invalid_token",
"error_description": "Access token expired: eyJhbGciO..."
}
Sequence
Authorization
Every request should contain the JWT token in an Authorization header.
Approve
PUT
https://oauth.keystrokedna.com/api/sequences/approve/{code}
Confirms that the given sequence was submitted from a genuine user logged in with his credentials.
Name
Required
Type
Description
code
true
String
Unique sequence identifier in our system. Example: abd3673haa89
Headers
Authorization: Bearer {JWT_TOKEN}
Disapprove
DELETE
https://oauth.keystrokedna.com/api/sequences/approve/{code}
Gives our system a note that the given sequence was submitted with the wrong credentials.
Name
Required
Type
Description
code
true
String
Unique sequence identifier in our system. Example: abd3673haa89
Headers
Authorization: Bearer {JWT_TOKEN}
KSDNA
KSDNA
Class representing a KeystrokeDNA instance.
Constructor
new KSDNA(element)
Create a KeystrokeDNA instance.

Parameters:
Name
Type
Description
element
HTMLElement
INPUT or TEXTAREA KeystrokeDNA plugin will be collecting data from

Methods
(static) addElement(element)
Add element to KeystrokeDNA collection.

Parameters:
Name
Type
Description
element
HTMLElement
(static) getUID(uid) → {string}
Get UID that will be used to save data.

Parameters:
Name
Type
Description
uid
string
uid that will override default value
Default Value: value of element with [ksdna-uid] attribute
Throws:
if uid was not specified
Error
Returns:
uid that will be used to save data
string
(static) init()
Initialize KeystrokeDNA plugin with all the elements that contain the attribute [ksdna]. This creates a new KSDNA instance for each element.
(static) initUID(uid)
Initialize UID that will be used to save data.

Parameters:
Name
Type
Description
uid
string
uid that will override default value
Default Value: value of element with [ksdna-uid] attribute
Throws:
if uid was not specified
Error
(static) ready(callback)
Check if plugin is ready.

Parameters:
Name
Type
Description
callback
function
The callback that is called when plugin is ready or immediately if plugin is already ready.
(static) removeElement(element)
Remove element from KeystrokeDNA collection.

Parameters:
Name
Type
Description
element
HTMLElement
(static) reset()
Reset element value
(static) validate(uidopt) → {Promise.<Object, Object>}
Authenticate user.

Parameters:
Name
Type
Attributes
Description
uid
string
optional
uid that will be used to save data #initUID
Returns:
A promise that returns an Object with
Promise.<Object, Object>
addOverlayElement()
Modify element's name to disable autocomplete
reset()
Reset element value
Response
Success Response
{
"score": undefined | 0..1,
"sequences": Array[Number, ...]
}
Error Response
{
"error": {
"key": {ERROR_KEY},
"message": {ERROR_MESSAGE}
}
}
where ERROR_KEY and ERROR_MESSAGE are one of
{
UID_NOT_FOUND: 'KSDNA: KSDNA_UID not specified.',
UID_ELEMENT_NOT_FOUND: 'KSDNA: [ksdna-uid] element not found.',
UID_ELEMENT_DUP: 'KSDNA: [ksdna-uid] element can be only one. Please remove all duplicated [ksdna-uid] elements.',
NO_DATA: 'KSDNA: _prepareData(): No data collected',
ELEMENT_REQUIRED: 'KSDNA: new KSDNA(): [element] is required.',
ELEMENT_TYPE_MISMATCH: 'KSDNA: new KSDNA(): [element] should be INPUT or TEXTAREA element.',
INTERNAL_ERROR: 'We have encountered an internal server error. Please contact us to report a problem.'
}