/lib/request.js

https://github.com/leppert/signature · JavaScript · 118 lines · 104 code · 11 blank · 3 comment · 27 complexity · 7863bd55500d4de1e63ee8acb397f4d0 MD5 · raw file

  1. var crypto = require('crypto');
  2. var Request = exports.Request = function(method, path, query){
  3. if(typeof path != 'string') throw "Expected string";
  4. if(typeof query != 'object') throw "Expected object";
  5. var query_hash = {},
  6. auth_hash = {},
  7. k;
  8. for(var key in query){
  9. k = key.toLowerCase();
  10. k.substring(0,5) == 'auth_' ? auth_hash[k] = query[key] : query_hash[k] = query[key];
  11. }
  12. this.method = method.toUpperCase();
  13. this.path = path;
  14. this.query_hash = query_hash;
  15. this.auth_hash = auth_hash;
  16. };
  17. Request.prototype = {
  18. sign: function(token){
  19. this.auth_hash = {
  20. auth_version: "1.0",
  21. auth_key: token.key,
  22. auth_timestamp: Math.floor(new Date().getTime()/1000)
  23. }
  24. this.auth_hash['auth_signature'] = this.signature(token);
  25. return this.auth_hash
  26. },
  27. authenticate_by_token_with_exceptions: function(token, timestamp_grace){
  28. timestamp_grace = timestamp_grace || 600;
  29. this.validate_version();
  30. this.validate_timestamp(timestamp_grace);
  31. this.validate_signature(token);
  32. return true;
  33. },
  34. authenticate_by_token: function(token, timestamp_grace){
  35. timestamp_grace = timestamp_grace || 600;
  36. try {
  37. this.authenticate_by_token_with_exceptions(token, timestamp_grace);
  38. return true;
  39. } catch(e){
  40. return false;
  41. }
  42. },
  43. authenticate: function(block, timestamp_grace){
  44. timestamp_grace = timestamp_grace || 600;
  45. var key = this.auth_hash['auth_key'];
  46. if(!key) throw "Authentication key required";
  47. var token = block(key);
  48. if(!token || !token.secret){
  49. throw "Invalid authentication key";
  50. }
  51. this.authenticate_by_token_with_exceptions(token, timestamp_grace);
  52. return token;
  53. },
  54. get auth_hash(){
  55. if(!this._auth_hash || !this._auth_hash['auth_signature']) throw "Request not signed";
  56. return this._auth_hash;
  57. },
  58. set auth_hash(val){
  59. this._auth_hash = val;
  60. },
  61. signature: function(token){
  62. return crypto.createHmac('sha256', token.secret).update(this.string_to_sign()).digest('hex');
  63. },
  64. string_to_sign: function(){
  65. return [this.method, this.path, this.parameter_string()].join("\n");
  66. },
  67. parameter_string: function(){
  68. var hash = {}, array = [], key;
  69. // Join the hashes
  70. for(key in this.query_hash){ hash[key.toLowerCase()] = this.query_hash[key]; }
  71. if(this.auth_hash) for(key in this.auth_hash){ hash[key.toLowerCase()] = this.auth_hash[key]; }
  72. // Exclude signature from signature generation
  73. delete hash["auth_signature"];
  74. // Sort the items alphabetically and join with &
  75. for(key in hash){ array.push(key+'='+hash[key]); }
  76. return array.sort().join('&');
  77. },
  78. validate_version: function(){
  79. var version = this.auth_hash['auth_version'];
  80. if(!version) throw "Version required";
  81. if(version != '1.0') throw "Version not supported";
  82. return true;
  83. },
  84. validate_timestamp: function(grace){
  85. if(grace == null) return true;
  86. var timestamp = this.auth_hash["auth_timestamp"],
  87. server_time = Math.floor(new Date().getTime()/1000),
  88. error = Math.abs(parseInt(timestamp) - server_time);
  89. if(!timestamp) throw "Timestamp required";
  90. if(error >= grace){
  91. throw "Timestamp expired: Given timestamp "+
  92. "("+timestamp+") "+
  93. "not within "+grace+"s of server time "+
  94. "("+server_time+")";
  95. }
  96. return true;
  97. },
  98. validate_signature: function(token){
  99. var _signature = this.signature(token);
  100. if(this.auth_hash["auth_signature"] != _signature){
  101. throw "Invalid signature: you should have "+
  102. "sent "+_signature+
  103. ", but you sent "+this.auth_hash["auth_signature"];
  104. }
  105. return true
  106. }
  107. };