twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

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