mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-31 19:17:13 +00:00
* 📮 fix: Consume an Invite Only Once the Account Exists `checkInviteUser` deleted the invite token and then called `next()`, but everything that can still reject a registration runs after it: the schema, the allowed-domain check, and the email-already-in-use check. A mistyped password confirmation therefore destroyed the invite — the invitee corrected it, resubmitted, and got "Invalid invite token" with no way back short of an admin re-inviting them. The deletion moves to `registrationController`, after `registerUser` reports success. That report needed a new signal. `registerUser` returns the same 200 and the same generic message whether it created an account or found the email already in use — deliberately, so the response cannot be used to enumerate accounts — so the status alone cannot say whether an account exists. It now also returns `userCreated` on the creation path, which the controller reads and never forwards; the response body is unchanged. A failed deletion is logged rather than surfaced. By that point the account exists, and leaving a usable invite behind is recoverable in a way that telling the user their registration failed is not. Fixes #15541 * ♻️ test: Fold the Invite Tests Into the Existing AuthController Spec From Copilot's review. The new spec reached for `jest.requireActual` on `@librechat/data-schemas`, `~/server/services/AuthService` and `~/models`, which pulls real implementations into a unit test — `~/models` builds the data-schemas methods against mongoose — and diverges from `AuthController.spec.js`, which stubs each module outright. Rather than restate that harness with narrower stubs, the tests move into the spec that already has it. `deleteTokens` joins its `~/models` mock, which also keeps that shared mock in step with the controller's imports: a stale mock there hands the controller an undefined function, and the omission only surfaces when some later test happens to exercise the path.
34 lines
1.1 KiB
JavaScript
34 lines
1.1 KiB
JavaScript
const { getInvite: getInviteFn } = require('@librechat/api');
|
|
const { createToken, findToken } = require('~/models');
|
|
|
|
const getInvite = (encodedToken, email) =>
|
|
getInviteFn(encodedToken, email, { createToken, findToken });
|
|
|
|
async function checkInviteUser(req, res, next) {
|
|
const token = req.body.token;
|
|
|
|
if (!token || token === 'undefined') {
|
|
next();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const invite = await getInvite(token, req.body.email);
|
|
|
|
if (!invite || invite.error === true) {
|
|
return res.status(400).json({ message: 'Invalid invite token' });
|
|
}
|
|
|
|
/** The invite is deliberately left in place here. Everything that can still
|
|
* reject this registration — the schema, the allowed-domain check, an email
|
|
* already in use — runs after this middleware, and consuming the invite first
|
|
* meant a mistyped password confirmation destroyed it. `registrationController`
|
|
* deletes it once an account actually exists. */
|
|
req.invite = invite;
|
|
next();
|
|
} catch (error) {
|
|
return res.status(429).json({ message: error.message });
|
|
}
|
|
}
|
|
|
|
module.exports = checkInviteUser;
|