@proteinjs/user-server 1.20.2 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/generated/index.js +1 -1
  3. package/dist/generated/index.js.map +1 -1
  4. package/dist/src/authentication/PasswordResetToken.d.ts +45 -0
  5. package/dist/src/authentication/PasswordResetToken.d.ts.map +1 -0
  6. package/dist/src/authentication/PasswordResetToken.js +138 -0
  7. package/dist/src/authentication/PasswordResetToken.js.map +1 -0
  8. package/dist/src/routes/executePasswordReset.d.ts +4 -4
  9. package/dist/src/routes/executePasswordReset.d.ts.map +1 -1
  10. package/dist/src/routes/executePasswordReset.js +27 -26
  11. package/dist/src/routes/executePasswordReset.js.map +1 -1
  12. package/dist/src/routes/initiatePasswordReset.js +2 -2
  13. package/dist/src/routes/initiatePasswordReset.js.map +1 -1
  14. package/dist/src/routes/validateResetPasswordToken.d.ts.map +1 -1
  15. package/dist/src/routes/validateResetPasswordToken.js +13 -16
  16. package/dist/src/routes/validateResetPasswordToken.js.map +1 -1
  17. package/dist/src/services/Signup.d.ts +14 -0
  18. package/dist/src/services/Signup.d.ts.map +1 -1
  19. package/dist/src/services/Signup.js +102 -27
  20. package/dist/src/services/Signup.js.map +1 -1
  21. package/dist/test/ExecutePasswordReset.test.d.ts +2 -0
  22. package/dist/test/ExecutePasswordReset.test.d.ts.map +1 -0
  23. package/dist/test/ExecutePasswordReset.test.js +394 -0
  24. package/dist/test/ExecutePasswordReset.test.js.map +1 -0
  25. package/dist/test/SignupResendInvite.integration.test.d.ts +2 -0
  26. package/dist/test/SignupResendInvite.integration.test.d.ts.map +1 -0
  27. package/dist/test/SignupResendInvite.integration.test.js +235 -0
  28. package/dist/test/SignupResendInvite.integration.test.js.map +1 -0
  29. package/dist/test/ValidateResetToken.test.js +30 -7
  30. package/dist/test/ValidateResetToken.test.js.map +1 -1
  31. package/generated/index.ts +1 -1
  32. package/package.json +3 -3
  33. package/src/authentication/PasswordResetToken.ts +96 -0
  34. package/src/routes/executePasswordReset.ts +25 -24
  35. package/src/routes/initiatePasswordReset.ts +2 -2
  36. package/src/routes/validateResetPasswordToken.ts +12 -15
  37. package/src/services/Signup.ts +71 -21
  38. package/test/ExecutePasswordReset.test.ts +224 -0
  39. package/test/SignupResendInvite.integration.test.ts +163 -0
  40. package/test/ValidateResetToken.test.ts +24 -6
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.PasswordResetToken = void 0;
43
+ var crypto_1 = require("crypto");
44
+ var moment_1 = __importDefault(require("moment"));
45
+ var db_1 = require("@proteinjs/db");
46
+ var user_1 = require("@proteinjs/user");
47
+ /**
48
+ * The password-reset token's one owner: its shape, its mint, the lookup that maps a presented
49
+ * value back to the user it was issued to, and its single-use redemption.
50
+ *
51
+ * A token is 32 bytes from the platform CSPRNG, hex-encoded (64 lowercase hex characters),
52
+ * stored on the user row beside its expiry until it is redeemed or expires.
53
+ *
54
+ * Resolution refuses anything that is not a value of that shape BEFORE any lookup. A query
55
+ * filter built from a request value that is not a token does not compare the way a token does:
56
+ * `null` renders as `IS NULL` and would match every account with no pending reset, an empty
57
+ * string matches an emptied column, and other types reach the driver. The row the lookup
58
+ * returns is then re-checked in code — its stored token must be a string equal to the presented
59
+ * one (compared in constant time) and its expiry a real timestamp still in the future — so the
60
+ * outcome never rests on how the storage compares.
61
+ */
62
+ var PasswordResetToken = /** @class */ (function () {
63
+ function PasswordResetToken() {
64
+ }
65
+ PasswordResetToken.prototype.mint = function () {
66
+ return (0, crypto_1.randomBytes)(32).toString('hex');
67
+ };
68
+ PasswordResetToken.prototype.resolve = function (presented) {
69
+ return __awaiter(this, void 0, void 0, function () {
70
+ var token, user;
71
+ return __generator(this, function (_a) {
72
+ switch (_a.label) {
73
+ case 0:
74
+ token = this.parse(presented);
75
+ if (token === undefined) {
76
+ return [2 /*return*/, { status: 'malformed' }];
77
+ }
78
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(user_1.tables.User, { passwordResetToken: token })];
79
+ case 1:
80
+ user = _a.sent();
81
+ if (!user || !this.matches(user.passwordResetToken, token)) {
82
+ return [2 /*return*/, { status: 'unknown' }];
83
+ }
84
+ if (!this.isLive(user.passwordResetTokenExpiration)) {
85
+ return [2 /*return*/, { status: 'expired', user: user }];
86
+ }
87
+ return [2 /*return*/, { status: 'live', user: user, token: token }];
88
+ }
89
+ });
90
+ });
91
+ };
92
+ /**
93
+ * Write the new credential and clear the token in one conditional update: the row must still
94
+ * carry this token at write time, so two presentations of the same token cannot both succeed.
95
+ * Resolves false when the token was already redeemed.
96
+ */
97
+ PasswordResetToken.prototype.redeem = function (user, token, hashedPassword) {
98
+ return __awaiter(this, void 0, void 0, function () {
99
+ var updated;
100
+ return __generator(this, function (_a) {
101
+ switch (_a.label) {
102
+ case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().update(user_1.tables.User, { password: hashedPassword, passwordResetToken: null, passwordResetTokenExpiration: null }, { id: user.id, passwordResetToken: token })];
103
+ case 1:
104
+ updated = _a.sent();
105
+ return [2 /*return*/, updated === 1];
106
+ }
107
+ });
108
+ });
109
+ };
110
+ /** A log-safe reference to a presented token: a short digest of a well-formed one, never the value itself. */
111
+ PasswordResetToken.prototype.fingerprint = function (presented) {
112
+ var token = this.parse(presented);
113
+ if (token === undefined) {
114
+ return undefined;
115
+ }
116
+ return (0, crypto_1.createHash)('sha256').update(token).digest('hex').slice(0, 12);
117
+ };
118
+ PasswordResetToken.prototype.parse = function (presented) {
119
+ return typeof presented === 'string' && PasswordResetToken.SHAPE.test(presented) ? presented : undefined;
120
+ };
121
+ PasswordResetToken.prototype.matches = function (stored, presented) {
122
+ if (typeof stored !== 'string' || stored.length !== presented.length) {
123
+ return false;
124
+ }
125
+ return (0, crypto_1.timingSafeEqual)(Buffer.from(stored), Buffer.from(presented));
126
+ };
127
+ PasswordResetToken.prototype.isLive = function (expiration) {
128
+ if (!expiration) {
129
+ return false;
130
+ }
131
+ var expiresAt = (0, moment_1.default)(expiration);
132
+ return expiresAt.isValid() && (0, moment_1.default)().isBefore(expiresAt);
133
+ };
134
+ PasswordResetToken.SHAPE = /^[0-9a-f]{64}$/;
135
+ return PasswordResetToken;
136
+ }());
137
+ exports.PasswordResetToken = PasswordResetToken;
138
+ //# sourceMappingURL=PasswordResetToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordResetToken.js","sourceRoot":"","sources":["../../../src/authentication/PasswordResetToken.ts"],"names":[],"mappings":"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"}
@@ -2,13 +2,13 @@ import { Route } from '@proteinjs/server-api';
2
2
  /**
3
3
  * Route handler for executing a password reset.
4
4
  *
5
- * This function handles the process of resetting a user's password using a provided reset token.
6
- * It verifies the token, checks its expiration, and updates the user's password if everything is valid.
5
+ * Resolves the presented token through `PasswordResetToken` — which refuses anything but a
6
+ * well-formed token before any lookup — checks its expiry, and redeems it: the new password is
7
+ * written and the token cleared in one conditional update, so a token resets a password once.
8
+ * The token itself never reaches the log.
7
9
  *
8
10
  * @bodyParam {string} token - The password reset token.
9
11
  * @bodyParam {string} newPassword - The new password for the user.
10
- *
11
- * @throws {Error} If there's an issue with the database operations or if the token is invalid or expired.
12
12
  */
13
13
  export declare const executePasswordReset: Route;
14
14
  //# sourceMappingURL=executePasswordReset.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"executePasswordReset.d.ts","sourceRoot":"","sources":["../../../src/routes/executePasswordReset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAO9C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAqClC,CAAC"}
1
+ {"version":3,"file":"executePasswordReset.d.ts","sourceRoot":"","sources":["../../../src/routes/executePasswordReset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAM9C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAuClC,CAAC"}
@@ -35,64 +35,65 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
36
  }
37
37
  };
38
- var __importDefault = (this && this.__importDefault) || function (mod) {
39
- return (mod && mod.__esModule) ? mod : { "default": mod };
40
- };
41
38
  Object.defineProperty(exports, "__esModule", { value: true });
42
39
  exports.executePasswordReset = void 0;
43
- var db_1 = require("@proteinjs/db");
44
40
  var user_1 = require("@proteinjs/user");
45
41
  var logger_1 = require("@proteinjs/logger");
46
- var moment_1 = __importDefault(require("moment"));
47
42
  var PasswordHasher_1 = require("../authentication/PasswordHasher");
43
+ var PasswordResetToken_1 = require("../authentication/PasswordResetToken");
48
44
  /**
49
45
  * Route handler for executing a password reset.
50
46
  *
51
- * This function handles the process of resetting a user's password using a provided reset token.
52
- * It verifies the token, checks its expiration, and updates the user's password if everything is valid.
47
+ * Resolves the presented token through `PasswordResetToken` — which refuses anything but a
48
+ * well-formed token before any lookup — checks its expiry, and redeems it: the new password is
49
+ * written and the token cleared in one conditional update, so a token resets a password once.
50
+ * The token itself never reaches the log.
53
51
  *
54
52
  * @bodyParam {string} token - The password reset token.
55
53
  * @bodyParam {string} newPassword - The new password for the user.
56
- *
57
- * @throws {Error} If there's an issue with the database operations or if the token is invalid or expired.
58
54
  */
59
55
  exports.executePasswordReset = {
60
56
  path: user_1.routes.executePasswordReset.path,
61
57
  method: user_1.routes.executePasswordReset.method,
62
58
  onRequest: function (request, response) { return __awaiter(void 0, void 0, void 0, function () {
63
- var logger, _a, token, newPassword, db, user, currentTime, tokenExpiration, hashedPassword;
59
+ var logger, _a, token, newPassword, resetToken, resolution, user, hashedPassword;
64
60
  return __generator(this, function (_b) {
65
61
  switch (_b.label) {
66
62
  case 0:
67
63
  logger = new logger_1.Logger({ name: 'executePasswordReset' });
68
64
  _a = request.body, token = _a.token, newPassword = _a.newPassword;
69
- db = (0, db_1.getDbAsSystem)();
70
- return [4 /*yield*/, db.get(user_1.tables.User, { passwordResetToken: token })];
65
+ if (typeof newPassword !== 'string' || newPassword.length === 0) {
66
+ response.status(400).send({ error: 'New password cannot be blank' });
67
+ return [2 /*return*/];
68
+ }
69
+ resetToken = new PasswordResetToken_1.PasswordResetToken();
70
+ return [4 /*yield*/, resetToken.resolve(token)];
71
71
  case 1:
72
- user = _b.sent();
73
- if (!user) {
74
- logger.info({ message: "Invalid reset token used", obj: { token: token } });
72
+ resolution = _b.sent();
73
+ if (resolution.status === 'malformed' || resolution.status === 'unknown') {
74
+ logger.info({
75
+ message: "Invalid reset token used",
76
+ obj: { reason: resolution.status, token: resetToken.fingerprint(token) },
77
+ });
75
78
  response.status(400).send({ error: 'Invalid or expired reset token' });
76
79
  return [2 /*return*/];
77
80
  }
78
- currentTime = (0, moment_1.default)();
79
- tokenExpiration = (0, moment_1.default)(user.passwordResetTokenExpiration);
80
- if (currentTime.isAfter(tokenExpiration)) {
81
- logger.info({ message: "Expired reset token used", obj: { email: user.email } });
81
+ if (resolution.status === 'expired') {
82
+ logger.info({ message: "Expired reset token used", obj: { email: resolution.user.email } });
82
83
  response.status(400).send({ error: 'Reset token has expired' });
83
84
  return [2 /*return*/];
84
85
  }
86
+ user = resolution.user;
85
87
  return [4 /*yield*/, new PasswordHasher_1.PasswordHasher().hash(newPassword)];
86
88
  case 2:
87
89
  hashedPassword = _b.sent();
88
- return [4 /*yield*/, db.update(user_1.tables.User, {
89
- id: user.id,
90
- password: hashedPassword,
91
- passwordResetToken: null,
92
- passwordResetTokenExpiration: null,
93
- })];
90
+ return [4 /*yield*/, resetToken.redeem(user, resolution.token, hashedPassword)];
94
91
  case 3:
95
- _b.sent();
92
+ if (!(_b.sent())) {
93
+ logger.info({ message: "Reset token already redeemed", obj: { email: user.email } });
94
+ response.status(400).send({ error: 'Invalid or expired reset token' });
95
+ return [2 /*return*/];
96
+ }
96
97
  logger.info({ message: "Password successfully reset", obj: { email: user.email } });
97
98
  response.send({ message: 'Password has been successfully reset' });
98
99
  return [2 /*return*/];
@@ -1 +1 @@
1
- {"version":3,"file":"executePasswordReset.js","sourceRoot":"","sources":["../../../src/routes/executePasswordReset.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oCAA8C;AAC9C,wCAAiD;AACjD,4CAA2C;AAC3C,kDAA4B;AAC5B,mEAAkE;AAElE;;;;;;;;;;GAUG;AACU,QAAA,oBAAoB,GAAU;IACzC,IAAI,EAAE,aAAM,CAAC,oBAAoB,CAAC,IAAI;IACtC,MAAM,EAAE,aAAM,CAAC,oBAAoB,CAAC,MAAM;IAC1C,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;oBAC3B,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;oBACtD,KAAyB,OAAO,CAAC,IAAI,EAAnC,KAAK,WAAA,EAAE,WAAW,iBAAA,CAAkB;oBACtC,EAAE,GAAG,IAAA,kBAAa,GAAE,CAAC;oBAGd,qBAAM,EAAE,CAAC,GAAG,CAAC,aAAM,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAA;;oBAA/D,IAAI,GAAG,SAAwD;oBACrE,IAAI,CAAC,IAAI,EAAE;wBACT,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,OAAA,EAAE,EAAE,CAAC,CAAC;wBACrE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;wBACvE,sBAAO;qBACR;oBAGK,WAAW,GAAG,IAAA,gBAAM,GAAE,CAAC;oBACvB,eAAe,GAAG,IAAA,gBAAM,EAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;oBAClE,IAAI,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;wBACxC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBACjF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;wBAChE,sBAAO;qBACR;oBAGsB,qBAAM,IAAI,+BAAc,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAA;;oBAA7D,cAAc,GAAG,SAA4C;oBACnE,qBAAM,EAAE,CAAC,MAAM,CAAC,aAAM,CAAC,IAAI,EAAE;4BAC3B,EAAE,EAAE,IAAI,CAAC,EAAE;4BACX,QAAQ,EAAE,cAAc;4BACxB,kBAAkB,EAAE,IAAI;4BACxB,4BAA4B,EAAE,IAAI;yBACnC,CAAC,EAAA;;oBALF,SAKE,CAAC;oBAEH,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC,CAAC;;;;SACpE;CACF,CAAC"}
1
+ {"version":3,"file":"executePasswordReset.js","sourceRoot":"","sources":["../../../src/routes/executePasswordReset.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,wCAAyC;AACzC,4CAA2C;AAC3C,mEAAkE;AAClE,2EAA0E;AAE1E;;;;;;;;;;GAUG;AACU,QAAA,oBAAoB,GAAU;IACzC,IAAI,EAAE,aAAM,CAAC,oBAAoB,CAAC,IAAI;IACtC,MAAM,EAAE,aAAM,CAAC,oBAAoB,CAAC,MAAM;IAC1C,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;oBAC3B,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;oBACtD,KAAyB,OAAO,CAAC,IAAI,EAAnC,KAAK,WAAA,EAAE,WAAW,iBAAA,CAAkB;oBAC5C,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;wBAC/D,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,CAAC,CAAC;wBACrE,sBAAO;qBACR;oBAEK,UAAU,GAAG,IAAI,uCAAkB,EAAE,CAAC;oBACzB,qBAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAA5C,UAAU,GAAG,SAA+B;oBAClD,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE;wBACxE,MAAM,CAAC,IAAI,CAAC;4BACV,OAAO,EAAE,0BAA0B;4BACnC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;yBACzE,CAAC,CAAC;wBACH,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;wBACvE,sBAAO;qBACR;oBAED,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE;wBACnC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBAC5F,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;wBAChE,sBAAO;qBACR;oBAEO,IAAI,GAAK,UAAU,KAAf,CAAgB;oBACL,qBAAM,IAAI,+BAAc,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAA;;oBAA7D,cAAc,GAAG,SAA4C;oBAC7D,qBAAM,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,EAAA;;oBAArE,IAAI,CAAC,CAAC,SAA+D,CAAC,EAAE;wBACtE,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,8BAA8B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBACrF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;wBACvE,sBAAO;qBACR;oBAED,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC,CAAC;;;;SACpE;CACF,CAAC"}
@@ -56,7 +56,7 @@ var user_1 = require("@proteinjs/user");
56
56
  var logger_1 = require("@proteinjs/logger");
57
57
  var moment_1 = __importDefault(require("moment"));
58
58
  var email_server_1 = require("@proteinjs/email-server");
59
- var crypto_js_1 = require("crypto-js");
59
+ var PasswordResetToken_1 = require("../authentication/PasswordResetToken");
60
60
  /**
61
61
  * Route for initiating a password reset process.
62
62
  *
@@ -108,7 +108,7 @@ exports.initiatePasswordReset = {
108
108
  if (!defaultConfigFactory) {
109
109
  throw new Error("Unable to find a @proteinjs/email-server/DefaultPasswordResetEmailConfigFactory implementation when initiating password reset.");
110
110
  }
111
- passwordResetToken = crypto_js_1.lib.WordArray.random(32).toString();
111
+ passwordResetToken = new PasswordResetToken_1.PasswordResetToken().mint();
112
112
  passwordResetTokenExpiration = (0, moment_1.default)().add(1, 'hour');
113
113
  _d.label = 2;
114
114
  case 2:
@@ -1 +1 @@
1
- {"version":3,"file":"initiatePasswordReset.js","sourceRoot":"","sources":["../../../src/routes/initiatePasswordReset.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"initiatePasswordReset.js","sourceRoot":"","sources":["../../../src/routes/initiatePasswordReset.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oCAA8C;AAC9C,wCAA2D;AAC3D,4CAA2C;AAC3C,kDAA4B;AAC5B,wDAGiC;AACjC,2EAA0E;AAE1E;;;;;;;;;;;;GAYG;AACU,QAAA,qBAAqB,GAAU;IAC1C,IAAI,EAAE,aAAM,CAAC,qBAAqB,CAAC,IAAI;IACvC,MAAM,EAAE,aAAM,CAAC,qBAAqB,CAAC,MAAM;IAC3C,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;;oBAC3B,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;oBACvD,KAAK,GAAG,MAAA,OAAO,CAAC,IAAI,CAAC,KAAK,0CAAE,WAAW,EAAE,CAAC;oBAC1C,EAAE,GAAG,IAAA,kBAAa,GAAE,CAAC;oBAErB,eAAe,GAAG,EAAE,OAAO,EAAE,2EAA2E,EAAE,CAAC;oBAEpG,qBAAM,EAAE,CAAC,GAAG,CAAC,aAAM,CAAC,IAAI,EAAE,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;;oBAA3C,IAAI,GAAG,SAAoC;oBACjD,IAAI,CAAC,IAAI,EAAE;wBACT,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,gDAAgD,EAAE,GAAG,EAAE,EAAE,KAAK,OAAA,EAAE,EAAE,CAAC,CAAC;wBAC3F,2CAA2C;wBAC3C,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;wBAC/B,sBAAO;qBACR;oBAED,sEAAsE;oBACtE,IAAI,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,4BAA4B,EAAE;wBAC1D,WAAW,GAAG,IAAA,gBAAM,GAAE,CAAC;wBACvB,iBAAiB,GAAG,IAAA,gBAAM,EAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;wBAClF,cAAc,GAAG,WAAW,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;wBACtE,IAAI,cAAc,GAAG,CAAC,EAAE;4BACtB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,4CAA4C,EAAE,GAAG,EAAE,EAAE,KAAK,OAAA,EAAE,EAAE,CAAC,CAAC;4BACvF,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;4BAC/B,sBAAO;yBACR;qBACF;oBAEK,WAAW,GAAG,IAAI,0BAAW,EAAE,CAAC;oBAEhC,oBAAoB,GAAG,IAAA,wDAAuB,GAAE,CAAC;oBACvD,IAAI,CAAC,oBAAoB,EAAE;wBACzB,MAAM,IAAI,KAAK,CACb,gIAAgI,CACjI,CAAC;qBACH;oBAGK,kBAAkB,GAAG,IAAI,uCAAkB,EAAE,CAAC,IAAI,EAAE,CAAC;oBACrD,4BAA4B,GAAG,IAAA,gBAAM,GAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;;;;oBAGrD,MAAM,GAAG,oBAAoB,CAAC,SAAS,EAAE,CAAC;oBAC1C,KAAiB,MAAM,CAAC,eAAe,CAAC,UAAG,eAAQ,CAAC,IAAI,CAAC,aAAa,oBAAU,kBAAkB,CAAE,CAAC,EAAnG,IAAI,UAAA,EAAE,IAAI,UAAA,CAA0F;oBAE5G,qCAAqC;oBACrC,qBAAM,WAAW,CAAC,SAAS,YACzB,EAAE,EAAE,IAAI,CAAC,KAAK,EACd,OAAO,EAAE,CAAA,MAAA,MAAM,CAAC,OAAO,0CAAE,OAAO,KAAI,gBAAgB,EACpD,IAAI,MAAA,EACJ,IAAI,MAAA,IACD,MAAM,CAAC,OAAO,EACjB,EAAA;;oBAPF,qCAAqC;oBACrC,SAME,CAAC;oBAEH,iEAAiE;oBACjE,qBAAM,EAAE,CAAC,MAAM,CAAC,aAAM,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,kBAAkB,oBAAA,EAAE,4BAA4B,8BAAA,EAAE,CAAC,EAAA;;oBAD/F,iEAAiE;oBACjE,SAA+F,CAAC;oBAChG,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;;;;oBAE/B,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,GAAG,EAAE,EAAE,KAAK,OAAA,EAAE,EAAE,KAAK,SAAA,EAAE,CAAC,CAAC;oBACxF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8DAA8D,EAAE,CAAC,CAAC;;;;;SAExG;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validateResetPasswordToken.d.ts","sourceRoot":"","sources":["../../../src/routes/validateResetPasswordToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAM9C,eAAO,MAAM,0BAA0B,EAAE,KAoCxC,CAAC"}
1
+ {"version":3,"file":"validateResetPasswordToken.d.ts","sourceRoot":"","sources":["../../../src/routes/validateResetPasswordToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAK9C,eAAO,MAAM,0BAA0B,EAAE,KAkCxC,CAAC"}
@@ -35,42 +35,39 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
36
  }
37
37
  };
38
- var __importDefault = (this && this.__importDefault) || function (mod) {
39
- return (mod && mod.__esModule) ? mod : { "default": mod };
40
- };
41
38
  Object.defineProperty(exports, "__esModule", { value: true });
42
39
  exports.validateResetPasswordToken = void 0;
43
- var db_1 = require("@proteinjs/db");
44
40
  var user_1 = require("@proteinjs/user");
45
41
  var logger_1 = require("@proteinjs/logger");
46
- var moment_1 = __importDefault(require("moment"));
42
+ var PasswordResetToken_1 = require("../authentication/PasswordResetToken");
47
43
  exports.validateResetPasswordToken = {
48
44
  path: user_1.routes.validateResetToken.path,
49
45
  method: user_1.routes.validateResetToken.method,
50
46
  onRequest: function (request, response) { return __awaiter(void 0, void 0, void 0, function () {
51
- var logger, token, db, user, currentTime, tokenExpiration;
47
+ var logger, token, resetToken, resolution;
52
48
  return __generator(this, function (_a) {
53
49
  switch (_a.label) {
54
50
  case 0:
55
51
  logger = new logger_1.Logger({ name: 'validateResetToken' });
56
52
  token = request.query.token;
57
- db = (0, db_1.getDbAsSystem)();
58
53
  if (!token) {
59
54
  response.status(400).send({ isValid: false, message: 'No token provided' });
60
55
  return [2 /*return*/];
61
56
  }
62
- return [4 /*yield*/, db.get(user_1.tables.User, { passwordResetToken: token })];
57
+ resetToken = new PasswordResetToken_1.PasswordResetToken();
58
+ return [4 /*yield*/, resetToken.resolve(token)];
63
59
  case 1:
64
- user = _a.sent();
65
- if (!user) {
66
- logger.info({ message: "Invalid reset token used", obj: { token: token } });
60
+ resolution = _a.sent();
61
+ if (resolution.status === 'malformed' || resolution.status === 'unknown') {
62
+ logger.info({
63
+ message: "Invalid reset token used",
64
+ obj: { reason: resolution.status, token: resetToken.fingerprint(token) },
65
+ });
67
66
  response.status(200).send({ isValid: false, message: 'Invalid token' });
68
67
  return [2 /*return*/];
69
68
  }
70
- currentTime = (0, moment_1.default)();
71
- tokenExpiration = (0, moment_1.default)(user.passwordResetTokenExpiration);
72
- if (currentTime.isAfter(tokenExpiration)) {
73
- logger.info({ message: "Expired reset token used", obj: { email: user.email } });
69
+ if (resolution.status === 'expired') {
70
+ logger.info({ message: "Expired reset token used", obj: { email: resolution.user.email } });
74
71
  response.status(200).send({ isValid: false, message: 'Token has expired' });
75
72
  return [2 /*return*/];
76
73
  }
@@ -78,7 +75,7 @@ exports.validateResetPasswordToken = {
78
75
  // read-only `autocomplete="username"` field so password managers associate the updated
79
76
  // password with the stored credential. The token was delivered to this very inbox, so a
80
77
  // valid-token holder learns nothing new; invalid/expired verdicts stay email-free.
81
- response.status(200).send({ isValid: true, email: user.email });
78
+ response.status(200).send({ isValid: true, email: resolution.user.email });
82
79
  return [2 /*return*/];
83
80
  }
84
81
  });
@@ -1 +1 @@
1
- {"version":3,"file":"validateResetPasswordToken.js","sourceRoot":"","sources":["../../../src/routes/validateResetPasswordToken.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,oCAA8C;AAC9C,wCAAiD;AACjD,4CAA2C;AAC3C,kDAA4B;AAEf,QAAA,0BAA0B,GAAU;IAC/C,IAAI,EAAE,aAAM,CAAC,kBAAkB,CAAC,IAAI;IACpC,MAAM,EAAE,aAAM,CAAC,kBAAkB,CAAC,MAAM;IACxC,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;oBAC3B,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC;oBAClD,KAAK,GAAK,OAAO,CAAC,KAAK,MAAlB,CAAmB;oBAC1B,EAAE,GAAG,IAAA,kBAAa,GAAE,CAAC;oBAE3B,IAAI,CAAC,KAAK,EAAE;wBACV,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;wBAC5E,sBAAO;qBACR;oBAGY,qBAAM,EAAE,CAAC,GAAG,CAAC,aAAM,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAA;;oBAA/D,IAAI,GAAG,SAAwD;oBACrE,IAAI,CAAC,IAAI,EAAE;wBACT,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,OAAA,EAAE,EAAE,CAAC,CAAC;wBACrE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;wBACxE,sBAAO;qBACR;oBAGK,WAAW,GAAG,IAAA,gBAAM,GAAE,CAAC;oBACvB,eAAe,GAAG,IAAA,gBAAM,EAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;oBAClE,IAAI,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;wBACxC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBACjF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;wBAC5E,sBAAO;qBACR;oBAED,oFAAoF;oBACpF,uFAAuF;oBACvF,wFAAwF;oBACxF,mFAAmF;oBACnF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;;;;SACjE;CACF,CAAC"}
1
+ {"version":3,"file":"validateResetPasswordToken.js","sourceRoot":"","sources":["../../../src/routes/validateResetPasswordToken.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,wCAAyC;AACzC,4CAA2C;AAC3C,2EAA0E;AAE7D,QAAA,0BAA0B,GAAU;IAC/C,IAAI,EAAE,aAAM,CAAC,kBAAkB,CAAC,IAAI;IACpC,MAAM,EAAE,aAAM,CAAC,kBAAkB,CAAC,MAAM;IACxC,SAAS,EAAE,UAAO,OAAO,EAAE,QAAQ;;;;;oBAC3B,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC;oBAClD,KAAK,GAAK,OAAO,CAAC,KAAK,MAAlB,CAAmB;oBAChC,IAAI,CAAC,KAAK,EAAE;wBACV,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;wBAC5E,sBAAO;qBACR;oBAEK,UAAU,GAAG,IAAI,uCAAkB,EAAE,CAAC;oBACzB,qBAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAA;;oBAA5C,UAAU,GAAG,SAA+B;oBAClD,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE;wBACxE,MAAM,CAAC,IAAI,CAAC;4BACV,OAAO,EAAE,0BAA0B;4BACnC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;yBACzE,CAAC,CAAC;wBACH,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;wBACxE,sBAAO;qBACR;oBAED,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE;wBACnC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBAC5F,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;wBAC5E,sBAAO;qBACR;oBAED,oFAAoF;oBACpF,uFAAuF;oBACvF,wFAAwF;oBACxF,mFAAmF;oBACnF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;;;;SAC5E;CACF,CAAC"}
@@ -50,6 +50,14 @@ export declare class Signup implements SignupService {
50
50
  */
51
51
  createUser(user: UserSignup, token?: string): Promise<CreateUserResult>;
52
52
  sendInvite(email: string): Promise<SendInviteResponse>;
53
+ /**
54
+ * Re-sends a standing invite: the token in the earlier email stops working, a fresh token with
55
+ * a fresh expiry takes its place on the same row (never a second row), and the invite email goes
56
+ * out again through the same config. Refused when no invite stands for the address — there is
57
+ * nothing to re-send — and when the address already has an account (the invite was used).
58
+ * The inviter stays whoever sent it first.
59
+ */
60
+ resendInvite(email: string): Promise<SendInviteResponse>;
53
61
  revokeInvite(email: string): Promise<void>;
54
62
  /**
55
63
  * Initializes signup process, validating invite configuration and token if provided.
@@ -73,5 +81,11 @@ export declare class Signup implements SignupService {
73
81
  }): Promise<'created' | 'exists'>;
74
82
  /** Resolves an invite token, distinguishing "expired" from "never existed / already revoked". */
75
83
  private lookupInvite;
84
+ /** The invite email content factory the consumer registers; its absence is a misconfiguration, said aloud. */
85
+ private inviteEmailConfig;
86
+ /** A fresh redeemable token with its expiry from the TTL knob — the one minting path for send and re-send. */
87
+ private mintInviteToken;
88
+ /** The invite email itself — the signup link carrying `token`, rendered by the consumer's config. */
89
+ private emailInvite;
76
90
  }
77
91
  //# sourceMappingURL=Signup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Signup.d.ts","sourceRoot":"","sources":["../../../src/services/Signup.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAIlB,aAAa,EACb,wBAAwB,EAExB,MAAM,EACN,IAAI,EACJ,UAAU,EAEX,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAGnE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEhH,MAAM,WAAW,0BAA2B,SAAQ,QAAQ;IAC1D,SAAS,IAAI,YAAY,CAAC;CAC3B;AAED,eAAO,MAAM,6BAA6B,QAAO,0BAShD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC9B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,MAAO,YAAW,aAAa;IACnC,eAAe;;oCAEM,MAAM,QAAQ,GAAG,EAAE;;MAY7C;IAEF;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DvE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0DtD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD;;;OAGG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAoD1F;;;;;;;OAOG;IACG,aAAa,CAAC,OAAO,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;KAC9B,GAAG,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;IAmBjC,iGAAiG;YACnF,YAAY;CAc3B"}
1
+ {"version":3,"file":"Signup.d.ts","sourceRoot":"","sources":["../../../src/services/Signup.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAIlB,aAAa,EACb,wBAAwB,EAExB,MAAM,EACN,IAAI,EACJ,UAAU,EAEX,MAAM,iBAAiB,CAAC;AAUzB,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAGnE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEhH,MAAM,WAAW,0BAA2B,SAAQ,QAAQ;IAC1D,SAAS,IAAI,YAAY,CAAC;CAC3B;AAED,eAAO,MAAM,6BAA6B,QAAO,0BAShD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC9B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,MAAO,YAAW,aAAa;IACnC,eAAe;;oCAEM,MAAM,QAAQ,GAAG,EAAE;;MAY7C;IAEF;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DvE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyC5D;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA4BxD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD;;;OAGG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAoD1F;;;;;;;OAOG;IACG,aAAa,CAAC,OAAO,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;KAC9B,GAAG,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC;IAmBjC,iGAAiG;YACnF,YAAY;IAe1B,8GAA8G;IAC9G,OAAO,CAAC,iBAAiB;IAUzB,8GAA8G;IAC9G,OAAO,CAAC,eAAe;IAOvB,qGAAqG;YACvF,WAAW;CAU1B"}
@@ -82,7 +82,7 @@ var Signup = /** @class */ (function () {
82
82
  // previously evaluated the check WITHOUT returning it, which made sendInvite/revokeInvite
83
83
  // effectively public — any caller (even logged out) could mint themselves a valid signup
84
84
  // token and bypass invite-only signup.
85
- if (methodName === 'sendInvite' || methodName === 'revokeInvite') {
85
+ if (methodName === 'sendInvite' || methodName === 'resendInvite' || methodName === 'revokeInvite') {
86
86
  return user_1.UserAuth.hasPermission(user_1.USER_PERMISSIONS.users);
87
87
  }
88
88
  return true;
@@ -155,40 +155,33 @@ var Signup = /** @class */ (function () {
155
155
  });
156
156
  };
157
157
  Signup.prototype.sendInvite = function (email) {
158
- var _a;
159
158
  return __awaiter(this, void 0, void 0, function () {
160
- var logger, caseInsensitiveEmail, db, userRecord, emailSender, defaultConfigFactory, config, token, tokenExpiresAt, invite, userId, _b, text, html, error_1;
161
- return __generator(this, function (_c) {
162
- switch (_c.label) {
159
+ var logger, caseInsensitiveEmail, db, userRecord, config, _a, token, tokenExpiresAt, invite, userId, error_1;
160
+ return __generator(this, function (_b) {
161
+ switch (_b.label) {
163
162
  case 0:
164
163
  logger = new logger_1.Logger({ name: 'Signup.sendInvite' });
165
164
  caseInsensitiveEmail = email.toLowerCase();
166
- _c.label = 1;
165
+ _b.label = 1;
167
166
  case 1:
168
- _c.trys.push([1, 9, , 10]);
167
+ _b.trys.push([1, 9, , 10]);
169
168
  db = (0, db_1.getDbAsSystem)();
170
169
  return [4 /*yield*/, db.get(user_1.tables.User, { email: caseInsensitiveEmail })];
171
170
  case 2:
172
- userRecord = _c.sent();
171
+ userRecord = _b.sent();
173
172
  if (userRecord) {
174
173
  return [2 /*return*/, { sent: false, error: 'User already exists with that email.' }];
175
174
  }
176
- emailSender = new email_server_1.EmailSender();
177
- defaultConfigFactory = (0, email_server_1.getDefaultInviteEmailConfigFactory)();
178
- if (!defaultConfigFactory) {
179
- throw new Error("Unable to find a @proteinjs/email-server/DefaultInviteEmailConfigFactory implementation when sending invite.");
180
- }
181
- config = defaultConfigFactory.getConfig();
182
- token = crypto_js_1.lib.WordArray.random(32).toString();
183
- tokenExpiresAt = (0, moment_1.default)().add(exports.INVITE_TOKEN_TTL_DAYS, 'days');
175
+ config = this.inviteEmailConfig();
176
+ _a = this.mintInviteToken(), token = _a.token, tokenExpiresAt = _a.tokenExpiresAt;
184
177
  return [4 /*yield*/, db.get(user_1.tables.Invite, { email: caseInsensitiveEmail })];
185
178
  case 3:
186
- invite = _c.sent();
179
+ invite = _b.sent();
187
180
  if (!invite) return [3 /*break*/, 5];
188
181
  invite = __assign(__assign({}, invite), { token: token, tokenExpiresAt: tokenExpiresAt });
189
182
  return [4 /*yield*/, db.update(user_1.tables.Invite, invite)];
190
183
  case 4:
191
- _c.sent();
184
+ _b.sent();
192
185
  return [3 /*break*/, 7];
193
186
  case 5:
194
187
  userId = new user_1.UserRepo().getUser().id;
@@ -199,16 +192,14 @@ var Signup = /** @class */ (function () {
199
192
  invitedBy: new db_1.Reference(user_1.tables.User.name, userId),
200
193
  })];
201
194
  case 6:
202
- invite = _c.sent();
203
- _c.label = 7;
204
- case 7:
205
- _b = config.getEmailContent("".concat(user_1.uiRoutes.auth.signup, "?token=").concat(token)), text = _b.text, html = _b.html;
206
- return [4 /*yield*/, emailSender.sendEmail(__assign({ to: caseInsensitiveEmail, subject: ((_a = config.options) === null || _a === void 0 ? void 0 : _a.subject) || "You're Invited", text: text, html: html }, config.options))];
195
+ invite = _b.sent();
196
+ _b.label = 7;
197
+ case 7: return [4 /*yield*/, this.emailInvite(caseInsensitiveEmail, token, config)];
207
198
  case 8:
208
- _c.sent();
199
+ _b.sent();
209
200
  return [2 /*return*/, { sent: true }];
210
201
  case 9:
211
- error_1 = _c.sent();
202
+ error_1 = _b.sent();
212
203
  logger.error({ message: 'Error sending invite', obj: { email: caseInsensitiveEmail }, error: error_1 });
213
204
  return [2 /*return*/, {
214
205
  sent: false,
@@ -219,6 +210,58 @@ var Signup = /** @class */ (function () {
219
210
  });
220
211
  });
221
212
  };
213
+ /**
214
+ * Re-sends a standing invite: the token in the earlier email stops working, a fresh token with
215
+ * a fresh expiry takes its place on the same row (never a second row), and the invite email goes
216
+ * out again through the same config. Refused when no invite stands for the address — there is
217
+ * nothing to re-send — and when the address already has an account (the invite was used).
218
+ * The inviter stays whoever sent it first.
219
+ */
220
+ Signup.prototype.resendInvite = function (email) {
221
+ return __awaiter(this, void 0, void 0, function () {
222
+ var logger, caseInsensitiveEmail, db, userRecord, invite, config, _a, token, tokenExpiresAt, error_2;
223
+ return __generator(this, function (_b) {
224
+ switch (_b.label) {
225
+ case 0:
226
+ logger = new logger_1.Logger({ name: 'Signup.resendInvite' });
227
+ caseInsensitiveEmail = email.toLowerCase();
228
+ _b.label = 1;
229
+ case 1:
230
+ _b.trys.push([1, 6, , 7]);
231
+ db = (0, db_1.getDbAsSystem)();
232
+ return [4 /*yield*/, db.get(user_1.tables.User, { email: caseInsensitiveEmail })];
233
+ case 2:
234
+ userRecord = _b.sent();
235
+ if (userRecord) {
236
+ return [2 /*return*/, { sent: false, error: 'User already exists with that email.' }];
237
+ }
238
+ return [4 /*yield*/, db.get(user_1.tables.Invite, { email: caseInsensitiveEmail })];
239
+ case 3:
240
+ invite = _b.sent();
241
+ if (!invite) {
242
+ return [2 /*return*/, { sent: false, error: 'No invite exists for that email.' }];
243
+ }
244
+ config = this.inviteEmailConfig();
245
+ _a = this.mintInviteToken(), token = _a.token, tokenExpiresAt = _a.tokenExpiresAt;
246
+ return [4 /*yield*/, db.update(user_1.tables.Invite, __assign(__assign({}, invite), { token: token, tokenExpiresAt: tokenExpiresAt }))];
247
+ case 4:
248
+ _b.sent();
249
+ return [4 /*yield*/, this.emailInvite(caseInsensitiveEmail, token, config)];
250
+ case 5:
251
+ _b.sent();
252
+ return [2 /*return*/, { sent: true }];
253
+ case 6:
254
+ error_2 = _b.sent();
255
+ logger.error({ message: 'Error re-sending invite', obj: { email: caseInsensitiveEmail }, error: error_2 });
256
+ return [2 /*return*/, {
257
+ sent: false,
258
+ error: 'Error occurred.',
259
+ }];
260
+ case 7: return [2 /*return*/];
261
+ }
262
+ });
263
+ });
264
+ };
222
265
  Signup.prototype.revokeInvite = function (email) {
223
266
  return __awaiter(this, void 0, void 0, function () {
224
267
  var db;
@@ -243,7 +286,7 @@ var Signup = /** @class */ (function () {
243
286
  */
244
287
  Signup.prototype.initializeSignup = function (inviteToken) {
245
288
  return __awaiter(this, void 0, void 0, function () {
246
- var config, isInviteOnly, lookup, error_2;
289
+ var config, isInviteOnly, lookup, error_3;
247
290
  return __generator(this, function (_a) {
248
291
  switch (_a.label) {
249
292
  case 0:
@@ -286,7 +329,7 @@ var Signup = /** @class */ (function () {
286
329
  isInviteOnly: isInviteOnly,
287
330
  }];
288
331
  case 3:
289
- error_2 = _a.sent();
332
+ error_3 = _a.sent();
290
333
  return [2 /*return*/, {
291
334
  isReady: false,
292
335
  error: 'Initializing sign up failed.',
@@ -360,6 +403,38 @@ var Signup = /** @class */ (function () {
360
403
  });
361
404
  });
362
405
  };
406
+ /** The invite email content factory the consumer registers; its absence is a misconfiguration, said aloud. */
407
+ Signup.prototype.inviteEmailConfig = function () {
408
+ var defaultConfigFactory = (0, email_server_1.getDefaultInviteEmailConfigFactory)();
409
+ if (!defaultConfigFactory) {
410
+ throw new Error("Unable to find a @proteinjs/email-server/DefaultInviteEmailConfigFactory implementation when sending invite.");
411
+ }
412
+ return defaultConfigFactory.getConfig();
413
+ };
414
+ /** A fresh redeemable token with its expiry from the TTL knob — the one minting path for send and re-send. */
415
+ Signup.prototype.mintInviteToken = function () {
416
+ return {
417
+ token: crypto_js_1.lib.WordArray.random(32).toString(),
418
+ tokenExpiresAt: (0, moment_1.default)().add(exports.INVITE_TOKEN_TTL_DAYS, 'days'),
419
+ };
420
+ };
421
+ /** The invite email itself — the signup link carrying `token`, rendered by the consumer's config. */
422
+ Signup.prototype.emailInvite = function (email, token, config) {
423
+ var _a;
424
+ return __awaiter(this, void 0, void 0, function () {
425
+ var _b, text, html;
426
+ return __generator(this, function (_c) {
427
+ switch (_c.label) {
428
+ case 0:
429
+ _b = config.getEmailContent("".concat(user_1.uiRoutes.auth.signup, "?token=").concat(token)), text = _b.text, html = _b.html;
430
+ return [4 /*yield*/, new email_server_1.EmailSender().sendEmail(__assign({ to: email, subject: ((_a = config.options) === null || _a === void 0 ? void 0 : _a.subject) || "You're Invited", text: text, html: html }, config.options))];
431
+ case 1:
432
+ _c.sent();
433
+ return [2 /*return*/];
434
+ }
435
+ });
436
+ });
437
+ };
363
438
  return Signup;
364
439
  }());
365
440
  exports.Signup = Signup;