token.js 3.0 KB

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