aegis-auth
Version:
A credentials-based auth solution for Next.js (and other Node projects) with IP rate-limiting, account lockouts, and sessions in DB.
100 lines • 3.73 kB
JavaScript
import { VerificationType } from "@prisma/client";
import { hashPassword } from "../security";
import { ErrorCodes } from "../types";
import { limitIpAttempts } from "../utils";
import { completePasswordResetSchema } from "../validations";
import { useVerificationToken } from "./useVerificationToken";
export async function completePasswordReset(context, input) {
var _a;
const { prisma, config, limiters, parsedRequest } = context;
const { ipAddress } = parsedRequest !== null && parsedRequest !== void 0 ? parsedRequest : {};
try {
// Validate input
const validatedInput = completePasswordResetSchema.safeParse(input);
if (!validatedInput.success) {
config.logger.securityEvent("INVALID_INPUT", {
route: "completePasswordReset",
ipAddress,
});
return {
success: false,
status: 400,
message: "Invalid input provided",
code: ErrorCodes.INVALID_INPUT,
};
}
const { token, newPassword } = validatedInput.data;
if (config.rateLimiting.completePasswordReset.enabled && ipAddress) {
const limiter = limiters.completePasswordReset;
const limit = await limitIpAttempts({ ipAddress, limiter });
if (!limit.success) {
config.logger.securityEvent("RATE_LIMIT_EXCEEDED", {
route: "completePasswordReset",
ipAddress,
});
return {
success: false,
status: 429,
message: "Too many attempts. Try again later.",
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
};
}
}
const verification = await useVerificationToken(context, {
token: token,
type: VerificationType.PASSWORD_RESET,
});
if (!verification.success || !((_a = verification.data) === null || _a === void 0 ? void 0 : _a.verification)) {
config.logger.securityEvent("INVALID_TOKEN", {
route: "completePasswordReset",
ipAddress,
});
return {
success: false,
status: 400,
message: "Invalid token.",
code: ErrorCodes.INVALID_TOKEN,
};
}
const { verification: { userId }, } = verification.data;
const hashedPassword = await hashPassword({
password: newPassword,
config,
});
await prisma.$transaction([
prisma.user.update({
where: { id: userId },
data: { password: hashedPassword },
}),
prisma.session.updateMany({
where: {
userId: userId,
isRevoked: false,
},
data: { isRevoked: true },
}),
]);
config.logger.securityEvent("PASSWORD_RESET", {
ipAddress,
userId: userId,
});
return {
success: true,
status: 200,
message: "Password reset successfully.",
};
}
catch (error) {
config.logger.error("Error completing password reset", {
error,
ipAddress: parsedRequest === null || parsedRequest === void 0 ? void 0 : parsedRequest.ipAddress,
});
return {
success: false,
status: 500,
message: "An unexpected error occurred.",
code: ErrorCodes.INTERNAL_SERVER_ERROR,
};
}
}
//# sourceMappingURL=completePasswordReset.js.map