client.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Load modules
  2. var Url = require('url');
  3. var Code = require('code');
  4. var Hawk = require('../lib');
  5. var Lab = require('lab');
  6. // Declare internals
  7. var internals = {};
  8. // Test shortcuts
  9. var lab = exports.lab = Lab.script();
  10. var describe = lab.experiment;
  11. var it = lab.test;
  12. var expect = Code.expect;
  13. describe('Client', function () {
  14. describe('header()', function () {
  15. it('returns a valid authorization header (sha1)', function (done) {
  16. var credentials = {
  17. id: '123456',
  18. key: '2983d45yun89q',
  19. algorithm: 'sha1'
  20. };
  21. 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;
  22. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
  23. done();
  24. });
  25. it('returns a valid authorization header (sha256)', function (done) {
  26. var credentials = {
  27. id: '123456',
  28. key: '2983d45yun89q',
  29. algorithm: 'sha256'
  30. };
  31. 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;
  32. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
  33. done();
  34. });
  35. it('returns a valid authorization header (no ext)', function (done) {
  36. var credentials = {
  37. id: '123456',
  38. key: '2983d45yun89q',
  39. algorithm: 'sha256'
  40. };
  41. 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;
  42. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
  43. done();
  44. });
  45. it('returns a valid authorization header (null ext)', function (done) {
  46. var credentials = {
  47. id: '123456',
  48. key: '2983d45yun89q',
  49. algorithm: 'sha256'
  50. };
  51. 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;
  52. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
  53. done();
  54. });
  55. it('returns a valid authorization header (empty payload)', function (done) {
  56. var credentials = {
  57. id: '123456',
  58. key: '2983d45yun89q',
  59. algorithm: 'sha256'
  60. };
  61. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: '', contentType: 'text/plain' }).field;
  62. expect(header).to.equal('Hawk id=\"123456\", ts=\"1353809207\", nonce=\"Ygvqdz\", hash=\"q/t+NNAkQZNlq/aAD6PlexImwQTxwgT2MahfTa9XRLA=\", mac=\"U5k16YEzn3UnBHKeBzsDXn067Gu3R4YaY6xOt9PYRZM=\"');
  63. done();
  64. });
  65. it('returns a valid authorization header (pre hashed payload)', function (done) {
  66. var credentials = {
  67. id: '123456',
  68. key: '2983d45yun89q',
  69. algorithm: 'sha256'
  70. };
  71. var options = { credentials: credentials, timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about', contentType: 'text/plain' };
  72. options.hash = Hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
  73. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', options).field;
  74. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
  75. done();
  76. });
  77. it('errors on missing uri', function (done) {
  78. var header = Hawk.client.header('', 'POST');
  79. expect(header.field).to.equal('');
  80. expect(header.err).to.equal('Invalid argument type');
  81. done();
  82. });
  83. it('errors on invalid uri', function (done) {
  84. var header = Hawk.client.header(4, 'POST');
  85. expect(header.field).to.equal('');
  86. expect(header.err).to.equal('Invalid argument type');
  87. done();
  88. });
  89. it('errors on missing method', function (done) {
  90. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', '');
  91. expect(header.field).to.equal('');
  92. expect(header.err).to.equal('Invalid argument type');
  93. done();
  94. });
  95. it('errors on invalid method', function (done) {
  96. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 5);
  97. expect(header.field).to.equal('');
  98. expect(header.err).to.equal('Invalid argument type');
  99. done();
  100. });
  101. it('errors on missing options', function (done) {
  102. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST');
  103. expect(header.field).to.equal('');
  104. expect(header.err).to.equal('Invalid argument type');
  105. done();
  106. });
  107. it('errors on invalid credentials (id)', function (done) {
  108. var credentials = {
  109. key: '2983d45yun89q',
  110. algorithm: 'sha256'
  111. };
  112. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
  113. expect(header.field).to.equal('');
  114. expect(header.err).to.equal('Invalid credential object');
  115. done();
  116. });
  117. it('errors on missing credentials', function (done) {
  118. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { ext: 'Bazinga!', timestamp: 1353809207 });
  119. expect(header.field).to.equal('');
  120. expect(header.err).to.equal('Invalid credential object');
  121. done();
  122. });
  123. it('errors on invalid credentials', function (done) {
  124. var credentials = {
  125. id: '123456',
  126. algorithm: 'sha256'
  127. };
  128. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 });
  129. expect(header.field).to.equal('');
  130. expect(header.err).to.equal('Invalid credential object');
  131. done();
  132. });
  133. it('errors on invalid algorithm', function (done) {
  134. var credentials = {
  135. id: '123456',
  136. key: '2983d45yun89q',
  137. algorithm: 'hmac-sha-0'
  138. };
  139. var header = Hawk.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 });
  140. expect(header.field).to.equal('');
  141. expect(header.err).to.equal('Unknown algorithm');
  142. done();
  143. });
  144. });
  145. describe('authenticate()', function () {
  146. it('returns false on invalid header', function (done) {
  147. var res = {
  148. headers: {
  149. 'server-authorization': 'Hawk mac="abc", bad="xyz"'
  150. }
  151. };
  152. expect(Hawk.client.authenticate(res, {})).to.equal(false);
  153. done();
  154. });
  155. it('returns false on invalid mac', function (done) {
  156. var res = {
  157. headers: {
  158. 'content-type': 'text/plain',
  159. 'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
  160. }
  161. };
  162. var artifacts = {
  163. method: 'POST',
  164. host: 'example.com',
  165. port: '8080',
  166. resource: '/resource/4?filter=a',
  167. ts: '1362336900',
  168. nonce: 'eb5S_L',
  169. hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
  170. ext: 'some-app-data',
  171. app: undefined,
  172. dlg: undefined,
  173. mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
  174. id: '123456'
  175. };
  176. var credentials = {
  177. id: '123456',
  178. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  179. algorithm: 'sha256',
  180. user: 'steve'
  181. };
  182. expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(false);
  183. done();
  184. });
  185. it('returns true on ignoring hash', function (done) {
  186. var res = {
  187. headers: {
  188. 'content-type': 'text/plain',
  189. 'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
  190. }
  191. };
  192. var artifacts = {
  193. method: 'POST',
  194. host: 'example.com',
  195. port: '8080',
  196. resource: '/resource/4?filter=a',
  197. ts: '1362336900',
  198. nonce: 'eb5S_L',
  199. hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
  200. ext: 'some-app-data',
  201. app: undefined,
  202. dlg: undefined,
  203. mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
  204. id: '123456'
  205. };
  206. var credentials = {
  207. id: '123456',
  208. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  209. algorithm: 'sha256',
  210. user: 'steve'
  211. };
  212. expect(Hawk.client.authenticate(res, credentials, artifacts)).to.equal(true);
  213. done();
  214. });
  215. it('fails on invalid WWW-Authenticate header format', function (done) {
  216. var header = 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"';
  217. expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(false);
  218. done();
  219. });
  220. it('fails on invalid WWW-Authenticate header format', function (done) {
  221. var credentials = {
  222. id: '123456',
  223. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  224. algorithm: 'sha256',
  225. user: 'steve'
  226. };
  227. var header = 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"';
  228. expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, credentials)).to.equal(false);
  229. done();
  230. });
  231. it('skips tsm validation when missing ts', function (done) {
  232. var header = 'Hawk error="Stale timestamp"';
  233. expect(Hawk.client.authenticate({ headers: { 'www-authenticate': header } }, {})).to.equal(true);
  234. done();
  235. });
  236. });
  237. describe('message()', function () {
  238. it('generates authorization', function (done) {
  239. var credentials = {
  240. id: '123456',
  241. key: '2983d45yun89q',
  242. algorithm: 'sha1'
  243. };
  244. var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  245. expect(auth).to.exist();
  246. expect(auth.ts).to.equal(1353809207);
  247. expect(auth.nonce).to.equal('abc123');
  248. done();
  249. });
  250. it('errors on invalid host', function (done) {
  251. var credentials = {
  252. id: '123456',
  253. key: '2983d45yun89q',
  254. algorithm: 'sha1'
  255. };
  256. var auth = Hawk.client.message(5, 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  257. expect(auth).to.not.exist();
  258. done();
  259. });
  260. it('errors on invalid port', function (done) {
  261. var credentials = {
  262. id: '123456',
  263. key: '2983d45yun89q',
  264. algorithm: 'sha1'
  265. };
  266. var auth = Hawk.client.message('example.com', '80', 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  267. expect(auth).to.not.exist();
  268. done();
  269. });
  270. it('errors on missing host', function (done) {
  271. var credentials = {
  272. id: '123456',
  273. key: '2983d45yun89q',
  274. algorithm: 'sha1'
  275. };
  276. var auth = Hawk.client.message('example.com', 0, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  277. expect(auth).to.not.exist();
  278. done();
  279. });
  280. it('errors on null message', function (done) {
  281. var credentials = {
  282. id: '123456',
  283. key: '2983d45yun89q',
  284. algorithm: 'sha1'
  285. };
  286. var auth = Hawk.client.message('example.com', 80, null, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  287. expect(auth).to.not.exist();
  288. done();
  289. });
  290. it('errors on missing message', function (done) {
  291. var credentials = {
  292. id: '123456',
  293. key: '2983d45yun89q',
  294. algorithm: 'sha1'
  295. };
  296. var auth = Hawk.client.message('example.com', 80, undefined, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  297. expect(auth).to.not.exist();
  298. done();
  299. });
  300. it('errors on invalid message', function (done) {
  301. var credentials = {
  302. id: '123456',
  303. key: '2983d45yun89q',
  304. algorithm: 'sha1'
  305. };
  306. var auth = Hawk.client.message('example.com', 80, 5, { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  307. expect(auth).to.not.exist();
  308. done();
  309. });
  310. it('errors on missing options', function (done) {
  311. var credentials = {
  312. id: '123456',
  313. key: '2983d45yun89q',
  314. algorithm: 'sha1'
  315. };
  316. var auth = Hawk.client.message('example.com', 80, 'I am the boodyman');
  317. expect(auth).to.not.exist();
  318. done();
  319. });
  320. it('errors on invalid credentials (id)', function (done) {
  321. var credentials = {
  322. key: '2983d45yun89q',
  323. algorithm: 'sha1'
  324. };
  325. var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  326. expect(auth).to.not.exist();
  327. done();
  328. });
  329. it('errors on invalid credentials (key)', function (done) {
  330. var credentials = {
  331. id: '123456',
  332. algorithm: 'sha1'
  333. };
  334. var auth = Hawk.client.message('example.com', 80, 'I am the boodyman', { credentials: credentials, timestamp: 1353809207, nonce: 'abc123' });
  335. expect(auth).to.not.exist();
  336. done();
  337. });
  338. });
  339. });