Blame view

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