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.
The 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 in 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 most 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