You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
4.9 KiB

  1. var JsonWebTokenError = require('./lib/JsonWebTokenError');
  2. var NotBeforeError = require('./lib/NotBeforeError');
  3. var TokenExpiredError = require('./lib/TokenExpiredError');
  4. var decode = require('./decode');
  5. var jws = require('jws');
  6. var ms = require('ms');
  7. var xtend = require('xtend');
  8. module.exports = function (jwtString, secretOrPublicKey, options, callback) {
  9. if ((typeof options === 'function') && !callback) {
  10. callback = options;
  11. options = {};
  12. }
  13. if (!options) {
  14. options = {};
  15. }
  16. //clone this object since we are going to mutate it.
  17. options = xtend(options);
  18. var done;
  19. if (callback) {
  20. done = function() {
  21. var args = Array.prototype.slice.call(arguments, 0);
  22. return process.nextTick(function() {
  23. callback.apply(null, args);
  24. });
  25. };
  26. } else {
  27. done = function(err, data) {
  28. if (err) throw err;
  29. return data;
  30. };
  31. }
  32. if (!jwtString){
  33. return done(new JsonWebTokenError('jwt must be provided'));
  34. }
  35. var parts = jwtString.split('.');
  36. if (parts.length !== 3){
  37. return done(new JsonWebTokenError('jwt malformed'));
  38. }
  39. var hasSignature = parts[2].trim() !== '';
  40. if (!hasSignature && secretOrPublicKey){
  41. return done(new JsonWebTokenError('jwt signature is required'));
  42. }
  43. if (hasSignature && !secretOrPublicKey) {
  44. return done(new JsonWebTokenError('secret or public key must be provided'));
  45. }
  46. if (!hasSignature && !options.algorithms) {
  47. options.algorithms = ['none'];
  48. }
  49. if (!options.algorithms) {
  50. options.algorithms = ~secretOrPublicKey.toString().indexOf('BEGIN CERTIFICATE') ||
  51. ~secretOrPublicKey.toString().indexOf('BEGIN PUBLIC KEY') ?
  52. [ 'RS256','RS384','RS512','ES256','ES384','ES512' ] :
  53. ~secretOrPublicKey.toString().indexOf('BEGIN RSA PUBLIC KEY') ?
  54. [ 'RS256','RS384','RS512' ] :
  55. [ 'HS256','HS384','HS512' ];
  56. }
  57. var decodedToken;
  58. try {
  59. decodedToken = jws.decode(jwtString);
  60. } catch(err) {
  61. return done(new JsonWebTokenError('invalid token'));
  62. }
  63. if (!decodedToken) {
  64. return done(new JsonWebTokenError('invalid token'));
  65. }
  66. var header = decodedToken.header;
  67. if (!~options.algorithms.indexOf(header.alg)) {
  68. return done(new JsonWebTokenError('invalid algorithm'));
  69. }
  70. var valid;
  71. try {
  72. valid = jws.verify(jwtString, header.alg, secretOrPublicKey);
  73. } catch (e) {
  74. return done(e);
  75. }
  76. if (!valid)
  77. return done(new JsonWebTokenError('invalid signature'));
  78. var payload;
  79. try {
  80. payload = decode(jwtString);
  81. } catch(err) {
  82. return done(err);
  83. }
  84. if (typeof payload.nbf !== 'undefined' && !options.ignoreNotBefore) {
  85. if (typeof payload.nbf !== 'number') {
  86. return done(new JsonWebTokenError('invalid nbf value'));
  87. }
  88. if (payload.nbf > Math.floor(Date.now() / 1000) + (options.clockTolerance || 0)) {
  89. return done(new NotBeforeError('jwt not active', new Date(payload.nbf * 1000)));
  90. }
  91. }
  92. if (typeof payload.exp !== 'undefined' && !options.ignoreExpiration) {
  93. if (typeof payload.exp !== 'number') {
  94. return done(new JsonWebTokenError('invalid exp value'));
  95. }
  96. if (Math.floor(Date.now() / 1000) >= payload.exp + (options.clockTolerance || 0)) {
  97. return done(new TokenExpiredError('jwt expired', new Date(payload.exp * 1000)));
  98. }
  99. }
  100. if (options.audience) {
  101. var audiences = Array.isArray(options.audience)? options.audience : [options.audience];
  102. var target = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
  103. var match = target.some(function(aud) { return audiences.indexOf(aud) != -1; });
  104. if (!match)
  105. return done(new JsonWebTokenError('jwt audience invalid. expected: ' + audiences.join(' or ')));
  106. }
  107. if (options.issuer) {
  108. var invalid_issuer =
  109. (typeof options.issuer === 'string' && payload.iss !== options.issuer) ||
  110. (Array.isArray(options.issuer) && options.issuer.indexOf(payload.iss) === -1);
  111. if (invalid_issuer) {
  112. return done(new JsonWebTokenError('jwt issuer invalid. expected: ' + options.issuer));
  113. }
  114. }
  115. if (options.subject) {
  116. if (payload.sub !== options.subject) {
  117. return done(new JsonWebTokenError('jwt subject invalid. expected: ' + options.subject));
  118. }
  119. }
  120. if (options.jwtid) {
  121. if (payload.jti !== options.jwtid) {
  122. return done(new JsonWebTokenError('jwt jwtid invalid. expected: ' + options.jwtid));
  123. }
  124. }
  125. if (options.maxAge) {
  126. var maxAge = ms(options.maxAge);
  127. if (typeof payload.iat !== 'number') {
  128. return done(new JsonWebTokenError('iat required when maxAge is specified'));
  129. }
  130. if (Date.now() - (payload.iat * 1000) > maxAge + (options.clockTolerance || 0) * 1000) {
  131. return done(new TokenExpiredError('maxAge exceeded', new Date(payload.iat * 1000 + maxAge)));
  132. }
  133. }
  134. return done(null, payload);
  135. };