token.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import _ from "lodash";
  2. import errs from "../lib/error.js";
  3. import { parseDatePeriod } from "../lib/helpers.js";
  4. import authModel from "../models/auth.js";
  5. import TokenModel from "../models/token.js";
  6. import userModel from "../models/user.js";
  7. const ERROR_MESSAGE_INVALID_AUTH = "Invalid email or password";
  8. const ERROR_MESSAGE_INVALID_AUTH_I18N = "error.invalid-auth";
  9. export default {
  10. /**
  11. * @param {Object} data
  12. * @param {String} data.identity
  13. * @param {String} data.secret
  14. * @param {String} [data.scope]
  15. * @param {String} [data.expiry]
  16. * @param {String} [issuer]
  17. * @returns {Promise}
  18. */
  19. getTokenFromEmail: (data, issuer) => {
  20. const Token = TokenModel();
  21. data.scope = data.scope || "user";
  22. data.expiry = data.expiry || "1d";
  23. return userModel
  24. .query()
  25. .where("email", data.identity.toLowerCase().trim())
  26. .andWhere("is_deleted", 0)
  27. .andWhere("is_disabled", 0)
  28. .first()
  29. .then((user) => {
  30. if (user) {
  31. // Get auth
  32. return authModel
  33. .query()
  34. .where("user_id", "=", user.id)
  35. .where("type", "=", "password")
  36. .first()
  37. .then((auth) => {
  38. if (auth) {
  39. return auth.verifyPassword(data.secret).then((valid) => {
  40. if (valid) {
  41. if (data.scope !== "user" && _.indexOf(user.roles, data.scope) === -1) {
  42. // The scope requested doesn't exist as a role against the user,
  43. // you shall not pass.
  44. throw new errs.AuthError(`Invalid scope: ${data.scope}`);
  45. }
  46. // Create a moment of the expiry expression
  47. const expiry = parseDatePeriod(data.expiry);
  48. if (expiry === null) {
  49. throw new errs.AuthError(`Invalid expiry time: ${data.expiry}`);
  50. }
  51. return Token.create({
  52. iss: issuer || "api",
  53. attrs: {
  54. id: user.id,
  55. },
  56. scope: [data.scope],
  57. expiresIn: data.expiry,
  58. }).then((signed) => {
  59. return {
  60. token: signed.token,
  61. expires: expiry.toISOString(),
  62. };
  63. });
  64. }
  65. throw new errs.AuthError(
  66. ERROR_MESSAGE_INVALID_AUTH,
  67. ERROR_MESSAGE_INVALID_AUTH_I18N,
  68. );
  69. });
  70. }
  71. throw new errs.AuthError(ERROR_MESSAGE_INVALID_AUTH);
  72. });
  73. }
  74. throw new errs.AuthError(ERROR_MESSAGE_INVALID_AUTH);
  75. });
  76. },
  77. /**
  78. * @param {Access} access
  79. * @param {Object} [data]
  80. * @param {String} [data.expiry]
  81. * @param {String} [data.scope] Only considered if existing token scope is admin
  82. * @returns {Promise}
  83. */
  84. getFreshToken: (access, data) => {
  85. const Token = TokenModel();
  86. const thisData = data || {};
  87. thisData.expiry = thisData.expiry || "1d";
  88. if (access?.token.getUserId(0)) {
  89. // Create a moment of the expiry expression
  90. const expiry = parseDatePeriod(thisData.expiry);
  91. if (expiry === null) {
  92. throw new errs.AuthError(`Invalid expiry time: ${thisData.expiry}`);
  93. }
  94. const token_attrs = {
  95. id: access.token.getUserId(0),
  96. };
  97. // Only admins can request otherwise scoped tokens
  98. let scope = access.token.get("scope");
  99. if (thisData.scope && access.token.hasScope("admin")) {
  100. scope = [thisData.scope];
  101. if (thisData.scope === "job-board" || thisData.scope === "worker") {
  102. token_attrs.id = 0;
  103. }
  104. }
  105. return Token.create({
  106. iss: "api",
  107. scope: scope,
  108. attrs: token_attrs,
  109. expiresIn: thisData.expiry,
  110. }).then((signed) => {
  111. return {
  112. token: signed.token,
  113. expires: expiry.toISOString(),
  114. };
  115. });
  116. }
  117. throw new error.AssertionFailedError("Existing token contained invalid user data");
  118. },
  119. /**
  120. * @param {Object} user
  121. * @returns {Promise}
  122. */
  123. getTokenFromUser: async (user) => {
  124. const expire = "1d";
  125. const Token = new TokenModel();
  126. const expiry = parseDatePeriod(expire);
  127. const signed = await Token.create({
  128. iss: "api",
  129. attrs: {
  130. id: user.id,
  131. },
  132. scope: ["user"],
  133. expiresIn: expire,
  134. });
  135. return {
  136. token: signed.token,
  137. expires: expiry.toISOString(),
  138. user: user,
  139. };
  140. },
  141. };