Wiki

Clone wiki

Numera.LibrisAPI / SDK

Home > API Overview > Javascript SDK

Javascript SDK

Our Javascript SDK is provided in order to allow you to develop web based applications simply and quickly without having to invest too much time in integrating your back-end systems with Numera's platform. The SDK is a small javascript library (with no external dependencies) that is hosted by Numera and works in all major browsers on most operating systems. The SDK provides a simple way to invoke the actions provided by our API.

The implementation is geared towards enterprises adopting the single-page-application (SPA) philosophy in their application design, but it can be used in any web application.

Before you Begin

In order to use our API you will need to be assigned a unique Application ID and Secret Key that is unique to your application. This information will insure your application has the right level of access to the devices in the platform and help secure communication between the 2 platforms. You will be assigned a different Application Id and Secret Key in our stage and production environments.

To help explain the process, we will be using a sample Application ID and Secret Key in our examples.

  • Application ID = contoso-api
  • Secret Key = 472cccd50bfdfbdf87ad8f632e5fadf5

Including the SDK

To include the SDK in your application you will needed to reference our javascript file using a <script> tag in your HTML. The URL for the file will be based on what environment you will be using:

  • Stage: https://stage.bluelibris.com/static/js/libris-sdk.js
  • Production: https://na.bluelibris.com/static/js/libris-sdk.js

Near the bottom of your web page, just before the closing tag of your <body> element, you will place this EXACT script tag (the id attribute is important):

<head>
     <body>
        <!-- lots of other html you have -->
        <script id="libris-jssdk" type="text/javascript" src="https://stage.bluelibris.com/static/js/libris-sdk.js"></script>
     </body>
</head>

Initializing the SDK

Once your own scripts have loaded on the page and you are ready to begin making calls with the SDK, you must first initialize it. You only have to make this call one time per page load (this is why we recommend taking a SPA design approach to your application).

The init() function initializes the SDK for your application by:

  • validating "proof" you supply to the SDK that your application has access to the platform
  • determining if a user is currently logged in to the Numera platform
LibrisAPI.init(options, [callback_function]);

The options object must have the following properties:

PropertyDescription
application_idA string containing the Application Id assigned to you by Numera, contoso-api for our example
nonceAn integer representing the number of seconds that have elapsed since 1/1/1970 midnight (in UTC) ... you can see an example from http://www.epochconverter.com/ , we will use 1420744697 in our example
proofA base-64 encoded string representing an HMACSHA256 digest created using a string which we will detail for you in the next section

The callback_function is an optional argument you can supply that must be a function capable of receiving one object parameter that represents the result of the initialization.

Every call you make to the Libris API has the same structure, only the contents contained in the result and reason properties may change based on the call. For the initialization call, this is what is returned:

PropertyDescription
statusAn integer representing the success of the call, a 0 always means there were no problems processing the request. Anything else means there was an issue, and you should check the reason or error properties for further information
resultIf the call was successful, this will be an object with the following structure:

* logged_in - boolean letting you know if the user is authenticated with Numera
* identity - if the user is logged in, this will be a populated identity object
* login_url - will only be populated if the user is not logged in and you manually want to send them to login
reasonAn optional string containing more information related to a status code that is not 0, see the list below for possible reasons for a failed initialization:

* 1000 - INVALID_APPLICATION_ID - bad app id
* 1001 - REALM_NOT_ALLOWED - a user is logged in, but your application id prohibits the user from using the SDK with your application
* 1002 - INVALID_PROOF - something wrong with the proof you sent
* 1003 - STALE_REQUEST - the nonce used in your request is older than 30 minutes
errorAn optional object that when populated will be a standard javascript error object. You will receive these when your client javascript experiences some type of error handling your request, this is not a server-side error.

If this call succeeds you are then ready to use the other methods, but first we need to talk about identity and user authentication some more.

User Authentication

In order to use the SDK in your application, the user must have a valid authentication cookie for the Numera platform ... so how do you get one?

The answer is that it depends on how you want your users to be tracked on the Numera platform. If you want individual users to be tracked, each with their own security role, then we will call this the "user-authenticated" model. If you don't mind losing that user-level tracking, we give you the option to pre-authenticate the user with a specific role, either an Agent or an Agent-Supervisor. We will call this the "partner-authenticated" model.

user-authenticated model

The user-authenticated model requires additional setup with Numera for you to implement what is called a postback page on your platform.

When a user authenticates successfully with Numera's platform we will perform a secure postback (using the browser) to a URL on your server platform so you can examine the user details and then re-initialize the SDK. You don't have to do anything with the request if you don't want to, but we do need to have a page URL where you want the data posted.

We will make an HTTPS form-encoded post to your web page with 2 named values in the data:

  • libris-result - a base64 encoded JSON object
  • libris-proof - a base64 encoded string representing the HMACSHA256 signature we created that you can use it is a request that came from Numera

The libris-result object will contain the following properties:

PropertyDescription
actiona string letting you know if this is a login or logout action from Numera
statusinteger code, 0 implies success, for other look for a reason
noncenonce value used to compute the proof
resulta JSON object that will contain details about the user who logged in for a login:

* profile - an object containing name and other information about the user
* role - the user's permission level
* provider - how the user logged in (facebook or Numera.NIS)
* provider_login - the user's login
* accountid - internal account id you can always use to reference the identity with Numera
* email - current email address

There are 2 things you should do with this information:

  • validate that the nonce value is not too old
  • validate the proof value by taking the entire JSON string (you convert from base64 into a UTF8 string) and hashing it with your Secret Key assigned to you by Numera, then you compare the base64 version of that to the libris-proof value and they should match, otherwise someone else may be spoofing data

After you have processed the information, the user's browser will have a cookie with Numera's platform and you can redirect them to a page in your application.

Special Considerations when using init()

Once you make this call, if the user is not logged in you can call login() to initiate the authentication process for Numera's platform and we will handle the flow for you, otherwise you need to manually redirect the user to the correct authentication URL.

partner-authenticated model

Using this model requires you to change some parameters in the init() call you make to the SDK because you are asking the SDK to generate a user token for a role that is not associated with a specific user, but rather an entire realm.

One imporant aspect of using this authentication model is that any of your user's could navigate to Numera applications and they will have a valid authentication cookie that can be used to perform functions on your behalf. This is a valid use case, but you need to understand that this is a by-product of taking the approach to your application

In the options object you pass to the init() call, you must include a new force_user object with the following properties:

PropertyDescription
realmwhich realm you want the user authenticated into
supervisorboolean, false is default, if true they are logged in as an Agent Supervisor, otherwise just an Agent

Special Considerations when using init()

After calling init(), the user will always be logged in and have a valid identity unless something went wrong with the authentication.

Creating your Proof

Now that we have talked about the authentication models we can go back to how you create your proof for the init() call. The proof is a string that you create by hashing some information with the Secret Key assigned to your application. Your Secret Key should only be used in your server-side code to generate this proof and NEVER be sent down to the browser to do this calculation. Otherwise you are exposing your application's key to the entire internet and anyone could begin making API calls with your credentials.

First, you create the string content you are going to sign:

  • If using the user-authenticated model:
    • concatenate your application id and nonce value : contoso-api1420744697
  • If using the partner-authenticated model:
    • concatenate your application id, nonce, and realm you want the user to use : contoso-api1420744697contoso

Then use your programming environment's libraries to create an HMACSHA256 secure hash of that string (using your assigned Secret Key) and convert the output to a base64 encoded string. This is your "proof".

Putting it all together

You now have all the pieces to put together your init() call to the API. There are additional properties you can set on the options object:

PropertyDescription
debugset to true to see detailed messages of the data being returned in your browser's javascript console
disable_corsset to true if you want to test behavior that relies on JSON-P for older browsers
jsonp_timeoutby default, the SDK will throw an error if a JSON-P call does not complete in 60 seconds, you can alter the timeout here
postbackif using user-authenticated model, you can specify a specific URL for the postback as long as it belongs to one of your authorized domains
auto_postbackif using user-authenticated model, you can set this to true and the SDK will auto-postback if the user is already logged in on init(), which allows you to have a consistent experience whether the user needs to login or already has

There are several standard reason codes that can be returned by the SDK for various reasons:

Status CodeReasonDescription
-1UNEXPECTED_ERRORThe catch-call for any type of unexpected error where we can't give you more details except it failed badly
-1INIT_FAILUREOccurs when something is really wrong with the parameters you passed to the init call (before it even tries to communicate with the server)
-1JSONP_FAILUREEpic Fail while trying to setup the page to use JsonP .. most likely means the browser being used is not supported.
-1INVALID_ACTION_NAMEOccurs when you pass a bad action to a submit call, usually not following the 'entity.action' syntax
-1NO_JSON_SUPPORTfired with the init() call and it means the page does not have any JSON.parse or JSON.stringify available (should come with browser but could come with JQuery or other library)
-1NOT_INITIALIZEDThrown if you try to submit an API action and the API has not been initialized.
1INSUFFICIENT_PERMISSIONSThe logged in user does not have permissions to perform the function, or they are not logged in. The result will have a details property of MISSING_TOKEN or WEAK_ROLE
1MISSING_ARGUMENTSThe input parameters are missing required information

Using the SDK

The javascript SDK supports the following methods, all directly from the global LibrisAPI object, such as LibrisAPI.logout():

MethodReturnsDescription
LibrisAPI.login()true or falseThis will return true if it can redirect the user to a login page at which point it will automatically redirect the user. If it returns false, it means the user is either already logged in or the SDK has not been initialized and it does not know how to login the user.
LibrisAPI.logout()true or falseThis will return true if it can redirect the user to a logout page at which point it will automatically redirect the user. If it returns false, it means the user is already logged out or the SDK has not been initialized and it does not know how to logout the user.
LibrisAPI.identity()null or a populated identity objectIf the user is logged in, this will give you the full details about the currently logged in user.
LibrisAPI.submit(action, [data], [callback])This is how you submit actions to the platform through the api. See the API Documentation for information on what actions are supported, the action is sent as a string value. Each action will have different input parameters defined in the optional data parameter passed to this method. You can also define an optional callback function for the SDK to call when the results of the action are available. Your callback should accept one parameter which represents the response of the action.
LibrisAPI.subscribe(event_type, callback, [callback_owner])integer as the id of your subscription (used to unsubscribe)The SDK supports a pub/sub type event model in situations where you do not want to have to supply callbacks to all of your submit() calls. This approach allows you to define areas of your application that listen for certain events to take place and act upon the results. Every time the SDK is asked to submit an action to the Libris platform, when the result is available the SDK raises 2 events. You can subsribe to any of these events.

For example, the SDK raises an event called 'api.action' for every action it processes. To subscribe to this event, you would call:
LibrisAPI.subscribe('api.action', function(response) { /* do something */ });

You could then inspect the action property of the response to process the results.

If you are only interested in certain actions you append the action name as part of your subscription. For example, if you wanted to only listen for device searches, you would call:
LibrisAPI.subscribe('api.action.device.search', function(response) { /* do something */ });

In both types, you can supply an optional callback_owner to be used to bind the callback function as the this object if you use that context in your callback function.
NOTE: you can also subscribe to the results of the init() by using the event type 'user.status_change'
LibrisAPI.unsubscribe(event_type, subscription_id)If you subscribed to an event, you can cancel the subscription and you will no longer receive those events.
LibrisAPI.call_answer(rv)Answer an incoming call that is received from dashboard.open. On success you would invoke LibrisAPI.subscribe_real_time_events
LibrisAPI.subscribe_real_time_events( {rv, polling_interval, event_filter_flags}, callback)RTSMessageListen for specific device event messages. When event is received the callback function with the RTSMessage is invoked.
LibrisAPI.call_end(rv)Terminate the call (Hang up)

IdentityObject

When you ask for the current identity you will receive an object with the following structure:

PropertyDescription
emailUser's email address
roleAn integer representing the user's permission level in the Libris platform.

0 - Anonymous, or "Everyone", no rights
100 - Libris device user, only have access to their device record.
200 - Caregiver, have access to a defined set of devices.
500 - Agent, have access to a realm and/or sub-realms of devices.
800 - Agent Supervisor, have Agent access to a realm plus administrative privileges for the realm and its sub-realms
1000 - Numera internal user only
realmDefines the realm the user has access to
account_idA string representing the unique account id associated with this identity. You should use this field to reference the account in your application, as a user may change their email over time while this will remain constant.
profileThe profile object containing details about the identity:

first_name - First name
last_name - Last name
country - Country (United States)
culture - User's preferred culture code (en-US)
date_of_birth - As an iso string, YYYY-MM-DDT00:00:00Z
timezone - Timezone code, see the Numera.Net documentation for a list of timezone codes.
providerNotifies who the authentication credentials for the identity belong to, typically Numera.NIS if using a Numera login and password
provider_loginGives you the login id for the user if the provider allows it, if provider is Numera.NIS, then this will be the user's login

JavaScript Sample code on how to call libris-SDK

var _computeSignature = function(secret, message) {
    var hmac = crypto.createHmac('sha256', secret);
    hmac.update(message);
    return hmac.digest('base64');
};
 
var _createPartnerToken = function(realm, action) {
    var nonce = Math.floor(new Date().getTime() / 1000);
 
    // appId provided by Numera.
    var appId = 'XXXX';  // our appId
 
    // secret key provided by Numera.
    var secret = 'YYYY'; // our secret
 
    var toSign = appId + nonce + action;
    var signature = _computeSignature(secret, toSign);
 
    var partnerKey = {
        id: appId,
        r: realm,
        n: nonce,
        p: signature
    };
    return partnerKey;
};
 
// Call Libris SDK.
var _invokeLibrisSDK = function(handler, action, data) {
    // e.g. handler = realm
    // e.g. action = view
    var data_wrapper = {
        action: 'libris api: ' + handler + '.' + action,
        data: data
    };
 
    var myRealm = 'Life365';
    data_wrapper.data.partner_token = _createPartnerToken(myRealm, action);
 
    var libris_sdk_url = "https://stage.bluelibris.com/sdk/v1/"
    var target = libris_sdk_url + handler + '/' + action;
 
    var options = {
        uri: target,
        method: 'POST',
        body: data_wrapper,
        json: true,
        headers: {
            'Accept': 'application/json'
        }
    };
 
    request(options, function(err, resp, result) {
        if (err) {
            //_logger.error(err);
            throw err;
        } else {
            // process the response.
            console.log(resp);
        }
 
        resp.send('[' + result + ']');
    });
}
 
var handler = 'realm';
var action = 'view';
var data = new Object();
_invokeLibrisSDK(handler, action, data);

Python Sample code on how to call libris-SDK

#!/usr/bin/python
import hmac
import base64
import hashlib
import math
import time
import requests
import sys

# Sample use:
# From command line simply run ./sdk_test to see sample usage.
#
def invoke_libris_sdk(handler, action, realm, app_id, secret):
    """

    :param handler: eg device (default)
    :param action: eg view (default)
    :param realm: partner realm
    :param app_id: app_id
    :param secret: partner secret
    :return:
    """
    partner_token = create_partner_token(realm, action, app_id, secret)
    data_wrapper = {
        'action': 'libris api: ' + handler + '.' + action,
        'data': {'partner_token': partner_token }
    }
    libris_sdk_url = "https://stage.bluelibris.com/sdk/v1/"
    target = libris_sdk_url + handler + '/' + action
    r = requests.post(target, json=data_wrapper)
    print "HTTP status code:", r.status_code
    print "Response:", r.content

def create_partner_token(realm, action, app_id, secret):
    """

    :param realm: partner realm
    :param action: eg view (default)
    :param app_id: app_id
    :param secret: app secret
    :return:
    """
    nonce = int(math.floor(time.time()))
    to_sign = app_id + str(nonce) + action
    signature = compute_signature(secret, to_sign)
    partner_key = { 'id': app_id, 'r': realm, 'n': nonce, 'p': signature }
    return partner_key

def compute_signature(secret, tosign):
    """
    :param secret: secret key
    :param tosign: appId + nonce + action
    :return:
    """
    return base64.b64encode(hmac.new(secret, msg=tosign, digestmod=hashlib.sha256).digest())

def make_api_call(realm, app_id, secret, handler='device', action='view'):
    """
    Calls device.view
    :param realm: partner realm
    :param app_id: app_id
    :param secret: secret
    :param handler: eg device (default)
    :param action: eg view (default)
    :return:
    """
    invoke_libris_sdk(handler, action, realm, app_id, secret)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print "Usage: sdk_curl <realm> <app_id> <secret>"
    else:
        make_api_call(sys.argv[1], sys.argv[2], sys.argv[3])

You are Ready

You are now ready to start making API calls. See the complete API documentation for a reference of the different actions you can invoke. Enjoy!

Updated