browser.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. // Load modules
  2. var Lab = require('lab');
  3. var Hoek = require('hoek');
  4. var Hawk = require('../lib');
  5. var Browser = require('../lib/browser');
  6. var LocalStorage = require('localStorage');
  7. // Declare internals
  8. var internals = {};
  9. // Test shortcuts
  10. var expect = Lab.expect;
  11. var before = Lab.before;
  12. var after = Lab.after;
  13. var describe = Lab.experiment;
  14. var it = Lab.test;
  15. describe('Browser', function () {
  16. var credentialsFunc = function (id, callback) {
  17. var credentials = {
  18. id: id,
  19. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  20. algorithm: (id === '1' ? 'sha1' : 'sha256'),
  21. user: 'steve'
  22. };
  23. return callback(null, credentials);
  24. };
  25. it('should generate a header then successfully parse it (configuration)', function (done) {
  26. var req = {
  27. method: 'GET',
  28. url: '/resource/4?filter=a',
  29. host: 'example.com',
  30. port: 8080
  31. };
  32. credentialsFunc('123456', function (err, credentials) {
  33. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
  34. expect(req.authorization).to.exist;
  35. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  36. expect(err).to.not.exist;
  37. expect(credentials.user).to.equal('steve');
  38. expect(artifacts.ext).to.equal('some-app-data');
  39. done();
  40. });
  41. });
  42. });
  43. it('should generate a header then successfully parse it (node request)', function (done) {
  44. var req = {
  45. method: 'POST',
  46. url: '/resource/4?filter=a',
  47. headers: {
  48. host: 'example.com:8080',
  49. 'content-type': 'text/plain;x=y'
  50. }
  51. };
  52. var payload = 'some not so random text';
  53. credentialsFunc('123456', function (err, credentials) {
  54. var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
  55. req.headers.authorization = reqHeader.field;
  56. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  57. expect(err).to.not.exist;
  58. expect(credentials.user).to.equal('steve');
  59. expect(artifacts.ext).to.equal('some-app-data');
  60. expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
  61. var res = {
  62. headers: {
  63. 'content-type': 'text/plain'
  64. },
  65. getResponseHeader: function (header) {
  66. return res.headers[header.toLowerCase()];
  67. }
  68. };
  69. res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts, { payload: 'some reply', contentType: 'text/plain', ext: 'response-specific' });
  70. expect(res.headers['server-authorization']).to.exist;
  71. expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(true);
  72. done();
  73. });
  74. });
  75. });
  76. it('should generate a header then successfully parse it (no server header options)', function (done) {
  77. var req = {
  78. method: 'POST',
  79. url: '/resource/4?filter=a',
  80. headers: {
  81. host: 'example.com:8080',
  82. 'content-type': 'text/plain;x=y'
  83. }
  84. };
  85. var payload = 'some not so random text';
  86. credentialsFunc('123456', function (err, credentials) {
  87. var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
  88. req.headers.authorization = reqHeader.field;
  89. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  90. expect(err).to.not.exist;
  91. expect(credentials.user).to.equal('steve');
  92. expect(artifacts.ext).to.equal('some-app-data');
  93. expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
  94. var res = {
  95. headers: {
  96. 'content-type': 'text/plain'
  97. },
  98. getResponseHeader: function (header) {
  99. return res.headers[header.toLowerCase()];
  100. }
  101. };
  102. res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
  103. expect(res.headers['server-authorization']).to.exist;
  104. expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
  105. done();
  106. });
  107. });
  108. });
  109. it('should generate a header then successfully parse it (no server header)', function (done) {
  110. var req = {
  111. method: 'POST',
  112. url: '/resource/4?filter=a',
  113. headers: {
  114. host: 'example.com:8080',
  115. 'content-type': 'text/plain;x=y'
  116. }
  117. };
  118. var payload = 'some not so random text';
  119. credentialsFunc('123456', function (err, credentials) {
  120. var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
  121. req.headers.authorization = reqHeader.field;
  122. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  123. expect(err).to.not.exist;
  124. expect(credentials.user).to.equal('steve');
  125. expect(artifacts.ext).to.equal('some-app-data');
  126. expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
  127. var res = {
  128. headers: {
  129. 'content-type': 'text/plain'
  130. },
  131. getResponseHeader: function (header) {
  132. return res.headers[header.toLowerCase()];
  133. }
  134. };
  135. expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
  136. done();
  137. });
  138. });
  139. });
  140. it('should generate a header with stale ts and successfully authenticate on second call', function (done) {
  141. var req = {
  142. method: 'GET',
  143. url: '/resource/4?filter=a',
  144. host: 'example.com',
  145. port: 8080
  146. };
  147. credentialsFunc('123456', function (err, credentials) {
  148. Browser.utils.setNtpOffset(60 * 60 * 1000);
  149. var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });
  150. req.authorization = header.field;
  151. expect(req.authorization).to.exist;
  152. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  153. expect(err).to.exist;
  154. expect(err.message).to.equal('Stale timestamp');
  155. var res = {
  156. headers: {
  157. 'www-authenticate': err.response.headers['WWW-Authenticate']
  158. },
  159. getResponseHeader: function (header) {
  160. return res.headers[header.toLowerCase()];
  161. }
  162. };
  163. expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);
  164. expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);
  165. expect(Browser.utils.getNtpOffset()).to.equal(0);
  166. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
  167. expect(req.authorization).to.exist;
  168. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  169. expect(err).to.not.exist;
  170. expect(credentials.user).to.equal('steve');
  171. expect(artifacts.ext).to.equal('some-app-data');
  172. done();
  173. });
  174. });
  175. });
  176. });
  177. it('should generate a header with stale ts and successfully authenticate on second call (manual localStorage)', function (done) {
  178. var req = {
  179. method: 'GET',
  180. url: '/resource/4?filter=a',
  181. host: 'example.com',
  182. port: 8080
  183. };
  184. credentialsFunc('123456', function (err, credentials) {
  185. Browser.utils.setStorage(LocalStorage)
  186. Browser.utils.setNtpOffset(60 * 60 * 1000);
  187. var header = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' });
  188. req.authorization = header.field;
  189. expect(req.authorization).to.exist;
  190. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  191. expect(err).to.exist;
  192. expect(err.message).to.equal('Stale timestamp');
  193. var res = {
  194. headers: {
  195. 'www-authenticate': err.response.headers['WWW-Authenticate']
  196. },
  197. getResponseHeader: function (header) {
  198. return res.headers[header.toLowerCase()];
  199. }
  200. };
  201. expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(60 * 60 * 1000);
  202. expect(Browser.utils.getNtpOffset()).to.equal(60 * 60 * 1000);
  203. expect(Browser.client.authenticate(res, credentials, header.artifacts)).to.equal(true);
  204. expect(Browser.utils.getNtpOffset()).to.equal(0);
  205. expect(parseInt(LocalStorage.getItem('hawk_ntp_offset'))).to.equal(0);
  206. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
  207. expect(req.authorization).to.exist;
  208. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  209. expect(err).to.not.exist;
  210. expect(credentials.user).to.equal('steve');
  211. expect(artifacts.ext).to.equal('some-app-data');
  212. done();
  213. });
  214. });
  215. });
  216. });
  217. it('should generate a header then fails to parse it (missing server header hash)', function (done) {
  218. var req = {
  219. method: 'POST',
  220. url: '/resource/4?filter=a',
  221. headers: {
  222. host: 'example.com:8080',
  223. 'content-type': 'text/plain;x=y'
  224. }
  225. };
  226. var payload = 'some not so random text';
  227. credentialsFunc('123456', function (err, credentials) {
  228. var reqHeader = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', payload: payload, contentType: req.headers['content-type'] });
  229. req.headers.authorization = reqHeader.field;
  230. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  231. expect(err).to.not.exist;
  232. expect(credentials.user).to.equal('steve');
  233. expect(artifacts.ext).to.equal('some-app-data');
  234. expect(Hawk.server.authenticatePayload(payload, credentials, artifacts, req.headers['content-type'])).to.equal(true);
  235. var res = {
  236. headers: {
  237. 'content-type': 'text/plain'
  238. },
  239. getResponseHeader: function (header) {
  240. return res.headers[header.toLowerCase()];
  241. }
  242. };
  243. res.headers['server-authorization'] = Hawk.server.header(credentials, artifacts);
  244. expect(res.headers['server-authorization']).to.exist;
  245. expect(Browser.client.authenticate(res, credentials, artifacts, { payload: 'some reply' })).to.equal(false);
  246. done();
  247. });
  248. });
  249. });
  250. it('should generate a header then successfully parse it (with hash)', function (done) {
  251. var req = {
  252. method: 'GET',
  253. url: '/resource/4?filter=a',
  254. host: 'example.com',
  255. port: 8080
  256. };
  257. credentialsFunc('123456', function (err, credentials) {
  258. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
  259. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  260. expect(err).to.not.exist;
  261. expect(credentials.user).to.equal('steve');
  262. expect(artifacts.ext).to.equal('some-app-data');
  263. done();
  264. });
  265. });
  266. });
  267. it('should generate a header then successfully parse it then validate payload', function (done) {
  268. var req = {
  269. method: 'GET',
  270. url: '/resource/4?filter=a',
  271. host: 'example.com',
  272. port: 8080
  273. };
  274. credentialsFunc('123456', function (err, credentials) {
  275. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
  276. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  277. expect(err).to.not.exist;
  278. expect(credentials.user).to.equal('steve');
  279. expect(artifacts.ext).to.equal('some-app-data');
  280. expect(Hawk.server.authenticatePayload('hola!', credentials, artifacts)).to.be.true;
  281. expect(Hawk.server.authenticatePayload('hello!', credentials, artifacts)).to.be.false;
  282. done();
  283. });
  284. });
  285. });
  286. it('should generate a header then successfully parse it (app)', function (done) {
  287. var req = {
  288. method: 'GET',
  289. url: '/resource/4?filter=a',
  290. host: 'example.com',
  291. port: 8080
  292. };
  293. credentialsFunc('123456', function (err, credentials) {
  294. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased' }).field;
  295. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  296. expect(err).to.not.exist;
  297. expect(credentials.user).to.equal('steve');
  298. expect(artifacts.ext).to.equal('some-app-data');
  299. expect(artifacts.app).to.equal('asd23ased');
  300. done();
  301. });
  302. });
  303. });
  304. it('should generate a header then successfully parse it (app, dlg)', function (done) {
  305. var req = {
  306. method: 'GET',
  307. url: '/resource/4?filter=a',
  308. host: 'example.com',
  309. port: 8080
  310. };
  311. credentialsFunc('123456', function (err, credentials) {
  312. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data', app: 'asd23ased', dlg: '23434szr3q4d' }).field;
  313. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  314. expect(err).to.not.exist;
  315. expect(credentials.user).to.equal('steve');
  316. expect(artifacts.ext).to.equal('some-app-data');
  317. expect(artifacts.app).to.equal('asd23ased');
  318. expect(artifacts.dlg).to.equal('23434szr3q4d');
  319. done();
  320. });
  321. });
  322. });
  323. it('should generate a header then fail authentication due to bad hash', function (done) {
  324. var req = {
  325. method: 'GET',
  326. url: '/resource/4?filter=a',
  327. host: 'example.com',
  328. port: 8080
  329. };
  330. credentialsFunc('123456', function (err, credentials) {
  331. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, payload: 'hola!', ext: 'some-app-data' }).field;
  332. Hawk.server.authenticate(req, credentialsFunc, { payload: 'byebye!' }, function (err, credentials, artifacts) {
  333. expect(err).to.exist;
  334. expect(err.response.payload.message).to.equal('Bad payload hash');
  335. done();
  336. });
  337. });
  338. });
  339. it('should generate a header for one resource then fail to authenticate another', function (done) {
  340. var req = {
  341. method: 'GET',
  342. url: '/resource/4?filter=a',
  343. host: 'example.com',
  344. port: 8080
  345. };
  346. credentialsFunc('123456', function (err, credentials) {
  347. req.authorization = Browser.client.header('http://example.com:8080/resource/4?filter=a', req.method, { credentials: credentials, ext: 'some-app-data' }).field;
  348. req.url = '/something/else';
  349. Hawk.server.authenticate(req, credentialsFunc, {}, function (err, credentials, artifacts) {
  350. expect(err).to.exist;
  351. expect(credentials).to.exist;
  352. done();
  353. });
  354. });
  355. });
  356. describe('client', function () {
  357. describe('#header', function () {
  358. it('should return a valid authorization header (sha1)', function (done) {
  359. var credentials = {
  360. id: '123456',
  361. key: '2983d45yun89q',
  362. algorithm: 'sha1'
  363. };
  364. var header = Browser.client.header('http://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz', payload: 'something to write about' }).field;
  365. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="bsvY3IfUllw6V5rvk4tStEvpBhE=", ext="Bazinga!", mac="qbf1ZPG/r/e06F4ht+T77LXi5vw="');
  366. done();
  367. });
  368. it('should return a valid authorization header (sha256)', function (done) {
  369. var credentials = {
  370. id: '123456',
  371. key: '2983d45yun89q',
  372. algorithm: 'sha256'
  373. };
  374. var header = Browser.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;
  375. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", ext="Bazinga!", mac="q1CwFoSHzPZSkbIvl0oYlD+91rBUEvFk763nMjMndj8="');
  376. done();
  377. });
  378. it('should return a valid authorization header (no ext)', function (done) {
  379. var credentials = {
  380. id: '123456',
  381. key: '2983d45yun89q',
  382. algorithm: 'sha256'
  383. };
  384. var header = Browser.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;
  385. expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", hash="2QfCt3GuY9HQnHWyWD3wX68ZOKbynqlfYmuO2ZBRqtY=", mac="HTgtd0jPI6E4izx8e4OHdO36q00xFCU0FolNq3RiCYs="');
  386. done();
  387. });
  388. it('should return an empty authorization header on missing options', function (done) {
  389. var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST').field;
  390. expect(header).to.equal('');
  391. done();
  392. });
  393. it('should return an empty authorization header on invalid credentials', function (done) {
  394. var credentials = {
  395. key: '2983d45yun89q',
  396. algorithm: 'sha256'
  397. };
  398. var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, ext: 'Bazinga!', timestamp: 1353809207 }).field;
  399. expect(header).to.equal('');
  400. done();
  401. });
  402. it('should return an empty authorization header on invalid algorithm', function (done) {
  403. var credentials = {
  404. id: '123456',
  405. key: '2983d45yun89q',
  406. algorithm: 'hmac-sha-0'
  407. };
  408. var header = Browser.client.header('https://example.net/somewhere/over/the/rainbow', 'POST', { credentials: credentials, payload: 'something, anything!', ext: 'Bazinga!', timestamp: 1353809207 }).field;
  409. expect(header).to.equal('');
  410. done();
  411. });
  412. });
  413. describe('#authenticate', function () {
  414. it('should return false on invalid header', function (done) {
  415. var res = {
  416. headers: {
  417. 'server-authorization': 'Hawk mac="abc", bad="xyz"'
  418. },
  419. getResponseHeader: function (header) {
  420. return res.headers[header.toLowerCase()];
  421. }
  422. };
  423. expect(Browser.client.authenticate(res, {})).to.equal(false);
  424. done();
  425. });
  426. it('should return false on invalid mac', function (done) {
  427. var res = {
  428. headers: {
  429. 'content-type': 'text/plain',
  430. 'server-authorization': 'Hawk mac="_IJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
  431. },
  432. getResponseHeader: function (header) {
  433. return res.headers[header.toLowerCase()];
  434. }
  435. };
  436. var artifacts = {
  437. method: 'POST',
  438. host: 'example.com',
  439. port: '8080',
  440. resource: '/resource/4?filter=a',
  441. ts: '1362336900',
  442. nonce: 'eb5S_L',
  443. hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
  444. ext: 'some-app-data',
  445. app: undefined,
  446. dlg: undefined,
  447. mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
  448. id: '123456'
  449. };
  450. var credentials = {
  451. id: '123456',
  452. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  453. algorithm: 'sha256',
  454. user: 'steve'
  455. };
  456. expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(false);
  457. done();
  458. });
  459. it('should return true on ignoring hash', function (done) {
  460. var res = {
  461. headers: {
  462. 'content-type': 'text/plain',
  463. 'server-authorization': 'Hawk mac="XIJRsMl/4oL+nn+vKoeVZPdCHXB4yJkNnBbTbHFZUYE=", hash="f9cDF/TDm7TkYRLnGwRMfeDzT6LixQVLvrIKhh0vgmM=", ext="response-specific"'
  464. },
  465. getResponseHeader: function (header) {
  466. return res.headers[header.toLowerCase()];
  467. }
  468. };
  469. var artifacts = {
  470. method: 'POST',
  471. host: 'example.com',
  472. port: '8080',
  473. resource: '/resource/4?filter=a',
  474. ts: '1362336900',
  475. nonce: 'eb5S_L',
  476. hash: 'nJjkVtBE5Y/Bk38Aiokwn0jiJxt/0S2WRSUwWLCf5xk=',
  477. ext: 'some-app-data',
  478. app: undefined,
  479. dlg: undefined,
  480. mac: 'BlmSe8K+pbKIb6YsZCnt4E1GrYvY1AaYayNR82dGpIk=',
  481. id: '123456'
  482. };
  483. var credentials = {
  484. id: '123456',
  485. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  486. algorithm: 'sha256',
  487. user: 'steve'
  488. };
  489. expect(Browser.client.authenticate(res, credentials, artifacts)).to.equal(true);
  490. done();
  491. });
  492. it('should fail on invalid WWW-Authenticate header format', function (done) {
  493. var res = {
  494. headers: {
  495. 'www-authenticate': 'Hawk ts="1362346425875", tsm="PhwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", x="Stale timestamp"'
  496. },
  497. getResponseHeader: function (header) {
  498. return res.headers[header.toLowerCase()];
  499. }
  500. };
  501. expect(Browser.client.authenticate(res, {})).to.equal(false);
  502. done();
  503. });
  504. it('should fail on invalid WWW-Authenticate header format', function (done) {
  505. var credentials = {
  506. id: '123456',
  507. key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
  508. algorithm: 'sha256',
  509. user: 'steve'
  510. };
  511. var res = {
  512. headers: {
  513. 'www-authenticate': 'Hawk ts="1362346425875", tsm="hwayS28vtnn3qbv0mqRBYSXebN/zggEtucfeZ620Zo=", error="Stale timestamp"'
  514. },
  515. getResponseHeader: function (header) {
  516. return res.headers[header.toLowerCase()];
  517. }
  518. };
  519. expect(Browser.client.authenticate(res, credentials)).to.equal(false);
  520. done();
  521. });
  522. });
  523. describe('#message', function () {
  524. it('should generate an authorization then successfully parse it', function (done) {
  525. credentialsFunc('123456', function (err, credentials) {
  526. var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: credentials });
  527. expect(auth).to.exist;
  528. Hawk.server.authenticateMessage('example.com', 8080, 'some message', auth, credentialsFunc, {}, function (err, credentials) {
  529. expect(err).to.not.exist;
  530. expect(credentials.user).to.equal('steve');
  531. done();
  532. });
  533. });
  534. });
  535. it('should fail on missing host', function (done) {
  536. credentialsFunc('123456', function (err, credentials) {
  537. var auth = Browser.client.message(null, 8080, 'some message', { credentials: credentials });
  538. expect(auth).to.not.exist;
  539. done();
  540. });
  541. });
  542. it('should fail on missing credentials', function (done) {
  543. var auth = Browser.client.message('example.com', 8080, 'some message', {});
  544. expect(auth).to.not.exist;
  545. done();
  546. });
  547. it('should fail on invalid algorithm', function (done) {
  548. credentialsFunc('123456', function (err, credentials) {
  549. var creds = Hoek.clone(credentials);
  550. creds.algorithm = 'blah';
  551. var auth = Browser.client.message('example.com', 8080, 'some message', { credentials: creds });
  552. expect(auth).to.not.exist;
  553. done();
  554. });
  555. });
  556. });
  557. describe('#authenticateTimestamp', function (done) {
  558. it('should validate a timestamp', function (done) {
  559. credentialsFunc('123456', function (err, credentials) {
  560. var tsm = Hawk.crypto.timestampMessage(credentials);
  561. expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(true);
  562. done();
  563. });
  564. });
  565. it('should detect a bad timestamp', function (done) {
  566. credentialsFunc('123456', function (err, credentials) {
  567. var tsm = Hawk.crypto.timestampMessage(credentials);
  568. tsm.ts = 4;
  569. expect(Browser.client.authenticateTimestamp(tsm, credentials)).to.equal(false);
  570. done();
  571. });
  572. });
  573. });
  574. });
  575. describe('#parseAuthorizationHeader', function (done) {
  576. it('returns null on missing header', function (done) {
  577. expect(Browser.utils.parseAuthorizationHeader()).to.equal(null);
  578. done();
  579. });
  580. it('returns null on bad header syntax (structure)', function (done) {
  581. expect(Browser.utils.parseAuthorizationHeader('Hawk')).to.equal(null);
  582. done();
  583. });
  584. it('returns null on bad header syntax (parts)', function (done) {
  585. expect(Browser.utils.parseAuthorizationHeader(' ')).to.equal(null);
  586. done();
  587. });
  588. it('returns null on bad scheme name', function (done) {
  589. expect(Browser.utils.parseAuthorizationHeader('Basic asdasd')).to.equal(null);
  590. done();
  591. });
  592. it('returns null on bad attribute value', function (done) {
  593. expect(Browser.utils.parseAuthorizationHeader('Hawk test="\t"', ['test'])).to.equal(null);
  594. done();
  595. });
  596. it('returns null on duplicated attribute', function (done) {
  597. expect(Browser.utils.parseAuthorizationHeader('Hawk test="a", test="b"', ['test'])).to.equal(null);
  598. done();
  599. });
  600. });
  601. describe('#setNtpOffset', function (done) {
  602. it('catches localStorage errors', function (done) {
  603. var orig = Browser.utils.storage.setItem;
  604. var error = console.error;
  605. var count = 0;
  606. console.error = function () { if (count++ === 2) { console.error.error; } };
  607. Browser.utils.storage.setItem = function () {
  608. Browser.utils.storage.setItem = orig;
  609. throw new Error()
  610. };
  611. expect(function () {
  612. Browser.utils.setNtpOffset(100);
  613. }).not.to.throw();
  614. done();
  615. });
  616. });
  617. });