token.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. token_data.scope = ['user'];
  74. }
  75. resolve(token_data);
  76. }
  77. });
  78. }
  79. } catch (err) {
  80. reject(err);
  81. }
  82. });
  83. },
  84. /**
  85. * Does the token have the specified scope?
  86. *
  87. * @param {String} scope
  88. * @returns {Boolean}
  89. */
  90. hasScope: function (scope) {
  91. return typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, scope) !== -1;
  92. },
  93. /**
  94. * @param {String} key
  95. * @return {*}
  96. */
  97. get: function (key) {
  98. if (typeof token_data[key] !== 'undefined') {
  99. return token_data[key];
  100. }
  101. return null;
  102. },
  103. /**
  104. * @param {String} key
  105. * @param {*} value
  106. */
  107. set: function (key, value) {
  108. token_data[key] = value;
  109. },
  110. /**
  111. * @param [default_value]
  112. * @returns {Integer}
  113. */
  114. getUserId: (default_value) => {
  115. let attrs = self.get('attrs');
  116. if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
  117. return attrs.id;
  118. }
  119. return default_value || 0;
  120. }
  121. };
  122. return self;
  123. };