index.js 3.1 KB

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