Authentication

Security model

To establish the authenticity of a request, the server must perform a message integrity check, operating on a cryptographic primitive known as a HMAC (hash-based message authentication code). A MAC is attached to each request, in the form of an API-Access header. During dispatch, a subsequent code is computed from the request object using

  • a token (secure key) associated with the client application,
  • an incremental nonce (see below), and
  • the request method together with the path info.

The result of this operation is compared with the original MAC attached to the request, in order to verify its authenticity.

A valid key is a random, 40-character long, hexadecimal string.

53d5864520d65aa0364a52ddbb116ca78e0df8dc

Table schema

The trombone_keys table maintains client-key associations.

CREATE TABLE trombone_keys (
    id serial,
    client character varying(40) NOT NULL,
    key character varying(40) NOT NULL,
    nonce bigint NOT NULL
);

ALTER TABLE ONLY trombone_keys
    ADD CONSTRAINT trombone_keys PRIMARY KEY (id);

ALTER TABLE ONLY trombone_keys
    ADD CONSTRAINT unique_trombone_keys_client UNIQUE (client);

Note

This table is automatically created when the server starts with authentication enabled (i.e., in default mode), unless it already exists.

Authenticating client applications

In order for a client application to be granted access to the service, it must;

  1. be present in the trombone_keys table with a unique identifier and its secure token; as well as
  2. supply the following HTTP header with each request:
API-Access: <client_id>:<nonce>:<hash>

where <client_id> is replaced with the name of the application (as it appears in the trombone_keys table), and <hash> with the MAC code obtained by hashing a concatenated string – the constituents of which are given below, using the HMAC-SHA1 algorithm and aforementioned key.

The <nonce> is an integer value introduced to prevent an adversary from reusing a hash under a, so called, replay attack. The client implementation must therefore ensure that the nonce is strictly increasing for each request. This can be achieved using a timestamp, such as the one used in the reference implementation.

Hash string format

The format of the string given as input to the hashing algorithm must be as follows:

<client_id>:<method>:<uri>:<nonce>:<json_body>

SHA1 implementations are available for many programming languages. The following have been tested with Trombone:

JavaScript https://code.google.com/p/crypto-js/
Haskell http://hackage.haskell.org/package/Crypto/docs/Data-HMAC.html

For complete, working examples, see Reference Implementations.

Client key administration

Trombone includes the keyman utility, which can be used for command line administration of client keys.

See Tools & Utilities.

Disable HMAC authentication

Message authentication can be disabled with the -x command line switch. Doing so in a production setting is not recommended.

Warning

Deactivating message authentication gives everyone access to your server interface. To mitigate the risk of unauthorized access to production data, only use the -x flag in a safe environment.

Allowing access from localhost

To bypass HMAC authentication specifically for requests originating from the local host, instead use the -t, or --trust-localhost option.

Reference Implementations

CREATE DATABASE basic_auth_demo;

\c basic_auth_demo
CREATE TABLE IF NOT EXISTS utilities (
    id        serial PRIMARY KEY,
    name      character varying(255)       NOT NULL,
    summary   character varying(255)       NOT NULL
);

INSERT INTO utilities (name, summary) VALUES
    ('ls',   'list directory contents'),
    ('htop', 'interactive process viewer'),
    ('df',   'report file system disk usage'),
    ('pwd',  'print name of current/working directory'),
    ('awk',  'pattern scanning and text processing language');

CREATE TABLE IF NOT EXISTS trombone_config (
    id        serial PRIMARY KEY,
    key       character varying(40) UNIQUE NOT NULL,
    val       text                         NOT NULL
);

INSERT INTO trombone_config (key, val) VALUES
    ('routes', E'GET /utils >> SELECT * FROM utilities\nPOST /util <> INSERT INTO utilities (name, summary) VALUES ({{name}}, {{summary}})');

Create a file basic-keyman.conf:

host     = 'localhost'
port     =  5432
dbname   = 'basic_auth_demo'
user     = 'postgres'
password = 'postgres'

(Modify the file as required.)

$ ./keyman register demo -c basic-keyman.conf

Client registered:
demo: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Start the server

$ trombone -d basic_auth_demo -C

JavaScript

Insert the generated demo key on line 15.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// auth-example.js

$(document).ready(function() {

    var render = function(obj) {
        $('#response').html('<pre>' + JSON.stringify(obj, null, 4) + '</pre>');
    };

    var onError = function(e) {
        render(JSON.parse(e.responseText));
    };

    var defaults = {
        host     : 'http://localhost:3010',
        key      : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
        client   : 'demo',
        type     : 'GET',
        error    : onError
    };

    $('#insert-action').click(function() {

        var name    = $('#insert-title').val(),
            summary = $('#insert-description').val();

        if (!summary || !name) {
            $('#response').html('Please fill out both fields.');
            return;
        }

        var obj = {
            summary : summary,
            name    : name
        };

        Trombone.request($.extend({}, defaults, {
            data     : obj,
            nonce    : Date.now()/10 | 0,
            type     : 'POST',
            resource : 'util',
            success  : function() {
                $('#response').html('Ok.');
            }
        }));

    });

    $('#request-action').click(function() {

        Trombone.request($.extend({}, defaults, {
            nonce    : Date.now()/10 | 0,
            resource : 'utils',
            success  : render
        }));

    });
});
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Trombone data access service example: Request authentication</title>
    </head>
    <body>

        <div>
            <a id="request-action" href="javascript:">Request some data</a>
        </div>
        <div>
            <div><input id="insert-title" type="text"></div>
            <div><textarea id="insert-description"></textarea></div>
            <div><a id="insert-action" href="javascript:">Insert some data</a></div>
        </div>
        <div id="response"></div>

        <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
        <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
        <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>
        <script src="js/trombone.request.min.js"></script>
        <script src="js/auth-example.js"></script>
    </body>
</html>

Haskell

@todo

Purescript

@todo

C++/Qt

@todo