Embedding Gate One Into Other Applications

Note

A much better interactive tutorial is available in Gate One's 'tests' directory: <gateone dir>/tests/hello_embedded/. The future of the documentation you're reading is uncertain.

This tutorial will walk you through embedding Gate One into a completely different web application. It is divided into two parts:

  1. Basics: Embedding Gate One into any web page.
  2. Advanced: API-based authentication, "embedded mode", and customizing everything.

We'll assume you have Gate One installed and running with the following settings (in your server.conf):

auth = None # Anonymous authentication
port = 443
disable_ssl = False
origins = "*" # Disable origin checking for now (this will be covered in Part 2)
url_prefix = "/" # Keep it simple for the tutorial

Note

'origins' is the only setting above that is differs from defaults.

Before we continue please test your Gate One server by loading it in your browser. This will also ensure that you've accepted the server's SSL certificate (if necessary).

Warning

Gate One's SSL certificate must be trusted by clients in order to embed Gate One. This usually means purchasing an SSL certificate when you move to production.

Placement

You must decide where you want Gate One to appear in your application. The simplest way to do this is to create a DIV element like so:

<div id="gateone"></div>

By default #gateone will fill itself out to the full size of its parent element. So it is usually a good idea to wrap it inside of a container which has some explicit dimensions set:

<div style="width: 60em; height: 30em;">
    <div id="gateone"></div>
</div>

Include gateone.js

Before you can initialize Gate One you need to include gateone.js in the web page. You can, of course, just copy it out of /opt/gateone/static and include it in a <script> tag somewhere in your web page. However, to ensure that you're always running the latest version of gateone.js it is recommended that you source the script from the Gate One server itself:

<script src="https://your-gateone-server/static/gateone.js"></script>

Tip

You can also load the script dynamically via JS (if you know how).

Call GateOne.init()

The GateOne.init() function takes a number of optional arguments but for this example all we need is goURL.

GateOne.init({goURL: "https://your-gateone-server/"});

Put that somewhere in your window.onload function and you should see something like this:

Todo

Put an example image here and finish this tutorial to be like hello_embedded

API Authentication

Gate One includes an authentication API that can be used when embedded into other applications. It allows the application embedding Gate One to pre-authenticate users so they won't have to re-authenticate when their browser connects to the Gate One server. Here's how it works:

Enable API Authentication

Set auth = "api" in your server.conf:

# grep "^auth" server.conf
auth = "api"

Generate an API Key/Secret

# ./gateone.py --new_api_key
[I 120905 14:00:07 gateone:2679] A new API key has been generated: NDEzMWEwYTdlZTAzNDkxMWIwMDI4YzJmZTk4YzI4OWJjM
[I 120905 14:00:07 gateone:2680] This key can now be used to embed Gate One into other applications.

Note

The secret is not output to the terminal to avoid it being captured in session logs.

API keys and secrets are stored in your server.conf like so:

api_keys = "<API Key>:<API Secret>,<API Key>:<API Secret>,..."

You'll need to have a look at your server.conf to see what the 'secret' is:

# grep "^api_keys" server.conf
api_keys = "NDEzMWEwYTdlZTAzNDkxMWIwMDI4YzJmZTk4YzI4OWJjM:M2U5YTMxMGQ3OWNlNDJlMTg5NmY0NmUyOTk5MWYwYWFiN"

In the above example our API key would be, "NDEzMWEwYTdlZTAzNDkxMWIwMDI4YzJmZTk4YzI4OWJjM" and our API secret would be, "M2U5YTMxMGQ3OWNlNDJlMTg5NmY0NmUyOTk5MWYwYWFiN".

Tip

You can set the API Key and secret to whatever you like by editing your server.conf. By default they're random, 45-character strings but they can be any combination of characters other than colons and commas--even Unicode!. The following is a perfectly valid API key and secret:

ʕ•ᴥ•ʔ /人 ◕ ‿‿ ◕ 人\:↑ ♥‿♥

Note

To use Unicode strings in your server.conf you must have an encoding marked at the top of the file like so:

# -*- coding: utf-8 -*-

Generate An Auth Object

The next step is to generate a JSON object (auth) from your application and pass it to GateOne.init(). The 'auth' object must contain the following information:

api_key
The key that was generated when you ran ./gateone.py --new_api_key
upn
The username or userPrincipalName (aka UPN) of the user you wish to preauthenticate.
timestamp
A JavaScript-style timestamp: 13 characters representing the amount of seconds since the epoch (January 1, 1970)
signature
A valid HMAC signature that is generated from the api_key, upn, and timestamp (in that order).
signature_method
The HMAC signature method that was used to sign the authentication object. Currently, only HMAC-SHA1 is supported.
api_version
The version of Gate One's API authentication to use. Currently, only '1.0' is valid.

Here's an example 'auth' object:

authobj = {
    'api_key': 'MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M',
    'upn': 'joe@company.com',
    'timestamp': '1323391717238',
    'signature': "f6c6c82281f8d56797599aeee01a5e3efab05a63",
    'signature_method': 'HMAC-SHA1',
    'api_version': '1.0'
}

This object would then be passed to GateOne.init() like so:

GateOne.init({auth: authobj})

Assuming the signature is valid Gate One would then inherently trust that the user connecting over the WebSocket is joe@company.com.

Note

Authentication objects (aka "authentication tokens") are only valid within the time frame specified in the --api_timestamp_window setting. They also can't be used more than once (to negate replay attacks).

Example API Authentication Code

The following are examples demonstrating how to generate valid 'auth' objects in various programming languages.

Python

import time, hmac, hashlib, json
secret = "secret"
authobj = {
    'api_key': "MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M",
    'upn': "joe@company.com",
    'timestamp': str(int(time.time() * 1000)),
    'signature_method': 'HMAC-SHA1',
    'api_version': '1.0'
}
hash = hmac.new(secret, digestmod=hashlib.sha1)
hash.update(authobj['api_key'] + authobj['upn'] + authobj['timestamp'])
authobj['signature'] = hash.hexdigest()
valid_json_auth_object = json.dumps(authobj)

Here's a create_signature() function that can be used as a shortcut to those hash calls above:

def create_signature(secret, *parts):
    import hmac, hashlib
    hash = hmac.new(secret, digestmod=hashlib.sha1)
    for part in parts:
        hash.update(str(part))
    return hash.hexdigest()

...which could be used like so:

>>> create_signature(secret, api_key, upn, timestamp)
'f6c6c82281f8d56797599aeee01a5e3efab05a63'

PHP

$secret = 'secret'
$authobj = array(
    'api_key' => 'MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M',
    'upn' => $_SERVER['REMOTE_USER'],
    'timestamp' => time() . '0000',
    'signature_method' => 'HMAC-SHA1',
    'api_version' => '1.0'
);
$authobj['signature'] = hash_hmac('sha1', $authobj['api_key'] . $authobj['upn'] . $authobj['timestamp'], $secret);
$valid_json_auth_object = json_encode($authobj)

Ruby

require 'cgi'
require 'openssl'
require 'json'
secret = 'secret'
authobj = {
    'api_key' => 'MjkwYzc3MDI2MjhhNGZkNDg1MjJkODgyYjBmN2MyMTM4M',
    'upn' => 'joe@company.com',
    'timestamp' => (Time.now.getutc.to_i * 1000).inspect,
    'signature_method' => 'HMAC-SHA1',
    'api_version' => '1.0'
}
authobj['signature' = OpenSSL::HMAC.hexdigest('sha1', secret, authobj['api_key'] + authobj['upn'] + authobj['timestamp'])
valid_json_auth_object = JSON.generate(authobj)