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;
- be present in the trombone_keys table with a unique identifier and its secure token; as well as
- 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