index.js (3926B)
1 var crypto = require('crypto') 2 3 function sha (key, body, algorithm) { 4 return crypto.createHmac(algorithm, key).update(body).digest('base64') 5 } 6 7 function rsa (key, body) { 8 return crypto.createSign('RSA-SHA1').update(body).sign(key, 'base64') 9 } 10 11 function rfc3986 (str) { 12 return encodeURIComponent(str) 13 .replace(/!/g,'%21') 14 .replace(/\*/g,'%2A') 15 .replace(/\(/g,'%28') 16 .replace(/\)/g,'%29') 17 .replace(/'/g,'%27') 18 } 19 20 // Maps object to bi-dimensional array 21 // Converts { foo: 'A', bar: [ 'b', 'B' ]} to 22 // [ ['foo', 'A'], ['bar', 'b'], ['bar', 'B'] ] 23 function map (obj) { 24 var key, val, arr = [] 25 for (key in obj) { 26 val = obj[key] 27 if (Array.isArray(val)) 28 for (var i = 0; i < val.length; i++) 29 arr.push([key, val[i]]) 30 else if (typeof val === 'object') 31 for (var prop in val) 32 arr.push([key + '[' + prop + ']', val[prop]]) 33 else 34 arr.push([key, val]) 35 } 36 return arr 37 } 38 39 // Compare function for sort 40 function compare (a, b) { 41 return a > b ? 1 : a < b ? -1 : 0 42 } 43 44 function generateBase (httpMethod, base_uri, params) { 45 // adapted from https://dev.twitter.com/docs/auth/oauth and 46 // https://dev.twitter.com/docs/auth/creating-signature 47 48 // Parameter normalization 49 // http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2 50 var normalized = map(params) 51 // 1. First, the name and value of each parameter are encoded 52 .map(function (p) { 53 return [ rfc3986(p[0]), rfc3986(p[1] || '') ] 54 }) 55 // 2. The parameters are sorted by name, using ascending byte value 56 // ordering. If two or more parameters share the same name, they 57 // are sorted by their value. 58 .sort(function (a, b) { 59 return compare(a[0], b[0]) || compare(a[1], b[1]) 60 }) 61 // 3. The name of each parameter is concatenated to its corresponding 62 // value using an "=" character (ASCII code 61) as a separator, even 63 // if the value is empty. 64 .map(function (p) { return p.join('=') }) 65 // 4. The sorted name/value pairs are concatenated together into a 66 // single string by using an "&" character (ASCII code 38) as 67 // separator. 68 .join('&') 69 70 var base = [ 71 rfc3986(httpMethod ? httpMethod.toUpperCase() : 'GET'), 72 rfc3986(base_uri), 73 rfc3986(normalized) 74 ].join('&') 75 76 return base 77 } 78 79 function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) { 80 var base = generateBase(httpMethod, base_uri, params) 81 var key = [ 82 consumer_secret || '', 83 token_secret || '' 84 ].map(rfc3986).join('&') 85 86 return sha(key, base, 'sha1') 87 } 88 89 function hmacsign256 (httpMethod, base_uri, params, consumer_secret, token_secret) { 90 var base = generateBase(httpMethod, base_uri, params) 91 var key = [ 92 consumer_secret || '', 93 token_secret || '' 94 ].map(rfc3986).join('&') 95 96 return sha(key, base, 'sha256') 97 } 98 99 function rsasign (httpMethod, base_uri, params, private_key, token_secret) { 100 var base = generateBase(httpMethod, base_uri, params) 101 var key = private_key || '' 102 103 return rsa(key, base) 104 } 105 106 function plaintext (consumer_secret, token_secret) { 107 var key = [ 108 consumer_secret || '', 109 token_secret || '' 110 ].map(rfc3986).join('&') 111 112 return key 113 } 114 115 function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_secret) { 116 var method 117 var skipArgs = 1 118 119 switch (signMethod) { 120 case 'RSA-SHA1': 121 method = rsasign 122 break 123 case 'HMAC-SHA1': 124 method = hmacsign 125 break 126 case 'HMAC-SHA256': 127 method = hmacsign256 128 break 129 case 'PLAINTEXT': 130 method = plaintext 131 skipArgs = 4 132 break 133 default: 134 throw new Error('Signature method not supported: ' + signMethod) 135 } 136 137 return method.apply(null, [].slice.call(arguments, skipArgs)) 138 } 139 140 exports.hmacsign = hmacsign 141 exports.hmacsign256 = hmacsign256 142 exports.rsasign = rsasign 143 exports.plaintext = plaintext 144 exports.sign = sign 145 exports.rfc3986 = rfc3986 146 exports.generateBase = generateBase