-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
83 lines (74 loc) · 2.43 KB
/
index.js
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
var crypto = require('crypto')
var uuid = require('node-uuid')
function digest (user, pass, method, path, authHeader) {
// TODO: More complete implementation of RFC 2617.
// - handle challenge.domain
// - support qop="auth-int" only
// - handle Authentication-Info (not necessarily?)
// - check challenge.stale (not necessarily?)
// - increase nc (not necessarily?)
// For reference:
// http://tools.ietf.org/html/rfc2617#section-3
// https://github.com/bagder/curl/blob/master/lib/http_digest.c
var challenge = {}
var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
for (;;) {
var match = re.exec(authHeader)
if (!match) {
break
}
challenge[match[1]] = match[2] || match[3]
}
/**
* RFC 2617: handle both MD5 and MD5-sess algorithms.
*
* If the algorithm directive's value is "MD5" or unspecified, then HA1 is
* HA1=MD5(username:realm:password)
* If the algorithm directive's value is "MD5-sess", then HA1 is
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
*/
var ha1Compute = function (algorithm, user, realm, pass, nonce, cnonce) {
var ha1 = md5(user + ':' + realm + ':' + pass)
if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
return md5(ha1 + ':' + nonce + ':' + cnonce)
} else {
return ha1
}
}
var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
var nc = qop && '00000001'
var cnonce = qop && uuid().replace(/-/g, '')
var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce)
var ha2 = md5(method + ':' + path)
var digestResponse = qop
? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
: md5(ha1 + ':' + challenge.nonce + ':' + ha2)
var authValues = {
username: user,
realm: challenge.realm,
nonce: challenge.nonce,
uri: path,
qop: qop,
response: digestResponse,
nc: nc,
cnonce: cnonce,
algorithm: challenge.algorithm,
opaque: challenge.opaque
}
authHeader = []
for (var k in authValues) {
if (authValues[k]) {
if (k === 'qop' || k === 'nc' || k === 'algorithm') {
authHeader.push(k + '=' + authValues[k])
} else {
authHeader.push(k + '="' + authValues[k] + '"')
}
}
}
var header = 'Digest ' + authHeader.join(', ')
return header
}
function md5 (str) {
return crypto.createHash('md5').update(str).digest('hex')
}
module.exports = digest