auth.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. var createHash = require('crypto').createHash;
  2. function get_header(header, credentials, opts) {
  3. var type = header.split(' ')[0],
  4. user = credentials[0],
  5. pass = credentials[1];
  6. if (type == 'Digest') {
  7. return digest.generate(header, user, pass, opts.method, opts.path);
  8. } else if (type == 'Basic') {
  9. return basic(user, pass);
  10. }
  11. }
  12. ////////////////////
  13. // basic
  14. function md5(string) {
  15. return createHash('md5').update(string).digest('hex');
  16. }
  17. function basic(user, pass) {
  18. var str = typeof pass == 'undefined' ? user : [user, pass].join(':');
  19. return 'Basic ' + Buffer.from(str).toString('base64');
  20. }
  21. ////////////////////
  22. // digest
  23. // logic inspired from https://github.com/simme/node-http-digest-client
  24. var digest = {};
  25. digest.parse_header = function(header) {
  26. var challenge = {},
  27. matches = header.match(/([a-z0-9_-]+)="?([a-z0-9_=\/\.@\s-\+:)()]+)"?/gi);
  28. for (var i = 0, l = matches.length; i < l; i++) {
  29. var parts = matches[i].split('='),
  30. key = parts.shift(),
  31. val = parts.join('=').replace(/^"/, '').replace(/"$/, '');
  32. challenge[key] = val;
  33. }
  34. return challenge;
  35. }
  36. digest.update_nc = function(nc) {
  37. var max = 99999999;
  38. nc++;
  39. if (nc > max)
  40. nc = 1;
  41. var padding = new Array(8).join('0') + '';
  42. nc = nc + '';
  43. return padding.substr(0, 8 - nc.length) + nc;
  44. }
  45. digest.generate = function(header, user, pass, method, path) {
  46. var nc = 1,
  47. cnonce = null,
  48. challenge = digest.parse_header(header);
  49. var ha1 = md5(user + ':' + challenge.realm + ':' + pass),
  50. ha2 = md5(method.toUpperCase() + ':' + path),
  51. resp = [ha1, challenge.nonce];
  52. if (typeof challenge.qop === 'string') {
  53. cnonce = md5(Math.random().toString(36)).substr(0, 8);
  54. nc = digest.update_nc(nc);
  55. resp = resp.concat(nc, cnonce);
  56. resp = resp.concat(challenge.qop, ha2);
  57. } else {
  58. resp = resp.concat(ha2);
  59. }
  60. var params = {
  61. uri : path,
  62. realm : challenge.realm,
  63. nonce : challenge.nonce,
  64. username : user,
  65. response : md5(resp.join(':'))
  66. }
  67. if (challenge.qop) {
  68. params.qop = challenge.qop;
  69. }
  70. if (challenge.opaque) {
  71. params.opaque = challenge.opaque;
  72. }
  73. if (cnonce) {
  74. params.nc = nc;
  75. params.cnonce = cnonce;
  76. }
  77. header = []
  78. for (var k in params)
  79. header.push(k + '="' + params[k] + '"')
  80. return 'Digest ' + header.join(', ');
  81. }
  82. module.exports = {
  83. header : get_header,
  84. basic : basic,
  85. digest : digest.generate
  86. }