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 30api_keys.conf like so:

{
    "*": {
        "gateone": {
            "api_keys": {
                "<API Key>": "<Secret>",
                "<API Key 2>": "<Secret 2>"
            }
        }
    }
}

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

# cat settings/30api_keys.conf
{
    "*": {
        "gateone": {
            "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 30api_keys.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:

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

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 digits representing the amount of milliseconds 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() * 1000,
    '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)