token.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /**
  2. NOTE: This is not a database table, this is a model of a Token object that can be created/loaded
  3. and then has abilities after that.
  4. */
  5. const _ = require('lodash');
  6. const jwt = require('jsonwebtoken');
  7. const crypto = require('crypto');
  8. const error = require('../lib/error');
  9. const ALGO = 'RS256';
  10. let public_key = null;
  11. let private_key = null;
  12. function checkJWTKeyPair() {
  13. if (!public_key || !private_key) {
  14. let config = require('config');
  15. public_key = config.get('jwt.pub');
  16. private_key = config.get('jwt.key');
  17. }
  18. }
  19. module.exports = function () {
  20. let token_data = {};
  21. let self = {
  22. /**
  23. * @param {Object} payload
  24. * @returns {Promise}
  25. */
  26. create: (payload) => {
  27. // sign with RSA SHA256
  28. let options = {
  29. algorithm: ALGO,
  30. expiresIn: payload.expiresIn || '1d'
  31. };
  32. payload.jti = crypto.randomBytes(12)
  33. .toString('base64')
  34. .substr(-8);
  35. checkJWTKeyPair();
  36. return new Promise((resolve, reject) => {
  37. jwt.sign(payload, private_key, options, (err, token) => {
  38. if (err) {
  39. reject(err);
  40. } else {
  41. token_data = payload;
  42. resolve({
  43. token: token,
  44. payload: payload
  45. });
  46. }
  47. });
  48. });
  49. },
  50. /**
  51. * @param {String} token
  52. * @returns {Promise}
  53. */
  54. load: function (token) {
  55. return new Promise((resolve, reject) => {
  56. checkJWTKeyPair();
  57. try {
  58. if (!token || token === null || token === 'null') {
  59. reject(new error.AuthError('Empty token'));
  60. } else {
  61. jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
  62. if (err) {
  63. if (err.name === 'TokenExpiredError') {
  64. reject(new error.AuthError('Token has expired', err));
  65. } else {
  66. reject(err);
  67. }
  68. } else {
  69. token_data = result;
  70. // Hack: some tokens out in the wild have a scope of 'all' instead of 'user'.
  71. // For 30 days at least, we need to replace 'all' with user.
  72. if ((typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, 'all') !== -1)) {
  73. //console.log('Warning! Replacing "all" scope with "user"');
  74. token_data.scope = ['user'];
  75. }
  76. resolve(token_data);
  77. }
  78. });
  79. }
  80. } catch (err) {
  81. reject(err);
  82. }
  83. });
  84. },
  85. /**
  86. * Does the token have the specified scope?
  87. *
  88. * @param {String} scope
  89. * @returns {Boolean}
  90. */
  91. hasScope: function (scope) {
  92. return typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, scope) !== -1;
  93. },
  94. /**
  95. * @param {String} key
  96. * @return {*}
  97. */
  98. get: function (key) {
  99. if (typeof token_data[key] !== 'undefined') {
  100. return token_data[key];
  101. }
  102. return null;
  103. },
  104. /**
  105. * @param {String} key
  106. * @param {*} value
  107. */
  108. set: function (key, value) {
  109. token_data[key] = value;
  110. },
  111. /**
  112. * @param [default_value]
  113. * @returns {Integer}
  114. */
  115. getUserId: (default_value) => {
  116. let attrs = self.get('attrs');
  117. if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
  118. return attrs.id;
  119. }
  120. return default_value || 0;
  121. }
  122. };
  123. return self;
  124. };