token.js 3.7 KB

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