token.js 2.9 KB

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