Logs Automatically purges themselves

This commit is contained in:
Luis Fernando Molina committed 2018-03-17 17:04:07 +01:00
1 parent fda24f934c
commit a42174b233
632 files changed
+19952 -20039

No files matched your search

+5 -20
View File
@@ -1,20 +1,5 @@
.idea
*.iml
npm-debug.log
dump.rdb
node_modules
components
build
results.tap
results.xml
npm-shrinkwrap.json
config.json
.DS_Store
*/.DS_Store
*/*/.DS_Store
._*
*/._*
*/*/._*
coverage.*
lib-cov
*
!lib/**
!dist/**
!client.js
!.npmignore
-5
View File
@@ -1,5 +0,0 @@
language: node_js
node_js:
- 0.10
Generated Vendored
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2012-2014, Eran Hammer and other contributors.
Copyright (c) 2012-2017, Eran Hammer and Project contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
+56 -53
View File
@@ -3,11 +3,11 @@
<img align="right" src="https://raw.github.com/hueniverse/hawk/master/images/logo.png" /> **Hawk** is an HTTP authentication scheme using a message authentication code (MAC) algorithm to provide partial
HTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](https://github.com/hueniverse/oz).
Current version: **3.x**
Current version: **6.x**
Note: 3.x and 2.x are the same exact protocol as 1.1. The version increments reflect changes in the node API.
Note: 6.x, 5.x, 4.x, 3.x, and 2.x are the same exact protocol as 1.1. The version increments reflect changes in the node API.
[![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)
[![Build Status](https://travis-ci.org/hueniverse/hawk.svg?branch=master)](https://travis-ci.org/hueniverse/hawk)
# Table of Content
@@ -18,10 +18,8 @@ Note: 3.x and 2.x are the same exact protocol as 1.1. The version increments ref
- [Payload Validation](#payload-validation)
- [Response Payload Validation](#response-payload-validation)
- [Browser Support and Considerations](#browser-support-and-considerations)
<p></p>
- [**Single URI Authorization**](#single-uri-authorization)
- [Usage Example](#bewit-usage-example)
<p></p>
- [**Security Considerations**](#security-considerations)
- [MAC Keys Transmission](#mac-keys-transmission)
- [Confidentiality of Requests](#confidentiality-of-requests)
@@ -33,9 +31,7 @@ Note: 3.x and 2.x are the same exact protocol as 1.1. The version increments ref
- [Client Clock Poisoning](#client-clock-poisoning)
- [Bewit Limitations](#bewit-limitations)
- [Host Header Forgery](#host-header-forgery)
<p></p>
- [**Frequently Asked Questions**](#frequently-asked-questions)
<p></p>
- [**Implementations**](#implementations)
- [**Acknowledgements**](#acknowledgements)
@@ -75,14 +71,14 @@ and the server.
## Replay Protection
Without replay protection, an attacker can use a compromised (but otherwise valid and authenticated) request more
than once, gaining access to a protected resource. To mitigate this, clients include both a nonce and a timestamp when
Without replay protection, an attacker can use a compromised (but otherwise valid and authenticated) request more
than once, gaining access to a protected resource. To mitigate this, clients include both a nonce and a timestamp when
making requests. This gives the server enough information to prevent replay attacks.
The nonce is generated by the client, and is a string unique across all requests with the same timestamp and
key identifier combination.
key identifier combination.
The timestamp enables the server to restrict the validity period of the credentials where requests occuring afterwards
The timestamp enables the server to restrict the validity period of the credentials where requests occurring afterwards
are rejected. It also removes the need for the server to retain an unbounded number of nonce values for future checks.
By default, **Hawk** uses a time window of 1 minute to allow for time skew between the client and server (which in
practice translates to a maximum of 2 minutes as the skew can be positive or negative).
@@ -103,15 +99,15 @@ the number of round trips required to authenticate the first request.
Server code:
```javascript
var Http = require('http');
var Hawk = require('hawk');
const Http = require('http');
const Hawk = require('hawk');
// Credentials lookup function
var credentialsFunc = function (id, callback) {
const credentialsFunc = function (id, callback) {
var credentials = {
const credentials = {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'Steve'
@@ -122,20 +118,20 @@ var credentialsFunc = function (id, callback) {
// Create HTTP server
var handler = function (req, res) {
const handler = function (req, res) {
// Authenticate incoming request
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
Hawk.server.authenticate(req, credentialsFunc, {}, (err, credentials, artifacts) => {
// Prepare response
var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
var headers = { 'Content-Type': 'text/plain' };
const payload = (!err ? `Hello ${credentials.user} ${artifacts.ext}` : 'Shoosh!');
const headers = { 'Content-Type': 'text/plain' };
// Generate Server-Authorization response header
var header = Hawk.server.header(credentials, artifacts, { payload: payload, contentType: headers['Content-Type'] });
const header = Hawk.server.header(credentials, artifacts, { payload, contentType: headers['Content-Type'] });
headers['Server-Authorization'] = header;
// Send the response back
@@ -153,13 +149,13 @@ Http.createServer(handler).listen(8000, 'example.com');
Client code:
```javascript
var Request = require('request');
var Hawk = require('hawk');
const Request = require('request');
const Hawk = require('hawk');
// Client credentials
var credentials = {
const credentials = {
id: 'dh37fgj492je',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
@@ -167,7 +163,7 @@ var credentials = {
// Request options
var requestOptions = {
const requestOptions = {
uri: 'http://example.com:8000/resource/1?b=1&a=2',
method: 'GET',
headers: {}
@@ -175,7 +171,7 @@ var requestOptions = {
// Generate Authorization request header
var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });
const header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'some-app-data' });
requestOptions.headers.Authorization = header.field;
// Send authenticated request
@@ -184,16 +180,16 @@ Request(requestOptions, function (error, response, body) {
// Authenticate the server's response
var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
const isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
// Output results
console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
console.log(`${response.statusCode}: ${body}` + (isValid ? ' (valid)' : ' (invalid)'));
});
```
**Hawk** utilized the [**SNTP**](https://github.com/hueniverse/sntp) module for time sync management. By default, the local
machine time is used. To automatically retrieve and synchronice the clock within the application, use the SNTP 'start()' method.
machine time is used. To automatically retrieve and synchronize the clock within the application, use the SNTP 'start()' method.
```javascript
Hawk.sntp.start();
@@ -217,12 +213,13 @@ HTTP/1.1 401 Unauthorized
WWW-Authenticate: Hawk
```
The client has previously obtained a set of **Hawk** credentials for accessing resources on the "http://example.com/"
The client has previously obtained a set of **Hawk** credentials for accessing resources on the "`http://example.com/`"
server. The **Hawk** credentials issued to the client include the following attributes:
* Key identifier: dh37fgj492je
* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn
* Algorithm: sha256
* Key identifier: `dh37fgj492je`
* Key: `werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn`
* Algorithm: `hmac sha256`
* Hash: `6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=`
The client generates the authentication header by calculating a timestamp (e.g. the number of seconds since January 1,
1970 00:00:00 GMT), generating a nonce, and constructing the normalized request string (each value followed by a newline
@@ -241,7 +238,7 @@ some-app-ext-data
```
The request MAC is calculated using HMAC with the specified hash algorithm "sha256" and the key over the normalized request string.
The request MAC is calculated using HMAC with the specified hash algorithm "`sha256`" and the key over the normalized request string.
The result is base64-encoded to produce the request MAC:
```
@@ -273,7 +270,8 @@ For example:
* Payload: `Thank you for flying Hawk`
* Content Type: `text/plain`
* Hash (sha256): `Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=`
* Algorithm: `sha256`
* Hash: `Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=`
Results in the following input to the payload hash function (newline terminated values):
@@ -314,7 +312,7 @@ Host: example.com:8000
Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="
```
It is up to the server if and when it validates the payload for any given request, based solely on it's security policy
It is up to the server if and when it validates the payload for any given request, based solely on its security policy
and the nature of the data included.
If the payload is available at the time of authentication, the server uses the hash value provided by the client to construct
@@ -336,7 +334,7 @@ by the client, the payload may be modified by an attacker.
client to authenticate the response and ensure it is talking to the right server. **Hawk** defines the HTTP `Server-Authorization` header
as a response header using the exact same syntax as the `Authorization` request header field.
The header is contructed using the same process as the client's request header. The server uses the same credentials and other
The header is constructed using the same process as the client's request header. The server uses the same credentials and other
artifacts provided by the client to constructs the normalized request string. The `ext` and `hash` values are replaced with
new values based on the server response. The rest as identical to those used by the client.
@@ -373,7 +371,7 @@ and for a finite period of time. Both the client and server can issue bewit cred
credentials as the client to maintain clear traceability as to who issued which credentials.
In order to simplify implementation, bewit credentials do not support single-use policy and can be replayed multiple times within
the granted access timeframe.
the granted access timeframe.
## Bewit Usage Example
@@ -381,15 +379,15 @@ the granted access timeframe.
Server code:
```javascript
var Http = require('http');
var Hawk = require('hawk');
const Http = require('http');
const Hawk = require('hawk');
// Credentials lookup function
var credentialsFunc = function (id, callback) {
const credentialsFunc = function (id, callback) {
var credentials = {
const credentials = {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
};
@@ -399,9 +397,9 @@ var credentialsFunc = function (id, callback) {
// Create HTTP server
var handler = function (req, res) {
const handler = function (req, res) {
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
Hawk.uri.authenticate(req, credentialsFunc, {}, (err, credentials, attributes) => {
res.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' });
res.end(!err ? 'Access granted' : 'Shoosh!');
@@ -414,13 +412,13 @@ Http.createServer(handler).listen(8000, 'example.com');
Bewit code generation:
```javascript
var Request = require('request');
var Hawk = require('hawk');
const Request = require('request');
const Hawk = require('hawk');
// Client credentials
var credentials = {
const credentials = {
id: 'dh37fgj492je',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
@@ -428,9 +426,13 @@ var credentials = {
// Generate bewit
var duration = 60 * 5; // 5 Minutes
var bewit = Hawk.uri.getBewit('http://example.com:8080/resource/1?b=1&a=2', { credentials: credentials, ttlSec: duration, ext: 'some-app-data' });
var uri = 'http://example.com:8000/resource/1?b=1&a=2' + '&bewit=' + bewit;
const duration = 60 * 5; // 5 Minutes
const bewit = Hawk.uri.getBewit('http://example.com:8000/resource/1?b=1&a=2', { credentials: credentials, ttlSec: duration, ext: 'some-app-data' });
const uri = 'http://example.com:8000/resource/1?b=1&a=2' + '&bewit=' + bewit;
// Output URI
console.log('URI: ' + uri);
```
@@ -496,9 +498,9 @@ which can often affect how the request body is interpreted by the server. If the
or value of such headers, an attacker can manipulate the request headers without being detected. Implementers should use the
`ext` feature to pass application-specific information via the `Authorization` header which is protected by the request MAC.
The response authentication, when performed, only covers the response payload, content-type, and the request information
provided by the client in it's request (method, resource, timestamp, nonce, etc.). It does not cover the HTTP status code or
any other response header field (e.g. Location) which can affect the client's behaviour.
The response authentication, when performed, only covers the response payload, content-type, and the request information
provided by the client in its request (method, resource, timestamp, nonce, etc.). It does not cover the HTTP status code or
any other response header field (e.g. `Location`) which can affect the client's behaviour.
### Future Time Manipulation
@@ -531,7 +533,7 @@ and sensitive information.
### Host Header Forgery
Hawk validates the incoming request MAC against the incoming HTTP Host header. However, unless the optional `host` and `port`
options are used with `server.authenticate()`, a malicous client can mint new host names pointing to the server's IP address and
options are used with `server.authenticate()`, a malicious client can mint new host names pointing to the server's IP address and
use that to craft an attack by sending a valid request that's meant for another hostname than the one used by the server. Server
implementors must manually verify that the host header received matches their expectation (or use the options mentioned above).
@@ -623,6 +625,7 @@ of delegating access to a third party. If you are looking for an OAuth alternati
- [Tent Hawk in Ruby](https://github.com/tent/hawk-ruby)
- [Wealdtech in Java](https://github.com/wealdtech/hawk)
- [Kumar's Mohawk in Python](https://github.com/kumar303/mohawk/)
- [Hiyosi in Go](https://github.com/hiyosi/hawk)
# Acknowledgements
-24
View File
@@ -1,24 +0,0 @@
{
"name": "hawk",
"main": "lib/browser.js",
"license": "./LICENSE",
"ignore": [
"!lib",
"lib/*",
"!lib/browser.js",
"index.js"
],
"keywords": [
"http",
"authentication",
"scheme",
"hawk"
],
"authors": [
"Eran Hammer <eran@hammer.io>"
],
"repository": {
"type": "git",
"url": "git://github.com/hueniverse/hawk.git"
}
}
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./dist/browser');
-19
View File
@@ -1,19 +0,0 @@
{
"name": "hawk",
"repo": "hueniverse/hawk",
"description": "HTTP Hawk Authentication Scheme",
"version": "1.0.0",
"keywords": [
"http",
"authentication",
"scheme",
"hawk"
],
"dependencies": {},
"development": {},
"license": "BSD",
"main": "lib/browser.js",
"scripts": [
"lib/browser.js"
]
}
+793
View File
@@ -0,0 +1,793 @@
'use strict';
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
// Declare namespace
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var hawk = {
internals: {}
};
hawk.client = {
// Generate an Authorization header for a given request
/*
uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/
header: function header(uri, method, options) {
var result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !method || typeof method !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
result.err = 'Invalid argument type';
return result;
}
// Application time
var timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
result.err = 'Invalid credentials object';
return result;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}
// Parse URI
if (typeof uri === 'string') {
uri = hawk.utils.parseUri(uri);
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
method: method,
resource: uri.resource,
host: uri.host,
port: uri.port,
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash && (options.payload || options.payload === '')) {
artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = hawk.crypto.calculateMac('header', credentials, artifacts);
// Construct header
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id + '", ts="' + artifacts.ts + '", nonce="' + artifacts.nonce + (artifacts.hash ? '", hash="' + artifacts.hash : '') + (hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') + '", mac="' + mac + '"';
if (artifacts.app) {
header += ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
},
// Generate a bewit value for a given URI
/*
uri: 'http://example.com/resource?a=b'
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/
bewit: function bewit(uri, options) {
// Validate inputs
if (!uri || typeof uri !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || !options.ttlSec) {
return '';
}
options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value
// Application time
var now = hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
return '';
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Parse URI
uri = hawk.utils.parseUri(uri);
// Calculate signature
var exp = now + options.ttlSec;
var mac = hawk.crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.resource, // Maintain trailing '?' and query params
host: uri.host,
port: uri.port,
ext: options.ext
});
// Construct bewit: id\exp\mac\ext
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return hawk.utils.base64urlEncode(bewit);
},
// Validate server response
/*
request: object created via 'new XMLHttpRequest()' after response received or fetch API 'Response'
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/
authenticate: function authenticate(request, credentials, artifacts, options) {
options = options || {};
var getHeader = function getHeader(name) {
// Fetch API or plain headers
if (request.headers) {
return typeof request.headers.get === 'function' ? request.headers.get(name) : request.headers[name];
}
// XMLHttpRequest
return request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name);
};
var wwwAuthenticate = getHeader('www-authenticate');
if (wwwAuthenticate) {
// Parse HTTP WWW-Authenticate header
var wwwAttributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
if (!wwwAttributes) {
return false;
}
if (wwwAttributes.ts) {
var tsm = hawk.crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
hawk.utils.setNtpSecOffset(wwwAttributes.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
}
// Parse HTTP Server-Authorization header
var serverAuthorization = getHeader('server-authorization');
if (!serverAuthorization && !options.required) {
return true;
}
var attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
if (!attributes) {
return false;
}
var modArtifacts = {
ts: artifacts.ts,
nonce: artifacts.nonce,
method: artifacts.method,
resource: artifacts.resource,
host: artifacts.host,
port: artifacts.port,
hash: attributes.hash,
ext: attributes.ext,
app: artifacts.app,
dlg: artifacts.dlg
};
var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
if (mac !== attributes.mac) {
return false;
}
if (!options.payload && options.payload !== '') {
return true;
}
if (!attributes.hash) {
return false;
}
var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
return calculatedHash === attributes.hash;
},
message: function message(host, port, _message, options) {
// Validate inputs
if (!host || typeof host !== 'string' || !port || typeof port !== 'number' || _message === null || _message === undefined || typeof _message !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
return null;
}
// Application time
var timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
// Invalid credential object
return null;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
host: host,
port: port,
hash: hawk.crypto.calculatePayloadHash(_message, credentials.algorithm)
};
// Construct authorization
var result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: hawk.crypto.calculateMac('message', credentials, artifacts)
};
return result;
},
authenticateTimestamp: function authenticateTimestamp(message, credentials, updateClock) {
// updateClock defaults to true
var tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
if (tsm !== message.tsm) {
return false;
}
if (updateClock !== false) {
hawk.utils.setNtpSecOffset(message.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
return true;
}
};
hawk.crypto = {
headerVersion: '1',
algorithms: ['sha1', 'sha256'],
calculateMac: function calculateMac(type, credentials, options) {
var normalized = hawk.crypto.generateNormalizedString(type, options);
var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
return hmac.toString(CryptoJS.enc.Base64);
},
generateNormalizedString: function generateNormalizedString(type, options) {
var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' + options.ts + '\n' + options.nonce + '\n' + (options.method || '').toUpperCase() + '\n' + (options.resource || '') + '\n' + options.host.toLowerCase() + '\n' + options.port + '\n' + (options.hash || '') + '\n';
if (options.ext) {
normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
}
normalized += '\n';
if (options.app) {
normalized += options.app + '\n' + (options.dlg || '') + '\n';
}
return normalized;
},
calculatePayloadHash: function calculatePayloadHash(payload, algorithm, contentType) {
var hash = CryptoJS.algo[algorithm.toUpperCase()].create();
hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
hash.update(hawk.utils.parseContentType(contentType) + '\n');
hash.update(payload);
hash.update('\n');
return hash.finalize().toString(CryptoJS.enc.Base64);
},
calculateTsMac: function calculateTsMac(ts, credentials) {
var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
return hash.toString(CryptoJS.enc.Base64);
}
};
// localStorage compatible interface
hawk.internals.LocalStorage = function () {
this._cache = {};
this.length = 0;
this.getItem = function (key) {
return this._cache.hasOwnProperty(key) ? String(this._cache[key]) : null;
};
this.setItem = function (key, value) {
this._cache[key] = String(value);
this.length = Object.keys(this._cache).length;
};
this.removeItem = function (key) {
delete this._cache[key];
this.length = Object.keys(this._cache).length;
};
this.clear = function () {
this._cache = {};
this.length = 0;
};
this.key = function (i) {
return Object.keys(this._cache)[i || 0];
};
};
hawk.utils = {
storage: new hawk.internals.LocalStorage(),
setStorage: function setStorage(storage) {
var ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
hawk.utils.storage = storage;
if (ntpOffset) {
hawk.utils.setNtpSecOffset(ntpOffset);
}
},
setNtpSecOffset: function setNtpSecOffset(offset) {
try {
hawk.utils.storage.setItem('hawk_ntp_offset', offset);
} catch (err) {
console.error('[hawk] could not write to storage.');
console.error(err);
}
},
getNtpSecOffset: function getNtpSecOffset() {
var offset = hawk.utils.storage.getItem('hawk_ntp_offset');
if (!offset) {
return 0;
}
return parseInt(offset, 10);
},
now: function now(localtimeOffsetMsec) {
return Date.now() + (localtimeOffsetMsec || 0) + hawk.utils.getNtpSecOffset() * 1000;
},
nowSec: function nowSec(localtimeOffsetMsec) {
return Math.floor(hawk.utils.now(localtimeOffsetMsec) / 1000);
},
escapeHeaderAttribute: function escapeHeaderAttribute(attribute) {
return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
},
parseContentType: function parseContentType(header) {
if (!header) {
return '';
}
return header.split(';')[0].replace(/^\s+|\s+$/g, '').toLowerCase();
},
parseAuthorizationHeader: function parseAuthorizationHeader(header, keys) {
if (!header) {
return null;
}
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
if (!headerParts) {
return null;
}
var scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return null;
}
var attributesString = headerParts[2];
if (!attributesString) {
return null;
}
var attributes = {};
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
// Check valid attribute names
if (keys.indexOf($1) === -1) {
return;
}
// Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
return;
}
// Check for duplicates
if (attributes.hasOwnProperty($1)) {
return;
}
attributes[$1] = $2;
return '';
});
if (verify !== '') {
return null;
}
return attributes;
},
randomString: function randomString(size) {
var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var len = randomSource.length;
var result = [];
for (var i = 0; i < size; ++i) {
result[i] = randomSource[Math.floor(Math.random() * len)];
}
return result.join('');
},
// 1 2 3 4
uriRegex: /^([^:]+)\:\/\/(?:[^@/]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/, // scheme://credentials@host:port/resource#fragment
parseUri: function parseUri(input) {
var parts = input.match(hawk.utils.uriRegex);
if (!parts) {
return { host: '', port: '', resource: '' };
}
var scheme = parts[1].toLowerCase();
var uri = {
host: parts[2],
port: parts[3] || (scheme === 'http' ? '80' : scheme === 'https' ? '443' : ''),
resource: parts[4]
};
return uri;
},
base64urlEncode: function base64urlEncode(value) {
var wordArray = CryptoJS.enc.Utf8.parse(value);
var encoded = CryptoJS.enc.Base64.stringify(wordArray);
return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
};
// $lab:coverage:off$
/* eslint-disable */
// Based on: Crypto-JS v3.1.2
// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
// http://code.google.com/p/crypto-js/
// http://code.google.com/p/crypto-js/wiki/License
var CryptoJS = CryptoJS || function (h, r) {
var k = {},
l = k.lib = {},
n = function n() {},
f = l.Base = { extend: function extend(a) {
n.prototype = this;var b = new n();a && b.mixIn(a);b.hasOwnProperty("init") || (b.init = function () {
b.$super.init.apply(this, arguments);
});b.init.prototype = b;b.$super = this;return b;
}, create: function create() {
var a = this.extend();a.init.apply(a, arguments);return a;
}, init: function init() {}, mixIn: function mixIn(a) {
for (var _b in a) {
a.hasOwnProperty(_b) && (this[_b] = a[_b]);
}a.hasOwnProperty("toString") && (this.toString = a.toString);
}, clone: function clone() {
return this.init.prototype.extend(this);
} },
j = l.WordArray = f.extend({ init: function init(a, b) {
a = this.words = a || [];this.sigBytes = b != r ? b : 4 * a.length;
}, toString: function toString(a) {
return (a || s).stringify(this);
}, concat: function concat(a) {
var b = this.words,
d = a.words,
c = this.sigBytes;a = a.sigBytes;this.clamp();if (c % 4) for (var e = 0; e < a; e++) {
b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4);
} else if (65535 < d.length) for (var _e = 0; _e < a; _e += 4) {
b[c + _e >>> 2] = d[_e >>> 2];
} else b.push.apply(b, d);this.sigBytes += a;return this;
}, clamp: function clamp() {
var a = this.words,
b = this.sigBytes;a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4);a.length = h.ceil(b / 4);
}, clone: function clone() {
var a = f.clone.call(this);a.words = this.words.slice(0);return a;
}, random: function random(a) {
for (var _b2 = [], d = 0; d < a; d += 4) {
_b2.push(4294967296 * h.random() | 0);
}return new j.init(b, a);
} }),
m = k.enc = {},
s = m.Hex = { stringify: function stringify(a) {
var b = a.words;a = a.sigBytes;for (var d = [], c = 0; c < a; c++) {
var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255;d.push((e >>> 4).toString(16));d.push((e & 15).toString(16));
}return d.join("");
}, parse: function parse(a) {
for (var b = a.length, d = [], c = 0; c < b; c += 2) {
d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8);
}return new j.init(d, b / 2);
} },
p = m.Latin1 = { stringify: function stringify(a) {
var b = a.words;a = a.sigBytes;for (var d = [], c = 0; c < a; c++) {
d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255));
}return d.join("");
}, parse: function parse(a) {
for (var b = a.length, d = [], c = 0; c < b; c++) {
d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4);
}return new j.init(d, b);
} },
t = m.Utf8 = { stringify: function stringify(a) {
try {
return decodeURIComponent(escape(p.stringify(a)));
} catch (b) {
throw Error("Malformed UTF-8 data");
}
}, parse: function parse(a) {
return p.parse(unescape(encodeURIComponent(a)));
} },
q = l.BufferedBlockAlgorithm = f.extend({ reset: function reset() {
this._data = new j.init();this._nDataBytes = 0;
}, _append: function _append(a) {
"string" == typeof a && (a = t.parse(a));this._data.concat(a);this._nDataBytes += a.sigBytes;
}, _process: function _process(a) {
var b = this._data,
d = b.words,
c = b.sigBytes,
e = this.blockSize,
f = c / (4 * e),
f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0);a = f * e;c = h.min(4 * a, c);if (a) {
for (var g = 0; g < a; g += e) {
this._doProcessBlock(d, g);
}g = d.splice(0, a);b.sigBytes -= c;
}return new j.init(g, c);
}, clone: function clone() {
var a = f.clone.call(this);a._data = this._data.clone();return a;
}, _minBufferSize: 0 });l.Hasher = q.extend({ cfg: f.extend(), init: function init(a) {
this.cfg = this.cfg.extend(a);this.reset();
}, reset: function reset() {
q.reset.call(this);this._doReset();
}, update: function update(a) {
this._append(a);this._process();return this;
}, finalize: function finalize(a) {
a && this._append(a);return this._doFinalize();
}, blockSize: 16, _createHelper: function _createHelper(a) {
return function (b, d) {
return new a.init(d).finalize(b);
};
}, _createHmacHelper: function _createHmacHelper(a) {
return function (b, d) {
return new u.HMAC.init(a, d).finalize(b);
};
} });var u = k.algo = {};return k;
}(Math);
(function () {
var k = CryptoJS,
b = k.lib,
m = b.WordArray,
l = b.Hasher,
d = [],
b = k.algo.SHA1 = l.extend({ _doReset: function _doReset() {
this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]);
}, _doProcessBlock: function _doProcessBlock(n, p) {
for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) {
if (16 > c) d[c] = n[p + c] | 0;else {
var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16];d[c] = g << 1 | g >>> 31;
}g = (e << 5 | e >>> 27) + b + d[c];g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514);b = j;j = h;h = f << 30 | f >>> 2;f = e;e = g;
}a[0] = a[0] + e | 0;a[1] = a[1] + f | 0;a[2] = a[2] + h | 0;a[3] = a[3] + j | 0;a[4] = a[4] + b | 0;
}, _doFinalize: function _doFinalize() {
var b = this._data,
d = b.words,
a = 8 * this._nDataBytes,
e = 8 * b.sigBytes;d[e >>> 5] |= 128 << 24 - e % 32;d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296);d[(e + 64 >>> 9 << 4) + 15] = a;b.sigBytes = 4 * d.length;this._process();return this._hash;
}, clone: function clone() {
var b = l.clone.call(this);b._hash = this._hash.clone();return b;
} });k.SHA1 = l._createHelper(b);k.HmacSHA1 = l._createHmacHelper(b);
})();
(function (k) {
for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function u(q) {
return 4294967296 * (q - (q | 0)) | 0;
}, l = 2, b = 0; 64 > b;) {
var d;a: {
d = l;for (var w = k.sqrt(d), r = 2; r <= w; r++) {
if (!(d % r)) {
d = !1;break a;
}
}d = !0;
}d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++);l++;
}var n = [],
h = h.SHA256 = j.extend({ _doReset: function _doReset() {
this._hash = new v.init(s.slice(0));
}, _doProcessBlock: function _doProcessBlock(q, h) {
for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) {
if (16 > e) n[e] = q[h + e] | 0;else {
var m = n[e - 15],
p = n[e - 2];n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16];
}m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e];p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b);l = j;j = g;g = f;f = k + m | 0;k = b;b = d;d = c;c = m + p | 0;
}a[0] = a[0] + c | 0;a[1] = a[1] + d | 0;a[2] = a[2] + b | 0;a[3] = a[3] + k | 0;a[4] = a[4] + f | 0;a[5] = a[5] + g | 0;a[6] = a[6] + j | 0;a[7] = a[7] + l | 0;
}, _doFinalize: function _doFinalize() {
var d = this._data,
b = d.words,
a = 8 * this._nDataBytes,
c = 8 * d.sigBytes;b[c >>> 5] |= 128 << 24 - c % 32;b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296);b[(c + 64 >>> 9 << 4) + 15] = a;d.sigBytes = 4 * b.length;this._process();return this._hash;
}, clone: function clone() {
var b = j.clone.call(this);b._hash = this._hash.clone();return b;
} });g.SHA256 = j._createHelper(h);g.HmacSHA256 = j._createHmacHelper(h);
})(Math);
(function () {
var c = CryptoJS,
k = c.enc.Utf8;c.algo.HMAC = c.lib.Base.extend({ init: function init(a, b) {
a = this._hasher = new a.init();"string" == typeof b && (b = k.parse(b));var c = a.blockSize,
e = 4 * c;b.sigBytes > e && (b = a.finalize(b));b.clamp();for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) {
h[d] ^= 1549556828, j[d] ^= 909522486;
}f.sigBytes = g.sigBytes = e;this.reset();
}, reset: function reset() {
var a = this._hasher;a.reset();a.update(this._iKey);
}, update: function update(a) {
this._hasher.update(a);return this;
}, finalize: function finalize(a) {
var b = this._hasher;a = b.finalize(a);b.reset();return b.finalize(this._oKey.clone().concat(a));
} });
})();
(function () {
var h = CryptoJS,
j = h.lib.WordArray;h.enc.Base64 = { stringify: function stringify(b) {
var e = b.words,
f = b.sigBytes,
c = this._map;b.clamp();b = [];for (var a = 0; a < f; a += 3) {
for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) {
b.push(c.charAt(d >>> 6 * (3 - g) & 63));
}
}if (e = c.charAt(64)) for (; b.length % 4;) {
b.push(e);
}return b.join("");
}, parse: function parse(b) {
var e = b.length,
f = this._map,
c = f.charAt(64);c && (c = b.indexOf(c), -1 != c && (e = c));for (var c = [], a = 0, d = 0; d < e; d++) {
if (d % 4) {
var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4),
h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4);c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4);a++;
}
}return j.create(c, a);
}, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" };
})();
hawk.crypto.utils = CryptoJS;
// Export if used as a module
if (typeof module !== 'undefined' && module.exports) {
module.exports = hawk;
}
/* eslint-enable */
// $lab:coverage:on$
-343
View File
@@ -1,343 +0,0 @@
'use strict'
// Load modules
;
var _typeof = function (obj) {
return obj && typeof Symbol !== 'undefined' && obj.constructor === Symbol ? 'symbol' : typeof obj;
};
var Url = require('url');
var Hoek = require('hoek');
var Cryptiles = require('cryptiles');
var Crypto = require('./crypto');
var Utils = require('./utils');
// Declare internals
var internals = {};
// Generate an Authorization header for a given request
/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now(), // A pre-calculated timestamp
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/
exports.header = function (uri, method, options) {
var result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !method || typeof method !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
result.err = 'Invalid argument type';
return result;
}
// Application time
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
result.err = 'Invalid credential object';
return result;
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}
// Parse URI
if (typeof uri === 'string') {
uri = Url.parse(uri);
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
method: method,
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash && (options.payload || options.payload === '')) {
artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = Crypto.calculateMac('header', credentials, artifacts);
// Construct header
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id + '", ts="' + artifacts.ts + '", nonce="' + artifacts.nonce + (artifacts.hash ? '", hash="' + artifacts.hash : '') + (hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') + '", mac="' + mac + '"';
if (artifacts.app) {
header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
};
// Validate server response
/*
res: node's response object
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/
exports.authenticate = function (res, credentials, artifacts, options) {
artifacts = Hoek.clone(artifacts);
options = options || {};
if (res.headers['www-authenticate']) {
// Parse HTTP WWW-Authenticate header
var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
if (wwwAttributes instanceof Error) {
return false;
}
// Validate server timestamp (not used to update clock since it is done via the SNPT client)
if (wwwAttributes.ts) {
var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
}
}
// Parse HTTP Server-Authorization header
if (!res.headers['server-authorization'] && !options.required) {
return true;
}
var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (attributes instanceof Error) {
return false;
}
artifacts.ext = attributes.ext;
artifacts.hash = attributes.hash;
var mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== attributes.mac) {
return false;
}
if (!options.payload && options.payload !== '') {
return true;
}
if (!attributes.hash) {
return false;
}
var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
return calculatedHash === attributes.hash;
};
// Generate a bewit value for a given URI
/*
uri: 'http://example.com/resource?a=b' or object from Url.parse()
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/
exports.getBewit = function (uri, options) {
// Validate inputs
if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || !options.ttlSec) {
return '';
}
options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value
// Application time
var now = Utils.now(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
return '';
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Parse URI
if (typeof uri === 'string') {
uri = Url.parse(uri);
}
// Calculate signature
var exp = Math.floor(now / 1000) + options.ttlSec;
var mac = Crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
ext: options.ext
});
// Construct bewit: id\exp\mac\ext
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return Hoek.base64urlEncode(bewit);
};
// Generate an authorization string for a message
/*
host: 'example.com',
port: 8000,
message: '{"some":"payload"}', // UTF-8 encoded string for body hash generation
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
timestamp: Date.now(), // A pre-calculated timestamp
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
}
*/
exports.message = function (host, port, message, options) {
// Validate inputs
if (!host || typeof host !== 'string' || !port || typeof port !== 'number' || message === null || message === undefined || typeof message !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
return null;
}
// Application time
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
// Invalid credential object
return null;
}
if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
host: host,
port: port,
hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
};
// Construct authorization
var result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: Crypto.calculateMac('message', credentials, artifacts)
};
return result;
};
-78
View File
@@ -1,78 +0,0 @@
// Load modules
var Http = require('http');
var Request = require('request');
var Hawk = require('../lib');
// Declare internals
var internals = {
credentials: {
dh37fgj492je: {
id: 'dh37fgj492je', // Required by Hawk.client.header
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'Steve'
}
}
};
// Credentials lookup function
var credentialsFunc = function (id, callback) {
return callback(null, internals.credentials[id]);
};
// Create HTTP server
var handler = function (req, res) {
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
var payload = (!err ? 'Hello ' + credentials.user + ' ' + artifacts.ext : 'Shoosh!');
var headers = {
'Content-Type': 'text/plain',
'Server-Authorization': Hawk.server.header(credentials, artifacts, { payload: payload, contentType: 'text/plain' })
};
res.writeHead(!err ? 200 : 401, headers);
res.end(payload);
});
};
Http.createServer(handler).listen(8000, '127.0.0.1');
// Send unauthenticated request
Request('http://127.0.0.1:8000/resource/1?b=1&a=2', function (error, response, body) {
console.log(response.statusCode + ': ' + body);
});
// Send authenticated request
credentialsFunc('dh37fgj492je', function (err, credentials) {
var header = Hawk.client.header('http://127.0.0.1:8000/resource/1?b=1&a=2', 'GET', { credentials: credentials, ext: 'and welcome!' });
var options = {
uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
method: 'GET',
headers: {
authorization: header.field
}
};
Request(options, function (error, response, body) {
var isValid = Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body });
console.log(response.statusCode + ': ' + body + (isValid ? ' (valid)' : ' (invalid)'));
process.exit(0);
});
});
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

+83 -67
View File
@@ -1,13 +1,15 @@
'use strict';
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2014, Eran Hammer <eran@hammer.io>
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
// Declare namespace
var hawk = {
const hawk = {
internals: {}
};
@@ -45,7 +47,7 @@ hawk.client = {
header: function (uri, method, options) {
var result = {
const result = {
field: '',
artifacts: {}
};
@@ -62,11 +64,11 @@ hawk.client = {
// Application time
var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec);
const timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -89,10 +91,10 @@ hawk.client = {
// Calculate signature
var artifacts = {
const artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
method: method,
method,
resource: uri.resource,
host: uri.host,
port: uri.port,
@@ -112,12 +114,12 @@ hawk.client = {
artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = hawk.crypto.calculateMac('header', credentials, artifacts);
const mac = hawk.crypto.calculateMac('header', credentials, artifacts);
// Construct header
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id +
const hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
let header = 'Hawk id="' + credentials.id +
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
@@ -173,11 +175,11 @@ hawk.client = {
// Application time
var now = hawk.utils.now(options.localtimeOffsetMsec);
const now = hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -196,8 +198,8 @@ hawk.client = {
// Calculate signature
var exp = now + options.ttlSec;
var mac = hawk.crypto.calculateMac('bewit', credentials, {
const exp = now + options.ttlSec;
const mac = hawk.crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
@@ -209,14 +211,14 @@ hawk.client = {
// Construct bewit: id\exp\mac\ext
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
const bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return hawk.utils.base64urlEncode(bewit);
},
// Validate server response
/*
request: object created via 'new XMLHttpRequest()' after response received
request: object created via 'new XMLHttpRequest()' after response received or fetch API 'Response'
artifacts: object received from header().artifacts
options: {
payload: optional payload received
@@ -228,46 +230,54 @@ hawk.client = {
options = options || {};
var getHeader = function (name) {
const getHeader = function (name) {
return request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name);
// Fetch API or plain headers
if (request.headers) {
return (typeof request.headers.get === 'function' ? request.headers.get(name) : request.headers[name]);
}
// XMLHttpRequest
return (request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name));
};
var wwwAuthenticate = getHeader('www-authenticate');
const wwwAuthenticate = getHeader('www-authenticate');
if (wwwAuthenticate) {
// Parse HTTP WWW-Authenticate header
var wwwAttributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
const wwwAttributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
if (!wwwAttributes) {
return false;
}
if (wwwAttributes.ts) {
var tsm = hawk.crypto.calculateTsMac(wwwAttributes.ts, credentials);
const tsm = hawk.crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
hawk.utils.setNtpOffset(wwwAttributes.ts - Math.floor((new Date()).getTime() / 1000)); // Keep offset at 1 second precision
hawk.utils.setNtpSecOffset(wwwAttributes.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
}
// Parse HTTP Server-Authorization header
var serverAuthorization = getHeader('server-authorization');
const serverAuthorization = getHeader('server-authorization');
if (!serverAuthorization &&
!options.required) {
return true;
}
var attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
const attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
if (!attributes) {
return false;
}
var modArtifacts = {
const modArtifacts = {
ts: artifacts.ts,
nonce: artifacts.nonce,
method: artifacts.method,
@@ -280,7 +290,7 @@ hawk.client = {
dlg: artifacts.dlg
};
var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
const mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
if (mac !== attributes.mac) {
return false;
}
@@ -295,7 +305,7 @@ hawk.client = {
return false;
}
var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
const calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
return (calculatedHash === attributes.hash);
},
@@ -313,11 +323,11 @@ hawk.client = {
// Application time
var timestamp = options.timestamp || hawk.utils.now(options.localtimeOffsetMsec);
const timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -333,17 +343,17 @@ hawk.client = {
// Calculate signature
var artifacts = {
const artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
host: host,
port: port,
host,
port,
hash: hawk.crypto.calculatePayloadHash(message, credentials.algorithm)
};
// Construct authorization
var result = {
const result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
@@ -354,15 +364,15 @@ hawk.client = {
return result;
},
authenticateTimestamp: function (message, credentials, updateClock) { // updateClock defaults to true
authenticateTimestamp: function (message, credentials, updateClock) { // updateClock defaults to true
var tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
const tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
if (tsm !== message.tsm) {
return false;
}
if (updateClock !== false) {
hawk.utils.setNtpOffset(message.ts - Math.floor((new Date()).getTime() / 1000)); // Keep offset at 1 second precision
hawk.utils.setNtpSecOffset(message.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
return true;
@@ -378,15 +388,15 @@ hawk.crypto = {
calculateMac: function (type, credentials, options) {
var normalized = hawk.crypto.generateNormalizedString(type, options);
const normalized = hawk.crypto.generateNormalizedString(type, options);
var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
const hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
return hmac.toString(CryptoJS.enc.Base64);
},
generateNormalizedString: function (type, options) {
var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +
let normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' +
options.ts + '\n' +
options.nonce + '\n' +
(options.method || '').toUpperCase() + '\n' +
@@ -411,7 +421,7 @@ hawk.crypto = {
calculatePayloadHash: function (payload, algorithm, contentType) {
var hash = CryptoJS.algo[algorithm.toUpperCase()].create();
const hash = CryptoJS.algo[algorithm.toUpperCase()].create();
hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
hash.update(hawk.utils.parseContentType(contentType) + '\n');
hash.update(payload);
@@ -421,7 +431,7 @@ hawk.crypto = {
calculateTsMac: function (ts, credentials) {
var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
const hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
return hash.toString(CryptoJS.enc.Base64);
}
};
@@ -470,14 +480,14 @@ hawk.utils = {
setStorage: function (storage) {
var ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
const ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
hawk.utils.storage = storage;
if (ntpOffset) {
hawk.utils.setNtpOffset(ntpOffset);
hawk.utils.setNtpSecOffset(ntpOffset);
}
},
setNtpOffset: function (offset) {
setNtpSecOffset: function (offset) {
try {
hawk.utils.storage.setItem('hawk_ntp_offset', offset);
@@ -488,9 +498,9 @@ hawk.utils = {
}
},
getNtpOffset: function () {
getNtpSecOffset: function () {
var offset = hawk.utils.storage.getItem('hawk_ntp_offset');
const offset = hawk.utils.storage.getItem('hawk_ntp_offset');
if (!offset) {
return 0;
}
@@ -500,7 +510,12 @@ hawk.utils = {
now: function (localtimeOffsetMsec) {
return Math.floor(((new Date()).getTime() + (localtimeOffsetMsec || 0)) / 1000) + hawk.utils.getNtpOffset();
return Date.now() + (localtimeOffsetMsec || 0) + (hawk.utils.getNtpSecOffset() * 1000);
},
nowSec: function (localtimeOffsetMsec) {
return Math.floor(hawk.utils.now(localtimeOffsetMsec) / 1000);
},
escapeHeaderAttribute: function (attribute) {
@@ -523,23 +538,23 @@ hawk.utils = {
return null;
}
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
const headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
if (!headerParts) {
return null;
}
var scheme = headerParts[1];
const scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return null;
}
var attributesString = headerParts[2];
const attributesString = headerParts[2];
if (!attributesString) {
return null;
}
var attributes = {};
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
const attributes = {};
const verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, ($0, $1, $2) => {
// Check valid attribute names
@@ -572,27 +587,28 @@ hawk.utils = {
randomString: function (size) {
var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var len = randomSource.length;
const randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const len = randomSource.length;
var result = [];
for (var i = 0; i < size; ++i) {
const result = [];
for (let i = 0; i < size; ++i) {
result[i] = randomSource[Math.floor(Math.random() * len)];
}
return result.join('');
},
uriRegex: /^([^:]+)\:\/\/(?:[^@]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/, // scheme://credentials@host:port/resource#fragment
// 1 2 3 4
uriRegex: /^([^:]+)\:\/\/(?:[^@/]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/, // scheme://credentials@host:port/resource#fragment
parseUri: function (input) {
var parts = input.match(hawk.utils.uriRegex);
const parts = input.match(hawk.utils.uriRegex);
if (!parts) {
return { host: '', port: '', resource: '' };
}
var scheme = parts[1].toLowerCase();
var uri = {
const scheme = parts[1].toLowerCase();
const uri = {
host: parts[2],
port: parts[3] || (scheme === 'http' ? '80' : (scheme === 'https' ? '443' : '')),
resource: parts[4]
@@ -603,8 +619,8 @@ hawk.utils = {
base64urlEncode: function (value) {
var wordArray = CryptoJS.enc.Utf8.parse(value);
var encoded = CryptoJS.enc.Base64.stringify(wordArray);
const wordArray = CryptoJS.enc.Utf8.parse(value);
const encoded = CryptoJS.enc.Base64.stringify(wordArray);
return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
};
@@ -618,14 +634,14 @@ hawk.utils = {
// http://code.google.com/p/crypto-js/
// http://code.google.com/p/crypto-js/wiki/License
var CryptoJS = CryptoJS || function (h, r) { var k = {}, l = k.lib = {}, n = function () { }, f = l.Base = { extend: function (a) { n.prototype = this; var b = new n; a && b.mixIn(a); b.hasOwnProperty("init") || (b.init = function () { b.$super.init.apply(this, arguments) }); b.init.prototype = b; b.$super = this; return b }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (var b in a) a.hasOwnProperty(b) && (this[b] = a[b]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, j = l.WordArray = f.extend({ init: function (a, b) { a = this.words = a || []; this.sigBytes = b != r ? b : 4 * a.length }, toString: function (a) { return (a || s).stringify(this) }, concat: function (a) { var b = this.words, d = a.words, c = this.sigBytes; a = a.sigBytes; this.clamp(); if (c % 4) for (var e = 0; e < a; e++) b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4); else if (65535 < d.length) for (e = 0; e < a; e += 4) b[c + e >>> 2] = d[e >>> 2]; else b.push.apply(b, d); this.sigBytes += a; return this }, clamp: function () { var a = this.words, b = this.sigBytes; a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4); a.length = h.ceil(b / 4) }, clone: function () { var a = f.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (var b = [], d = 0; d < a; d += 4) b.push(4294967296 * h.random() | 0); return new j.init(b, a) } }), m = k.enc = {}, s = m.Hex = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) { var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255; d.push((e >>> 4).toString(16)); d.push((e & 15).toString(16)) } return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c += 2) d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8); return new j.init(d, b / 2) } }, p = m.Latin1 = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255)); return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c++) d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4); return new j.init(d, b) } }, t = m.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(p.stringify(a))) } catch (b) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return p.parse(unescape(encodeURIComponent(a))) } }, q = l.BufferedBlockAlgorithm = f.extend({ reset: function () { this._data = new j.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = t.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var b = this._data, d = b.words, c = b.sigBytes, e = this.blockSize, f = c / (4 * e), f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); a = f * e; c = h.min(4 * a, c); if (a) { for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); g = d.splice(0, a); b.sigBytes -= c } return new j.init(g, c) }, clone: function () { var a = f.clone.call(this); a._data = this._data.clone(); return a }, _minBufferSize: 0 }); l.Hasher = q.extend({ cfg: f.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, d) { return (new a.init(d)).finalize(b) } }, _createHmacHelper: function (a) { return function (b, d) { return (new u.HMAC.init(a, d)).finalize(b) } } }); var u = k.algo = {}; return k }(Math);
(function () { var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({ _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) { for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) { if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0 }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b } }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b) })();
var CryptoJS = CryptoJS || function (h, r) { var k = {}, l = k.lib = {}, n = function () { }, f = l.Base = { extend: function (a) { n.prototype = this; var b = new n; a && b.mixIn(a); b.hasOwnProperty("init") || (b.init = function () { b.$super.init.apply(this, arguments) }); b.init.prototype = b; b.$super = this; return b }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (let b in a) a.hasOwnProperty(b) && (this[b] = a[b]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, j = l.WordArray = f.extend({ init: function (a, b) { a = this.words = a || []; this.sigBytes = b != r ? b : 4 * a.length }, toString: function (a) { return (a || s).stringify(this) }, concat: function (a) { var b = this.words, d = a.words, c = this.sigBytes; a = a.sigBytes; this.clamp(); if (c % 4) for (let e = 0; e < a; e++) b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4); else if (65535 < d.length) for (let e = 0; e < a; e += 4) b[c + e >>> 2] = d[e >>> 2]; else b.push.apply(b, d); this.sigBytes += a; return this }, clamp: function () { var a = this.words, b = this.sigBytes; a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4); a.length = h.ceil(b / 4) }, clone: function () { var a = f.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (let b = [], d = 0; d < a; d += 4) b.push(4294967296 * h.random() | 0); return new j.init(b, a) } }), m = k.enc = {}, s = m.Hex = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) { var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255; d.push((e >>> 4).toString(16)); d.push((e & 15).toString(16)) } return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c += 2) d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8); return new j.init(d, b / 2) } }, p = m.Latin1 = { stringify: function (a) { var b = a.words; a = a.sigBytes; for (var d = [], c = 0; c < a; c++) d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255)); return d.join("") }, parse: function (a) { for (var b = a.length, d = [], c = 0; c < b; c++) d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4); return new j.init(d, b) } }, t = m.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(p.stringify(a))) } catch (b) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return p.parse(unescape(encodeURIComponent(a))) } }, q = l.BufferedBlockAlgorithm = f.extend({ reset: function () { this._data = new j.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = t.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var b = this._data, d = b.words, c = b.sigBytes, e = this.blockSize, f = c / (4 * e), f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); a = f * e; c = h.min(4 * a, c); if (a) { for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); g = d.splice(0, a); b.sigBytes -= c } return new j.init(g, c) }, clone: function () { var a = f.clone.call(this); a._data = this._data.clone(); return a }, _minBufferSize: 0 }); l.Hasher = q.extend({ cfg: f.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, d) { return (new a.init(d)).finalize(b) } }, _createHmacHelper: function (a) { return function (b, d) { return (new u.HMAC.init(a, d)).finalize(b) } } }); var u = k.algo = {}; return k }(Math);
(() => { var k = CryptoJS, b = k.lib, m = b.WordArray, l = b.Hasher, d = [], b = k.algo.SHA1 = l.extend({ _doReset: function () { this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (n, p) { for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) { if (16 > c) d[c] = n[p + c] | 0; else { var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16]; d[c] = g << 1 | g >>> 31 } g = (e << 5 | e >>> 27) + b + d[c]; g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514); b = j; j = h; h = f << 30 | f >>> 2; f = e; e = g } a[0] = a[0] + e | 0; a[1] = a[1] + f | 0; a[2] = a[2] + h | 0; a[3] = a[3] + j | 0; a[4] = a[4] + b | 0 }, _doFinalize: function () { var b = this._data, d = b.words, a = 8 * this._nDataBytes, e = 8 * b.sigBytes; d[e >>> 5] |= 128 << 24 - e % 32; d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296); d[(e + 64 >>> 9 << 4) + 15] = a; b.sigBytes = 4 * d.length; this._process(); return this._hash }, clone: function () { var b = l.clone.call(this); b._hash = this._hash.clone(); return b } }); k.SHA1 = l._createHelper(b); k.HmacSHA1 = l._createHmacHelper(b) })();
(function (k) { for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function (q) { return 4294967296 * (q - (q | 0)) | 0 }, l = 2, b = 0; 64 > b;) { var d; a: { d = l; for (var w = k.sqrt(d), r = 2; r <= w; r++) if (!(d % r)) { d = !1; break a } d = !0 } d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++); l++ } var n = [], h = h.SHA256 = j.extend({ _doReset: function () { this._hash = new v.init(s.slice(0)) }, _doProcessBlock: function (q, h) { for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) { if (16 > e) n[e] = q[h + e] | 0; else { var m = n[e - 15], p = n[e - 2]; n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16] } m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e]; p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b); l = j; j = g; g = f; f = k + m | 0; k = b; b = d; d = c; c = m + p | 0 } a[0] = a[0] + c | 0; a[1] = a[1] + d | 0; a[2] = a[2] + b | 0; a[3] = a[3] + k | 0; a[4] = a[4] + f | 0; a[5] = a[5] + g | 0; a[6] = a[6] + j | 0; a[7] = a[7] + l | 0 }, _doFinalize: function () { var d = this._data, b = d.words, a = 8 * this._nDataBytes, c = 8 * d.sigBytes; b[c >>> 5] |= 128 << 24 - c % 32; b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296); b[(c + 64 >>> 9 << 4) + 15] = a; d.sigBytes = 4 * b.length; this._process(); return this._hash }, clone: function () { var b = j.clone.call(this); b._hash = this._hash.clone(); return b } }); g.SHA256 = j._createHelper(h); g.HmacSHA256 = j._createHmacHelper(h) })(Math);
(function () { var c = CryptoJS, k = c.enc.Utf8; c.algo.HMAC = c.lib.Base.extend({ init: function (a, b) { a = this._hasher = new a.init; "string" == typeof b && (b = k.parse(b)); var c = a.blockSize, e = 4 * c; b.sigBytes > e && (b = a.finalize(b)); b.clamp(); for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) h[d] ^= 1549556828, j[d] ^= 909522486; f.sigBytes = g.sigBytes = e; this.reset() }, reset: function () { var a = this._hasher; a.reset(); a.update(this._iKey) }, update: function (a) { this._hasher.update(a); return this }, finalize: function (a) { var b = this._hasher; a = b.finalize(a); b.reset(); return b.finalize(this._oKey.clone().concat(a)) } }) })();
(function () { var h = CryptoJS, j = h.lib.WordArray; h.enc.Base64 = { stringify: function (b) { var e = b.words, f = b.sigBytes, c = this._map; b.clamp(); b = []; for (var a = 0; a < f; a += 3) for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) b.push(c.charAt(d >>> 6 * (3 - g) & 63)); if (e = c.charAt(64)) for (; b.length % 4;) b.push(e); return b.join("") }, parse: function (b) { var e = b.length, f = this._map, c = f.charAt(64); c && (c = b.indexOf(c), -1 != c && (e = c)); for (var c = [], a = 0, d = 0; d < e; d++) if (d % 4) { var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4), h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4); c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4); a++ } return j.create(c, a) }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" } })();
(() => { var c = CryptoJS, k = c.enc.Utf8; c.algo.HMAC = c.lib.Base.extend({ init: function (a, b) { a = this._hasher = new a.init; "string" == typeof b && (b = k.parse(b)); var c = a.blockSize, e = 4 * c; b.sigBytes > e && (b = a.finalize(b)); b.clamp(); for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) h[d] ^= 1549556828, j[d] ^= 909522486; f.sigBytes = g.sigBytes = e; this.reset() }, reset: function () { var a = this._hasher; a.reset(); a.update(this._iKey) }, update: function (a) { this._hasher.update(a); return this }, finalize: function (a) { var b = this._hasher; a = b.finalize(a); b.reset(); return b.finalize(this._oKey.clone().concat(a)) } }) })();
(() => { var h = CryptoJS, j = h.lib.WordArray; h.enc.Base64 = { stringify: function (b) { var e = b.words, f = b.sigBytes, c = this._map; b.clamp(); b = []; for (var a = 0; a < f; a += 3) for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) b.push(c.charAt(d >>> 6 * (3 - g) & 63)); if (e = c.charAt(64)) for (; b.length % 4;) b.push(e); return b.join("") }, parse: function (b) { var e = b.length, f = this._map, c = f.charAt(64); c && (c = b.indexOf(c), -1 != c && (e = c)); for (var c = [], a = 0, d = 0; d < e; d++) if (d % 4) { var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4), h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4); c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4); a++ } return j.create(c, a) }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" } })();
hawk.crypto.internals = CryptoJS;
hawk.crypto.utils = CryptoJS;
// Export if used as a module
+72 -47
View File
@@ -1,15 +1,17 @@
'use strict';
// Load modules
var Url = require('url');
var Hoek = require('hoek');
var Cryptiles = require('cryptiles');
var Crypto = require('./crypto');
var Utils = require('./utils');
const Url = require('url');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
// Declare internals
var internals = {};
const internals = {};
// Generate an Authorization header for a given request
@@ -30,7 +32,7 @@ var internals = {};
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now(), // A pre-calculated timestamp
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
@@ -43,7 +45,7 @@ var internals = {};
exports.header = function (uri, method, options) {
var result = {
const result = {
field: '',
artifacts: {}
};
@@ -60,11 +62,11 @@ exports.header = function (uri, method, options) {
// Application time
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
const timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -87,10 +89,10 @@ exports.header = function (uri, method, options) {
// Calculate signature
var artifacts = {
const artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
method: method,
method,
resource: uri.pathname + (uri.search || ''), // Maintain trailing '?'
host: uri.hostname,
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
@@ -110,12 +112,12 @@ exports.header = function (uri, method, options) {
artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = Crypto.calculateMac('header', credentials, artifacts);
const mac = Crypto.calculateMac('header', credentials, artifacts);
// Construct header
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id +
const hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
let header = 'Hawk id="' + credentials.id +
'", ts="' + artifacts.ts +
'", nonce="' + artifacts.nonce +
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
@@ -123,7 +125,7 @@ exports.header = function (uri, method, options) {
'", mac="' + mac + '"';
if (artifacts.app) {
header += ', app="' + artifacts.app +
header = header + ', app="' + artifacts.app +
(artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
@@ -144,26 +146,44 @@ exports.header = function (uri, method, options) {
}
*/
exports.authenticate = function (res, credentials, artifacts, options) {
exports.authenticate = function (res, credentials, artifacts, options, callback) {
artifacts = Hoek.clone(artifacts);
options = options || {};
let wwwAttributes = null;
let serverAuthAttributes = null;
const finalize = function (err) {
if (callback) {
const headers = {
'www-authenticate': wwwAttributes,
'server-authorization': serverAuthAttributes
};
return callback(err, headers);
}
return !err;
};
if (res.headers['www-authenticate']) {
// Parse HTTP WWW-Authenticate header
var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);
if (wwwAttributes instanceof Error) {
return false;
wwwAttributes = null;
return finalize(new Error('Invalid WWW-Authenticate header'));
}
// Validate server timestamp (not used to update clock since it is done via the SNPT client)
if (wwwAttributes.ts) {
var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
const tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
return finalize(new Error('Invalid server timestamp hash'));
}
}
}
@@ -173,34 +193,39 @@ exports.authenticate = function (res, credentials, artifacts, options) {
if (!res.headers['server-authorization'] &&
!options.required) {
return true;
return finalize();
}
var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (attributes instanceof Error) {
return false;
serverAuthAttributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);
if (serverAuthAttributes instanceof Error) {
serverAuthAttributes = null;
return finalize(new Error('Invalid Server-Authorization header'));
}
artifacts.ext = attributes.ext;
artifacts.hash = attributes.hash;
artifacts.ext = serverAuthAttributes.ext;
artifacts.hash = serverAuthAttributes.hash;
var mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== attributes.mac) {
return false;
const mac = Crypto.calculateMac('response', credentials, artifacts);
if (mac !== serverAuthAttributes.mac) {
return finalize(new Error('Bad response mac'));
}
if (!options.payload &&
options.payload !== '') {
return true;
return finalize();
}
if (!attributes.hash) {
return false;
if (!serverAuthAttributes.hash) {
return finalize(new Error('Missing response hash attribute'));
}
var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
return (calculatedHash === attributes.hash);
const calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);
if (calculatedHash !== serverAuthAttributes.hash) {
return finalize(new Error('Bad response payload mac'));
}
return finalize();
};
@@ -243,11 +268,11 @@ exports.getBewit = function (uri, options) {
// Application time
var now = Utils.now(options.localtimeOffsetMsec);
const now = Utils.now(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -268,8 +293,8 @@ exports.getBewit = function (uri, options) {
// Calculate signature
var exp = Math.floor(now / 1000) + options.ttlSec;
var mac = Crypto.calculateMac('bewit', credentials, {
const exp = Math.floor(now / 1000) + options.ttlSec;
const mac = Crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
@@ -281,7 +306,7 @@ exports.getBewit = function (uri, options) {
// Construct bewit: id\exp\mac\ext
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
const bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return Hoek.base64urlEncode(bewit);
};
@@ -304,7 +329,7 @@ exports.getBewit = function (uri, options) {
// Optional
timestamp: Date.now(), // A pre-calculated timestamp
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
}
@@ -324,11 +349,11 @@ exports.message = function (host, port, message, options) {
// Application time
var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
const timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
const credentials = options.credentials;
if (!credentials ||
!credentials.id ||
!credentials.key ||
@@ -344,17 +369,17 @@ exports.message = function (host, port, message, options) {
// Calculate signature
var artifacts = {
const artifacts = {
ts: timestamp,
nonce: options.nonce || Cryptiles.randomString(6),
host: host,
port: port,
host,
port,
hash: Crypto.calculatePayloadHash(message, credentials.algorithm)
};
// Construct authorization
var result = {
const result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
+22 -20
View File
@@ -1,13 +1,15 @@
'use strict';
// Load modules
var Crypto = require('crypto');
var Url = require('url');
var Utils = require('./utils');
const Crypto = require('crypto');
const Url = require('url');
const Utils = require('./utils');
// Declare internals
var internals = {};
const internals = {};
// MAC normalization format version
@@ -44,25 +46,25 @@ exports.algorithms = ['sha1', 'sha256'];
exports.calculateMac = function (type, credentials, options) {
var normalized = exports.generateNormalizedString(type, options);
const normalized = exports.generateNormalizedString(type, options);
var hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized);
var digest = hmac.digest('base64');
const hmac = Crypto.createHmac(credentials.algorithm, credentials.key).update(normalized);
const digest = hmac.digest('base64');
return digest;
};
exports.generateNormalizedString = function (type, options) {
var resource = options.resource || '';
let resource = options.resource || '';
if (resource &&
resource[0] !== '/') {
var url = Url.parse(resource, false);
const url = Url.parse(resource, false);
resource = url.path; // Includes query
}
var normalized = 'hawk.' + exports.headerVersion + '.' + type + '\n' +
let normalized = 'hawk.' + exports.headerVersion + '.' + type + '\n' +
options.ts + '\n' +
options.nonce + '\n' +
(options.method || '').toUpperCase() + '\n' +
@@ -72,14 +74,14 @@ exports.generateNormalizedString = function (type, options) {
(options.hash || '') + '\n';
if (options.ext) {
normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
normalized = normalized + options.ext.replace('\\', '\\\\').replace('\n', '\\n');
}
normalized += '\n';
normalized = normalized + '\n';
if (options.app) {
normalized += options.app + '\n' +
(options.dlg || '') + '\n';
normalized = normalized + options.app + '\n' +
(options.dlg || '') + '\n';
}
return normalized;
@@ -88,7 +90,7 @@ exports.generateNormalizedString = function (type, options) {
exports.calculatePayloadHash = function (payload, algorithm, contentType) {
var hash = exports.initializePayloadHash(algorithm, contentType);
const hash = exports.initializePayloadHash(algorithm, contentType);
hash.update(payload || '');
return exports.finalizePayloadHash(hash);
};
@@ -96,7 +98,7 @@ exports.calculatePayloadHash = function (payload, algorithm, contentType) {
exports.initializePayloadHash = function (algorithm, contentType) {
var hash = Crypto.createHash(algorithm);
const hash = Crypto.createHash(algorithm);
hash.update('hawk.' + exports.headerVersion + '.payload\n');
hash.update(Utils.parseContentType(contentType) + '\n');
return hash;
@@ -112,7 +114,7 @@ exports.finalizePayloadHash = function (hash) {
exports.calculateTsMac = function (ts, credentials) {
var hmac = Crypto.createHmac(credentials.algorithm, credentials.key);
const hmac = Crypto.createHmac(credentials.algorithm, credentials.key);
hmac.update('hawk.' + exports.headerVersion + '.ts\n' + ts + '\n');
return hmac.digest('base64');
};
@@ -120,7 +122,7 @@ exports.calculateTsMac = function (ts, credentials) {
exports.timestampMessage = function (credentials, localtimeOffsetMsec) {
var now = Utils.nowSecs(localtimeOffsetMsec);
var tsm = exports.calculateTsMac(now, credentials);
return { ts: now, tsm: tsm };
const now = Utils.nowSecs(localtimeOffsetMsec);
const tsm = exports.calculateTsMac(now, credentials);
return { ts: now, tsm };
};
+2
View File
@@ -1,3 +1,5 @@
'use strict';
// Export sub-modules
exports.error = exports.Error = require('boom');
+59 -57
View File
@@ -1,15 +1,17 @@
'use strict';
// Load modules
var Boom = require('boom');
var Hoek = require('hoek');
var Cryptiles = require('cryptiles');
var Crypto = require('./crypto');
var Utils = require('./utils');
const Boom = require('boom');
const Hoek = require('hoek');
const Cryptiles = require('cryptiles');
const Crypto = require('./crypto');
const Utils = require('./utils');
// Declare internals
var internals = {};
const internals = {};
// Hawk authentication
@@ -17,7 +19,7 @@ var internals = {};
/*
req: node's HTTP request object or an object as follows:
var request = {
const request = {
method: 'GET',
url: '/resource/4?a=1&b=2',
host: 'example.com',
@@ -30,7 +32,7 @@ var internals = {};
needed by the application. This function is the equivalent of verifying the username and
password in Basic authentication.
var credentialsFunc = function (id, callback) {
const credentialsFunc = function (id, callback) {
// Lookup credentials in database
db.lookup(id, function (err, item) {
@@ -39,7 +41,7 @@ var internals = {};
return callback(err);
}
var credentials = {
const credentials = {
// Required
key: item.key,
algorithm: item.algorithm,
@@ -93,25 +95,25 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
// Application time
var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
const now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
// Convert node Http request object to a request configuration object
var request = Utils.parseRequest(req, options);
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
// Parse HTTP Authorization header
var attributes = Utils.parseAuthorizationHeader(request.authorization);
const attributes = Utils.parseAuthorizationHeader(request.authorization);
if (attributes instanceof Error) {
return callback(attributes);
}
// Construct artifacts container
var artifacts = {
const artifacts = {
method: request.method,
host: request.host,
port: request.port,
@@ -138,14 +140,14 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
// Fetch Hawk credentials
credentialsFunc(attributes.id, function (err, credentials) {
credentialsFunc(attributes.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null, artifacts);
}
if (!credentials) {
return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, artifacts);
return callback(Utils.unauthorized('Unknown credentials'), null, artifacts);
}
if (!credentials.key ||
@@ -160,9 +162,9 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
// Calculate MAC
var mac = Crypto.calculateMac('header', credentials, artifacts);
const mac = Crypto.calculateMac('header', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, attributes.mac)) {
return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, artifacts);
return callback(Utils.unauthorized('Bad mac'), credentials, artifacts);
}
// Check payload hash
@@ -171,28 +173,28 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
options.payload === '') {
if (!attributes.hash) {
return callback(Boom.unauthorized('Missing required payload hash', 'Hawk'), credentials, artifacts);
return callback(Utils.unauthorized('Missing required payload hash'), credentials, artifacts);
}
var hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
const hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, request.contentType);
if (!Cryptiles.fixedTimeComparison(hash, attributes.hash)) {
return callback(Boom.unauthorized('Bad payload hash', 'Hawk'), credentials, artifacts);
return callback(Utils.unauthorized('Bad payload hash'), credentials, artifacts);
}
}
// Check nonce
options.nonceFunc(credentials.key, attributes.nonce, attributes.ts, function (err) {
options.nonceFunc(credentials.key, attributes.nonce, attributes.ts, (err) => {
if (err) {
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials, artifacts);
return callback(Utils.unauthorized('Invalid nonce'), credentials, artifacts);
}
// Check timestamp staleness
if (Math.abs((attributes.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
var tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
return callback(Boom.unauthorized('Stale timestamp', 'Hawk', tsm), credentials, artifacts);
const tsm = Crypto.timestampMessage(credentials, options.localtimeOffsetMsec);
return callback(Utils.unauthorized('Stale timestamp', tsm), credentials, artifacts);
}
// Successful authentication
@@ -214,7 +216,7 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
exports.authenticatePayload = function (payload, credentials, artifacts, contentType) {
var calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);
const calculatedHash = Crypto.calculatePayloadHash(payload, credentials.algorithm, contentType);
return Cryptiles.fixedTimeComparison(calculatedHash, artifacts.hash);
};
@@ -285,18 +287,18 @@ exports.header = function (credentials, artifacts, options) {
artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = Crypto.calculateMac('response', credentials, artifacts);
const mac = Crypto.calculateMac('response', credentials, artifacts);
// Construct header
var header = 'Hawk mac="' + mac + '"' +
let header = 'Hawk mac="' + mac + '"' +
(artifacts.hash ? ', hash="' + artifacts.hash + '"' : '');
if (artifacts.ext !== null &&
artifacts.ext !== undefined &&
artifacts.ext !== '') { // Other falsey values allowed
header += ', ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) + '"';
header = header + ', ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) + '"';
}
return header;
@@ -319,11 +321,11 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Application time
var now = Utils.now(options.localtimeOffsetMsec);
const now = Utils.now(options.localtimeOffsetMsec);
// Convert node Http request object to a request configuration object
var request = Utils.parseRequest(req, options);
const request = Utils.parseRequest(req, options);
if (request instanceof Error) {
return callback(Boom.badRequest(request.message));
}
@@ -334,15 +336,15 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
return callback(Boom.badRequest('Resource path exceeds max length'));
}
var resource = request.url.match(internals.bewitRegex);
const resource = request.url.match(internals.bewitRegex);
if (!resource) {
return callback(Boom.unauthorized(null, 'Hawk'));
return callback(Utils.unauthorized());
}
// Bewit not empty
if (!resource[3]) {
return callback(Boom.unauthorized('Empty bewit', 'Hawk'));
return callback(Utils.unauthorized('Empty bewit'));
}
// Verify method is GET
@@ -350,7 +352,7 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
if (request.method !== 'GET' &&
request.method !== 'HEAD') {
return callback(Boom.unauthorized('Invalid method', 'Hawk'));
return callback(Utils.unauthorized('Invalid method'));
}
// No other authentication
@@ -361,19 +363,19 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Parse bewit
var bewitString = Hoek.base64urlDecode(resource[3]);
const bewitString = Hoek.base64urlDecode(resource[3]);
if (bewitString instanceof Error) {
return callback(Boom.badRequest('Invalid bewit encoding'));
}
// Bewit format: id\exp\mac\ext ('\' is used because it is a reserved header attribute character)
var bewitParts = bewitString.split('\\');
const bewitParts = bewitString.split('\\');
if (bewitParts.length !== 4) {
return callback(Boom.badRequest('Invalid bewit structure'));
}
var bewit = {
const bewit = {
id: bewitParts[0],
exp: parseInt(bewitParts[1], 10),
mac: bewitParts[2],
@@ -389,27 +391,27 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Construct URL without bewit
var url = resource[1];
let url = resource[1];
if (resource[4]) {
url += resource[2] + resource[4];
url = url + resource[2] + resource[4];
}
// Check expiration
if (bewit.exp * 1000 <= now) {
return callback(Boom.unauthorized('Access expired', 'Hawk'), null, bewit);
return callback(Utils.unauthorized('Access expired'), null, bewit);
}
// Fetch Hawk credentials
credentialsFunc(bewit.id, function (err, credentials) {
credentialsFunc(bewit.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null, bewit.ext);
}
if (!credentials) {
return callback(Boom.unauthorized('Unknown credentials', 'Hawk'), null, bewit);
return callback(Utils.unauthorized('Unknown credentials'), null, bewit);
}
if (!credentials.key ||
@@ -424,7 +426,7 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
// Calculate MAC
var mac = Crypto.calculateMac('bewit', credentials, {
const mac = Crypto.calculateMac('bewit', credentials, {
ts: bewit.exp,
nonce: '',
method: 'GET',
@@ -435,7 +437,7 @@ exports.authenticateBewit = function (req, credentialsFunc, options, callback) {
});
if (!Cryptiles.fixedTimeComparison(mac, bewit.mac)) {
return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials, bewit);
return callback(Utils.unauthorized('Bad mac'), credentials, bewit);
}
// Successful authentication
@@ -461,7 +463,7 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
// Application time
var now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
const now = Utils.now(options.localtimeOffsetMsec); // Measure now before any other processing
// Validate authorization
@@ -476,14 +478,14 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
// Fetch Hawk credentials
credentialsFunc(authorization.id, function (err, credentials) {
credentialsFunc(authorization.id, (err, credentials) => {
if (err) {
return callback(err, credentials || null);
}
if (!credentials) {
return callback(Boom.unauthorized('Unknown credentials', 'Hawk'));
return callback(Utils.unauthorized('Unknown credentials'));
}
if (!credentials.key ||
@@ -498,40 +500,40 @@ exports.authenticateMessage = function (host, port, message, authorization, cred
// Construct artifacts container
var artifacts = {
const artifacts = {
ts: authorization.ts,
nonce: authorization.nonce,
host: host,
port: port,
host,
port,
hash: authorization.hash
};
// Calculate MAC
var mac = Crypto.calculateMac('message', credentials, artifacts);
const mac = Crypto.calculateMac('message', credentials, artifacts);
if (!Cryptiles.fixedTimeComparison(mac, authorization.mac)) {
return callback(Boom.unauthorized('Bad mac', 'Hawk'), credentials);
return callback(Utils.unauthorized('Bad mac'), credentials);
}
// Check payload hash
var hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
const hash = Crypto.calculatePayloadHash(message, credentials.algorithm);
if (!Cryptiles.fixedTimeComparison(hash, authorization.hash)) {
return callback(Boom.unauthorized('Bad message hash', 'Hawk'), credentials);
return callback(Utils.unauthorized('Bad message hash'), credentials);
}
// Check nonce
options.nonceFunc(credentials.key, authorization.nonce, authorization.ts, function (err) {
options.nonceFunc(credentials.key, authorization.nonce, authorization.ts, (err) => {
if (err) {
return callback(Boom.unauthorized('Invalid nonce', 'Hawk'), credentials);
return callback(Utils.unauthorized('Invalid nonce'), credentials);
}
// Check timestamp staleness
if (Math.abs((authorization.ts * 1000) - now) > (options.timestampSkewSec * 1000)) {
return callback(Boom.unauthorized('Stale timestamp'), credentials);
return callback(Utils.unauthorized('Stale timestamp'), credentials);
}
// Successful authentication
+16 -14
View File
@@ -1,12 +1,14 @@
'use strict';
// Load modules
var Sntp = require('sntp');
var Boom = require('boom');
const Sntp = require('sntp');
const Boom = require('boom');
// Declare internals
var internals = {};
const internals = {};
exports.version = function () {
@@ -29,7 +31,7 @@ internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+
exports.parseHost = function (req, hostHeaderName) {
hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
var hostHeader = req.headers[hostHeaderName];
const hostHeader = req.headers[hostHeaderName];
if (!hostHeader) {
return null;
}
@@ -38,7 +40,7 @@ exports.parseHost = function (req, hostHeaderName) {
return null;
}
var hostParts = hostHeader.match(internals.hostHeaderRegex);
const hostParts = hostHeader.match(internals.hostHeaderRegex);
if (!hostParts) {
return null;
}
@@ -72,7 +74,7 @@ exports.parseRequest = function (req, options) {
// Obtain host and port information
var host;
let host;
if (!options.host ||
!options.port) {
@@ -82,7 +84,7 @@ exports.parseRequest = function (req, options) {
}
}
var request = {
const request = {
method: req.method,
url: req.url,
host: options.host || host.name,
@@ -125,24 +127,24 @@ exports.parseAuthorizationHeader = function (header, keys) {
return Boom.badRequest('Header length too long');
}
var headerParts = header.match(internals.authHeaderRegex);
const headerParts = header.match(internals.authHeaderRegex);
if (!headerParts) {
return Boom.badRequest('Invalid header syntax');
}
var scheme = headerParts[1];
const scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return Boom.unauthorized(null, 'Hawk');
}
var attributesString = headerParts[2];
const attributesString = headerParts[2];
if (!attributesString) {
return Boom.badRequest('Invalid header syntax');
}
var attributes = {};
var errorMessage = '';
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
const attributes = {};
let errorMessage = '';
const verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, ($0, $1, $2) => {
// Check valid attribute names
@@ -179,6 +181,6 @@ exports.parseAuthorizationHeader = function (header, keys) {
exports.unauthorized = function (message, attributes) {
return Boom.unauthorized(message, 'Hawk', attributes);
return Boom.unauthorized(message || null, 'Hawk', attributes);
};
+39 -50
View File
@@ -1,92 +1,81 @@
{
"_args": [
[
"hawk@~3.1.3",
"c:\\users\\luis\\source\\repos\\AACharly\\node_modules\\azure-storage\\node_modules\\request"
"hawk@6.0.2",
"C:\\Users\\Luis\\source\\repos\\AACloud"
]
],
"_from": "hawk@>=3.1.3-0 <3.2.0-0",
"_id": "hawk@3.1.3",
"_inCache": true,
"_from": "hawk@6.0.2",
"_id": "hawk@6.0.2",
"_inBundle": false,
"_integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
"_location": "/hawk",
"_nodeVersion": "5.4.1",
"_npmUser": {
"email": "eran@hammer.io",
"name": "hueniverse"
},
"_npmVersion": "3.3.12",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "hawk@6.0.2",
"name": "hawk",
"raw": "hawk@~3.1.3",
"rawSpec": "~3.1.3",
"scope": null,
"spec": ">=3.1.3-0 <3.2.0-0",
"type": "range"
"escapedName": "hawk",
"rawSpec": "6.0.2",
"saveSpec": null,
"fetchSpec": "6.0.2"
},
"_requiredBy": [
"/azure-storage/request"
"/request"
],
"_resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"_shasum": "078444bd7c1640b0fe540d2c9b73d59678e8e1c4",
"_shrinkwrap": null,
"_spec": "hawk@~3.1.3",
"_where": "c:\\users\\luis\\source\\repos\\AACharly\\node_modules\\azure-storage\\node_modules\\request",
"_resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
"_spec": "6.0.2",
"_where": "C:\\Users\\Luis\\source\\repos\\AACloud",
"author": {
"email": "eran@hammer.io",
"name": "Eran Hammer",
"email": "eran@hammer.io",
"url": "http://hueniverse.com"
},
"browser": "./lib/browser.js",
"babel": {
"presets": [
"es2015"
]
},
"browser": "dist/browser.js",
"bugs": {
"url": "https://github.com/hueniverse/hawk/issues"
},
"contributors": [],
"dependencies": {
"boom": "2.x.x",
"cryptiles": "2.x.x",
"hoek": "2.x.x",
"sntp": "1.x.x"
"boom": "4.x.x",
"cryptiles": "3.x.x",
"hoek": "4.x.x",
"sntp": "2.x.x"
},
"description": "HTTP Hawk Authentication Scheme",
"devDependencies": {
"code": "1.x.x",
"lab": "5.x.x"
},
"directories": {},
"dist": {
"shasum": "078444bd7c1640b0fe540d2c9b73d59678e8e1c4",
"tarball": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz"
"babel-cli": "^6.1.2",
"babel-preset-es2015": "^6.1.2",
"code": "4.x.x",
"lab": "14.x.x"
},
"engines": {
"node": ">=0.10.32"
"node": ">=4.5.0"
},
"gitHead": "2f0b93b34ed9b0ebc865838ef70c6a4035591430",
"homepage": "https://github.com/hueniverse/hawk#readme",
"installable": true,
"keywords": [
"authentication",
"hawk",
"http",
"scheme"
"authentication",
"scheme",
"hawk"
],
"license": "BSD-3-Clause",
"main": "lib/index.js",
"maintainers": [
{
"name": "hueniverse",
"email": "eran@hueniverse.com"
}
],
"name": "hawk",
"optionalDependencies": {},
"repository": {
"type": "git",
"url": "git://github.com/hueniverse/hawk.git"
},
"scripts": {
"build-client": "mkdir -p dist; babel lib/browser.js --out-file dist/browser.js",
"prepublish": "npm run-script build-client",
"test": "lab -a code -t 100 -L",
"test-cov-html": "lab -a code -r html -o coverage.html"
},
"version": "3.1.3"
"version": "6.0.2"
}
-1492
View File
File diff suppressed because it is too large. Load diff
-440
View File
@@ -1,440 +0,0 @@
// Load modules
var Url = require('url');
var Code = require('code');
var Hawk = require('../lib');
var Lab = require('lab');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('Client', function () {
describe('header()', function () {
it('returns a valid authorization header (sha1)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var header = Hawk.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
done();
});
it('returns a valid authorization header (sha256)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
done();
});
it('returns a valid authorization header (no ext)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' }).field;
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('returns a valid authorization header (null ext)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain', ext: null }).field;
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('returns a valid authorization header (empty payload)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' }).field;
expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", mac=\"U5k16YEzn3UnBHKeBzsDXn067Gu3R4YaY6xOt9PYRZM=\"');
done();
});
it('returns a valid authorization header (pre hashed payload)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var options = { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };
options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
done();
});
it('errors on missing uri', function (done) {
var header = Hawk.client.header('', 'POST');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
});
it('errors on invalid uri', function (done) {
var header = Hawk.client.header(4, 'POST');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
});
it('errors on missing method', function (done) {
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', '');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
});
it('errors on invalid method', function (done) {
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 5);
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
});
it('errors on missing options', function (done) {
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST');
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid argument type');
done();
});
it('errors on invalid credentials (id)', function (done) {
var credentials = {
key: '2983d45yun89q',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid credential object');
done();
});
it('errors on missing credentials', function (done) {
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid credential object');
done();
});
it('errors on invalid credentials', function (done) {
var credentials = {
id: '123456',
algorithm: 'sha256'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Invalid credential object');
done();
});
it('errors on invalid algorithm', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'hmac-sha-0'
};
var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });
expect(header.field).to.equal('');
expect(header.err).to.equal('Unknown algorithm');
done();
});
});
describe('authenticate()', function () {
it('returns false on invalid header', function (done) {
var res = {
headers: {
'server-authorization': 'Hawk mac="abc", bad="xyz"'
}
};
expect(Hawk.client.authenticate(res, {})).to.equal(false);
done();
});
it('returns false on invalid mac', function (done) {
var res = {
headers: {
'content-type': 'text/plain',
'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
}
};
var artifacts = {
method: 'POST',
host: 'example.com',
port: '8080',
resource: '/resource/4?filter=a',
ts: '1362336900',
nonce: 'eb5S_L',
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
ext: 'some-app-data',
app: undefined,
dlg: undefined,
mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
id: '123456'
};
var credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(false);
done();
});
it('returns true on ignoring hash', function (done) {
var res = {
headers: {
'content-type': 'text/plain',
'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
}
};
var artifacts = {
method: 'POST',
host: 'example.com',
port: '8080',
resource: '/resource/4?filter=a',
ts: '1362336900',
nonce: 'eb5S_L',
hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
ext: 'some-app-data',
app: undefined,
dlg: undefined,
mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
id: '123456'
};
var credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
done();
});
it('fails on invalid WWW-Authenticate header format', function (done) {
var header = 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(false);
done();
});
it('fails on invalid WWW-Authenticate header format', function (done) {
var credentials = {
id: '123456',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'steve'
};
var header = 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.equal(false);
done();
});
it('skips tsm validation when missing ts', function (done) {
var header = 'Hawk error="Stale timestamp"';
expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(true);
done();
});
});
describe('message()', function () {
it('generates authorization', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.exist();
expect(auth.ts).to.equal(1353809207);
expect(auth.nonce).to.equal('abc123');
done();
});
it('errors on invalid host', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message(5, 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on invalid port', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on missing host', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 0, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on null message', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, null, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on missing message', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, undefined, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on invalid message', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, 5, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on missing options', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman');
expect(auth).to.not.exist();
done();
});
it('errors on invalid credentials (id)', function (done) {
var credentials = {
key: '2983d45yun89q',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
it('errors on invalid credentials (key)', function (done) {
var credentials = {
id: '123456',
algorithm: 'sha1'
};
var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
expect(auth).to.not.exist();
done();
});
});
});
-70
View File
@@ -1,70 +0,0 @@
// Load modules
var Code = require('code');
var Hawk = require('../lib');
var Lab = require('lab');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('Crypto', function () {
describe('generateNormalizedString()', function () {
it('should return a valid normalized string', function (done) {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
nonce: 'k3k4j5',
method: 'GET',
resource: '/resource/something',
host: 'example.com',
port: 8080
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\n\n');
done();
});
it('should return a valid normalized string (ext)', function (done) {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
nonce: 'k3k4j5',
method: 'GET',
resource: '/resource/something',
host: 'example.com',
port: 8080,
ext: 'this is some app data'
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\n\nthis is some app data\n');
done();
});
it('should return a valid normalized string (payload + ext)', function (done) {
expect(Hawk.crypto.generateNormalizedString('header', {
ts: 1357747017,
nonce: 'k3k4j5',
method: 'GET',
resource: '/resource/something',
host: 'example.com',
port: 8080,
hash: 'U4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=',
ext: 'this is some app data'
})).to.equal('hawk.1.header\n1357747017\nk3k4j5\nGET\n/resource/something\nexample.com\n8080\nU4MKKSmiVxk37JCCrAVIjV/OhB3y+NdwoCr6RShbVkE=\nthis is some app data\n');
done();
});
});
});
-378
View File
@@ -1,378 +0,0 @@
// Load modules
var Url = require('url');
var Code = require('code');
var Hawk = require('../lib');
var Lab = require('lab');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('Hawk', function () {
var credentialsFunc = function (id, callback) {
var credentials = {
id: id,
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: (id === '1' ? 'sha1' : 'sha256'),
user: 'steve'
};
return callback(null, credentials);
};
it('generates a header then successfully parse it (configuration)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header(Url.parse('http://example.com:8080/resource/4?filter=a'), req.method, { credentials: credentials1, ext: 'some-app-data' }).field;
expect(req.authorization).to.exist();
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
});
it('generates a header then successfully parse it (node request)', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: 'example.com:8080',
'content-type': 'text/plain;x=y'
}
};
var payload = 'some not so random text';
credentialsFunc('123456', function (err, credentials1) {
var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
var res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);
done();
});
});
});
it('generates a header then successfully parse it (absolute request uri)', function (done) {
var req = {
method: 'POST',
url: 'http://example.com:8080/resource/4?filter=a',
headers: {
host: 'example.com:8080',
'content-type': 'text/plain;x=y'
}
};
var payload = 'some not so random text';
credentialsFunc('123456', function (err, credentials1) {
var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
var res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(true);
done();
});
});
});
it('generates a header then successfully parse it (no server header options)', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: 'example.com:8080',
'content-type': 'text/plain;x=y'
}
};
var payload = 'some not so random text';
credentialsFunc('123456', function (err, credentials1) {
var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
var res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts)).to.equal(true);
done();
});
});
});
it('generates a header then fails to parse it (missing server header hash)', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: 'example.com:8080',
'content-type': 'text/plain;x=y'
}
};
var payload = 'some not so random text';
credentialsFunc('123456', function (err, credentials1) {
var reqHeader = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
req.headers.authorization = reqHeader.field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload(payload, credentials2, artifacts, req.headers['content-type'])).to.equal(true);
var res = {
headers: {
'content-type': 'text/plain'
}
};
res.headers['server-authorization'] = Hawk.server.header(credentials2, artifacts);
expect(res.headers['server-authorization']).to.exist();
expect(Hawk.client.authenticate(res, credentials2, artifacts, { payload: 'some reply' })).to.equal(false);
done();
});
});
});
it('generates a header then successfully parse it (with hash)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
});
it('generates a header then successfully parse it then validate payload', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(Hawk.server.authenticatePayload('hola!', credentials2, artifacts)).to.be.true();
expect(Hawk.server.authenticatePayload('hello!', credentials2, artifacts)).to.be.false();
done();
});
});
});
it('generates a header then successfully parses and validates payload', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, { payload: 'hola!' }, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
done();
});
});
});
it('generates a header then successfully parse it (app)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
done();
});
});
});
it('generates a header then successfully parse it (app, dlg)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(artifacts.ext).to.equal('some-app-data');
expect(artifacts.app).to.equal('asd23ased');
expect(artifacts.dlg).to.equal('23434szr3q4d');
done();
});
});
});
it('generates a header then fail authentication due to bad hash', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, payload: 'hola!', ext: 'some-app-data' }).field;
Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials2, artifacts) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Bad payload hash');
done();
});
});
});
it('generates a header for one resource then fail to authenticate another', function (done) {
var req = {
method: 'GET',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
credentialsFunc('123456', function (err, credentials1) {
req.authorization = Hawk.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials1, ext: 'some-app-data' }).field;
req.url = '/something/else';
Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials2, artifacts) {
expect(err).to.exist();
expect(credentials2).to.exist();
done();
});
});
});
});
-95
View File
@@ -1,95 +0,0 @@
// Load modules
var Code = require('code');
var Hawk = require('../lib');
var Hoek = require('hoek');
var Lab = require('lab');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('README', function () {
describe('core', function () {
var credentials = {
id: 'dh37fgj492je',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
};
var options = {
credentials: credentials,
timestamp: 1353832234,
nonce: 'j4h3g2',
ext: 'some-app-ext-data'
};
it('should generate a header protocol example', function (done) {
var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', options).field;
expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="');
done();
});
it('should generate a normalized string protocol example', function (done) {
var normalized = Hawk.crypto.generateNormalizedString('header', {
credentials: credentials,
ts: options.timestamp,
nonce: options.nonce,
method: 'GET',
resource: '/resource?a=1&b=2',
host: 'example.com',
port: 8000,
ext: options.ext
});
expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nGET\n/resource?a=1&b=2\nexample.com\n8000\n\nsome-app-ext-data\n');
done();
});
var payloadOptions = Hoek.clone(options);
payloadOptions.payload = 'Thank you for flying Hawk';
payloadOptions.contentType = 'text/plain';
it('should generate a header protocol example (with payload)', function (done) {
var header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'POST', payloadOptions).field;
expect(header).to.equal('Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="');
done();
});
it('should generate a normalized string protocol example (with payload)', function (done) {
var normalized = Hawk.crypto.generateNormalizedString('header', {
credentials: credentials,
ts: options.timestamp,
nonce: options.nonce,
method: 'POST',
resource: '/resource?a=1&b=2',
host: 'example.com',
port: 8000,
hash: Hawk.crypto.calculatePayloadHash(payloadOptions.payload, credentials.algorithm, payloadOptions.contentType),
ext: options.ext
});
expect(normalized).to.equal('hawk.1.header\n1353832234\nj4h3g2\nPOST\n/resource?a=1&b=2\nexample.com\n8000\nYi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=\nsome-app-ext-data\n');
done();
});
});
});
-1329
View File
File diff suppressed because it is too large. Load diff
-838
View File
@@ -1,838 +0,0 @@
// Load modules
var Http = require('http');
var Url = require('url');
var Code = require('code');
var Hawk = require('../lib');
var Hoek = require('hoek');
var Lab = require('lab');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('Uri', function () {
var credentialsFunc = function (id, callback) {
var credentials = {
id: id,
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: (id === '1' ? 'sha1' : 'sha256'),
user: 'steve'
};
return callback(null, credentials);
};
it('should generate a bewit then successfully authenticate it', function (done) {
var req = {
method: 'GET',
url: '/resource/4?a=1&b=2',
host: 'example.com',
port: 80
};
credentialsFunc('123456', function (err, credentials1) {
var bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100, ext: 'some-app-data' });
req.url += '&bewit=' + bewit;
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials2, attributes) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
});
});
it('should generate a bewit then successfully authenticate it (no ext)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?a=1&b=2',
host: 'example.com',
port: 80
};
credentialsFunc('123456', function (err, credentials1) {
var bewit = Hawk.uri.getBewit('http://example.com/resource/4?a=1&b=2', { credentials: credentials1, ttlSec: 60 * 60 * 24 * 365 * 100 });
req.url += '&bewit=' + bewit;
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials2, attributes) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
done();
});
});
});
it('should successfully authenticate a request (last param)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDQ1MTE0ODQ2MjFcMzFjMmNkbUJFd1NJRVZDOVkva1NFb2c3d3YrdEVNWjZ3RXNmOGNHU2FXQT1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
});
it('should successfully authenticate a request (first param)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2MjFcMzFjMmNkbUJFd1NJRVZDOVkva1NFb2c3d3YrdEVNWjZ3RXNmOGNHU2FXQT1cc29tZS1hcHAtZGF0YQ&a=1&b=2',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
});
it('should successfully authenticate a request (only param)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2NDFcZm1CdkNWT3MvcElOTUUxSTIwbWhrejQ3UnBwTmo4Y1VrSHpQd3Q5OXJ1cz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.not.exist();
expect(credentials.user).to.equal('steve');
expect(attributes.ext).to.equal('some-app-data');
done();
});
});
it('should fail on multiple authentication', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MTE0ODQ2NDFcZm1CdkNWT3MvcElOTUUxSTIwbWhrejQ3UnBwTmo4Y1VrSHpQd3Q5OXJ1cz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080,
authorization: 'Basic asdasdasdasd'
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Multiple authentications');
done();
});
});
it('should fail on method other than GET', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
host: 'example.com',
port: 8080
};
var exp = Math.floor(Hawk.utils.now() / 1000) + 60;
var ext = 'some-app-data';
var mac = Hawk.crypto.calculateMac('bewit', credentials1, {
timestamp: exp,
nonce: '',
method: req.method,
resource: req.url,
host: req.host,
port: req.port,
ext: ext
});
var bewit = credentials1.id + '\\' + exp + '\\' + mac + '\\' + ext;
req.url += '&bewit=' + Hoek.base64urlEncode(bewit);
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials2, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid method');
done();
});
});
});
it('should fail on invalid host header', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
headers: {
host: 'example.com:something'
}
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid Host header');
done();
});
});
it('should fail on empty bewit', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Empty bewit');
expect(err.isMissing).to.not.exist();
done();
});
});
it('should fail on invalid bewit', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=*',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid bewit encoding');
expect(err.isMissing).to.not.exist();
done();
});
});
it('should fail on missing bewit', function (done) {
var req = {
method: 'GET',
url: '/resource/4',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.not.exist();
expect(err.isMissing).to.equal(true);
done();
});
});
it('should fail on invalid bewit structure', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=abc',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Invalid bewit structure');
done();
});
});
it('should fail on empty bewit attribute', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=YVxcY1xk',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Missing bewit attributes');
done();
});
});
it('should fail on missing bewit id attribute', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=XDQ1NTIxNDc2MjJcK0JFbFhQMXhuWjcvd1Nrbm1ldGhlZm5vUTNHVjZNSlFVRHk4NWpTZVJ4VT1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Missing bewit attributes');
done();
});
});
it('should fail on expired access', function (done) {
var req = {
method: 'GET',
url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDEzNTY0MTg1ODNcWk1wZlMwWU5KNHV0WHpOMmRucTRydEk3NXNXTjFjeWVITTcrL0tNZFdVQT1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Access expired');
done();
});
});
it('should fail on credentials function error', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(Hawk.error.badRequest('Boom'));
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Boom');
done();
});
});
it('should fail on credentials function error with credentials', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(Hawk.error.badRequest('Boom'), { some: 'value' });
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Boom');
expect(credentials.some).to.equal('value');
done();
});
});
it('should fail on null credentials function response', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(null, null);
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Unknown credentials');
done();
});
});
it('should fail on invalid credentials function response', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(null, {});
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.message).to.equal('Invalid credentials');
done();
});
});
it('should fail on invalid credentials function response (unknown algorithm)', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(null, { key: 'xxx', algorithm: 'xxx' });
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.message).to.equal('Unknown algorithm');
done();
});
});
it('should fail on expired access', function (done) {
var req = {
method: 'GET',
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ',
host: 'example.com',
port: 8080
};
Hawk.uri.authenticate(req, function (id, callback) {
callback(null, { key: 'xxx', algorithm: 'sha256' });
}, {}, function (err, credentials, attributes) {
expect(err).to.exist();
expect(err.output.payload.message).to.equal('Bad mac');
done();
});
});
describe('getBewit()', function () {
it('returns a valid bewit value', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
done();
});
it('returns a valid bewit value (explicit port)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com:8080/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcaFpiSjNQMmNLRW80a3kwQzhqa1pBa1J5Q1p1ZWc0V1NOYnhWN3ZxM3hIVT1ceGFuZHlhbmR6');
done();
});
it('returns a valid bewit value (null ext)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: null });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcSUdZbUxnSXFMckNlOEN4dktQczRKbFdJQStValdKSm91d2dBUmlWaENBZz1c');
done();
});
it('returns a valid bewit value (parsed uri)', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit(Url.parse('https://example.com/somewhere/over/the/rainbow'), { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdca3NjeHdOUjJ0SnBQMVQxekRMTlBiQjVVaUtJVTl0T1NKWFRVZEc3WDloOD1ceGFuZHlhbmR6');
done();
});
it('errors on invalid options', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', 4);
expect(bewit).to.equal('');
done();
});
it('errors on missing uri', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('', { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on invalid uri', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit(5, { credentials: credentials, ttlSec: 300, localtimeOffsetMsec: 1356420407232 - Hawk.utils.now(), ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on invalid credentials (id)', function (done) {
var credentials = {
key: '2983d45yun89q',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on missing credentials', function (done) {
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on invalid credentials (key)', function (done) {
var credentials = {
id: '123456',
algorithm: 'sha256'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 3000, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on invalid algorithm', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'hmac-sha-0'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow', { credentials: credentials, ttlSec: 300, ext: 'xandyandz' });
expect(bewit).to.equal('');
done();
});
it('errors on missing options', function (done) {
var credentials = {
id: '123456',
key: '2983d45yun89q',
algorithm: 'hmac-sha-0'
};
var bewit = Hawk.uri.getBewit('https://example.com/somewhere/over/the/rainbow');
expect(bewit).to.equal('');
done();
});
});
describe('authenticateMessage()', function () {
it('should generate an authorization then successfully parse it', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials2) {
expect(err).to.not.exist();
expect(credentials2.user).to.equal('steve');
done();
});
});
});
it('should fail authorization on mismatching host', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example1.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Bad mac');
done();
});
});
});
it('should fail authorization on stale timestamp', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { localtimeOffsetMsec: 100000 }, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Stale timestamp');
done();
});
});
});
it('overrides timestampSkewSec', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1, localtimeOffsetMsec: 100000 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, { timestampSkewSec: 500 }, function (err, credentials2) {
expect(err).to.not.exist();
done();
});
});
});
it('should fail authorization on invalid authorization', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
delete auth.id;
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Invalid authorization');
done();
});
});
});
it('should fail authorization on bad hash', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example.com', 8080, 'some message1', auth, credentialsFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Bad message hash');
done();
});
});
});
it('should fail authorization on nonce error', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {
nonceFunc: function (key, nonce, ts, callback) {
callback(new Error('kaboom'));
}
}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Invalid nonce');
done();
});
});
});
it('should fail authorization on credentials error', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
var errFunc = function (id, callback) {
callback(new Error('kablooey'));
};
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('kablooey');
done();
});
});
});
it('should fail authorization on missing credentials', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
var errFunc = function (id, callback) {
callback();
};
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Unknown credentials');
done();
});
});
});
it('should fail authorization on invalid credentials', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
var errFunc = function (id, callback) {
callback(null, {});
};
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Invalid credentials');
done();
});
});
});
it('should fail authorization on invalid credentials algorithm', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: credentials1 });
expect(auth).to.exist();
var errFunc = function (id, callback) {
callback(null, { key: '123', algorithm: '456' });
};
Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, errFunc, {}, function (err, credentials2) {
expect(err).to.exist();
expect(err.message).to.equal('Unknown algorithm');
done();
});
});
});
it('should fail on missing host', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var auth = Hawk.client.message(null, 8080, 'some message', { credentials: credentials1 });
expect(auth).to.not.exist();
done();
});
});
it('should fail on missing credentials', function (done) {
var auth = Hawk.client.message('example.com', 8080, 'some message', {});
expect(auth).to.not.exist();
done();
});
it('should fail on invalid algorithm', function (done) {
credentialsFunc('123456', function (err, credentials1) {
var creds = Hoek.clone(credentials1);
creds.algorithm = 'blah';
var auth = Hawk.client.message('example.com', 8080, 'some message', { credentials: creds });
expect(auth).to.not.exist();
done();
});
});
});
});
-149
View File
@@ -1,149 +0,0 @@
// Load modules
var Code = require('code');
var Hawk = require('../lib');
var Lab = require('lab');
var Package = require('../package.json');
// Declare internals
var internals = {};
// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.experiment;
var it = lab.test;
var expect = Code.expect;
describe('Utils', function () {
describe('parseHost()', function () {
it('returns port 80 for non tls node request', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: 'example.com',
'content-type': 'text/plain;x=y'
}
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(80);
done();
});
it('returns port 443 for non tls node request', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: 'example.com',
'content-type': 'text/plain;x=y'
},
connection: {
encrypted: true
}
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
done();
});
it('returns port 443 for non tls node request (IPv6)', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: '[123:123:123]',
'content-type': 'text/plain;x=y'
},
connection: {
encrypted: true
}
};
expect(Hawk.utils.parseHost(req, 'Host').port).to.equal(443);
done();
});
it('parses IPv6 headers', function (done) {
var req = {
method: 'POST',
url: '/resource/4?filter=a',
headers: {
host: '[123:123:123]:8000',
'content-type': 'text/plain;x=y'
},
connection: {
encrypted: true
}
};
var host = Hawk.utils.parseHost(req, 'Host');
expect(host.port).to.equal('8000');
expect(host.name).to.equal('[123:123:123]');
done();
});
it('errors on header too long', function (done) {
var long = '';
for (var i = 0; i < 5000; ++i) {
long += 'x';
}
expect(Hawk.utils.parseHost({ headers: { host: long } })).to.be.null();
done();
});
});
describe('parseAuthorizationHeader()', function () {
it('errors on header too long', function (done) {
var long = 'Scheme a="';
for (var i = 0; i < 5000; ++i) {
long += 'x';
}
long += '"';
var err = Hawk.utils.parseAuthorizationHeader(long, ['a']);
expect(err).to.be.instanceof(Error);
expect(err.message).to.equal('Header length too long');
done();
});
});
describe('version()', function () {
it('returns the correct package version number', function (done) {
expect(Hawk.utils.version()).to.equal(Package.version);
done();
});
});
describe('unauthorized()', function () {
it('returns a hawk 401', function (done) {
expect(Hawk.utils.unauthorized('kaboom').output.headers['WWW-Authenticate']).to.equal('Hawk error="kaboom"');
done();
});
it('supports attributes', function (done) {
expect(Hawk.utils.unauthorized('kaboom', { a: 'b' }).output.headers['WWW-Authenticate']).to.equal('Hawk a="b", error="kaboom"');
done();
});
});
});