@replyke/express
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
123 lines (122 loc) • 4.99 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const models_1 = require("../../../models");
const authentication_1 = require("../../../helpers/authentication");
const models_2 = require("../../../models");
const reduceAuthenticatedUserDetails_1 = __importDefault(require("../../../helpers/reduceAuthenticatedUserDetails"));
const config_1 = require("../../../config");
exports.default = async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
res.status(400).json({
error: "Email, and password are required.",
code: "auth/missing-fields",
});
return;
}
const project = req.project;
const projectId = project.id;
try {
// get the target model for that alias:
const SuspensionModel = models_1.User.associations.suspensions
.target;
const ActiveSuspensionModel = SuspensionModel.scope({
method: ["active", new Date()],
});
// Find user by username using Sequelize
const userWithSuspensions = (await models_1.User.findOne({
where: { projectId, email },
include: [
{
model: ActiveSuspensionModel,
as: "suspensions",
required: false,
},
],
}));
if (!userWithSuspensions) {
res.status(403).json({
error: "User not found.",
code: "auth/no-user-found",
});
return;
}
const { salt, hash } = userWithSuspensions;
if (!salt || !hash) {
res.status(403).json({
error: "Invalid credentials.",
code: "auth/invalid-credentials",
});
return;
}
// Validate password
const isValid = (0, authentication_1.verifyPassword)(password, hash, salt);
// If isn't valid
if (!isValid) {
res.status(401).json({
error: "Incorrect password.",
code: "auth/wrong-password",
});
return;
}
const { sequelize, refreshTokenSecret } = (0, config_1.getCoreConfig)();
// Wrap the token generation and database save in a transaction
const result = await sequelize.transaction(async (t) => {
// Create an empty Token entry (UUID will be generated here)
const refreshTokenEntry = (await models_2.Token.create({
userId: userWithSuspensions.id,
projectId,
}, { transaction: t } // Ensure this is part of the transaction
));
// Generate the JWT for the refresh token
const refreshTokenJWT = jsonwebtoken_1.default.sign({
sub: userWithSuspensions.id, // Subject, representing the user ID
projectId, // Project ID
aud: "replyke.com", // Audience
iss: "replyke.com", // Issuer
jti: refreshTokenEntry.id, // Use the token ID as the JWT ID
}, refreshTokenSecret, { expiresIn: "30d" } // Refresh token expiry
);
// Update the Token with the generated JWT and save it
refreshTokenEntry.refreshToken = refreshTokenJWT;
await refreshTokenEntry.save({ transaction: t });
// Generate the access token
const accessTokenJWT = jsonwebtoken_1.default.sign({
sub: userWithSuspensions.id, // Subject, representing the user ID
projectId, // Project ID
role: "user", // User role
aud: "replyke.com", // Audience
iss: "replyke.com", // Issuer
}, process.env.ACCESS_TOKEN_SECRET, { expiresIn: "30m" } // Access token expiry
);
return { accessTokenJWT, refreshTokenJWT };
});
const { refreshTokenJWT, accessTokenJWT } = result;
const reducedAuthenticatedUser = (0, reduceAuthenticatedUserDetails_1.default)(userWithSuspensions);
res.cookie("replyke-refresh-jwt", refreshTokenJWT, {
httpOnly: true,
sameSite: "none",
secure: true,
maxAge: 30 * 24 * 60 * 60 * 1000,
path: "/",
});
res.status(200).json({
success: true,
accessToken: accessTokenJWT,
refreshToken: refreshTokenJWT,
user: reducedAuthenticatedUser,
});
}
catch (err) {
console.error("Error signing user in:", err);
res.status(500).json({
error: "Internal server error.",
code: "auth/server-error",
details: err.message,
});
}
};