Revamp workspace archive handling with async support, input validation improvements and better session management
This commit is contained in:
+16
-9
@@ -6,13 +6,14 @@ import {asyncHandler} from "../middleware/asyncHandler.js";
|
||||
import bcrypt from "bcrypt";
|
||||
import {validateBody} from "../middleware/validate.js";
|
||||
import {newPasswordError} from "../services/passwordPolicy.js";
|
||||
import {normalizeUsername, usernameError} from "../services/usernamePolicy.js";
|
||||
|
||||
const credentialsValidator = (body) => {
|
||||
const username = String(body?.username || "").trim();
|
||||
const username = normalizeUsername(body?.username);
|
||||
const password = String(body?.password || "");
|
||||
const inviteCode = String(body?.inviteCode || "").trim();
|
||||
if (!username) return {error: "Username is required"};
|
||||
if (username.length < 3) return {error: "Username must be at least 3 characters"};
|
||||
const usernameValidationError = usernameError(username);
|
||||
if (usernameValidationError) return {error: usernameValidationError};
|
||||
if (!password) return {error: "Password is required"};
|
||||
if (password.length < 6) return {error: "Password must be at least 6 characters"};
|
||||
return {value: {username, password, inviteCode}};
|
||||
@@ -20,6 +21,14 @@ const credentialsValidator = (body) => {
|
||||
|
||||
const DUMMY_PASSWORD_HASH = "$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy";
|
||||
|
||||
const regenerateSession = (req, fields) => new Promise((resolve, reject) => {
|
||||
req.session.regenerate(error => {
|
||||
if (error) return reject(error);
|
||||
Object.assign(req.session, fields);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
export function createAuthRouter({repositories, authRateLimiters, config}) {
|
||||
const router = express.Router();
|
||||
|
||||
@@ -30,7 +39,7 @@ export function createAuthRouter({repositories, authRateLimiters, config}) {
|
||||
if (config.registrationInviteCode && inviteCode !== config.registrationInviteCode) {
|
||||
return res.status(403).json({error: "Valid invite code is required"});
|
||||
}
|
||||
if (repositories.getUser(username)) {
|
||||
if (repositories.getUserCaseInsensitive(username)) {
|
||||
return res.status(409).json({error: "User already exists", code: "USERNAME_TAKEN"});
|
||||
}
|
||||
|
||||
@@ -44,21 +53,19 @@ export function createAuthRouter({repositories, authRateLimiters, config}) {
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
req.session.user = username;
|
||||
req.session.sessionVersion = 0;
|
||||
await regenerateSession(req, {user: username, sessionVersion: 0});
|
||||
return res.json({message: "Registration successful"});
|
||||
}));
|
||||
|
||||
router.post("/login", ...authRateLimiters, validateBody(credentialsValidator), asyncHandler(async (req, res) => {
|
||||
const {username, password} = req.validatedBody;
|
||||
const user = repositories.getUser(username);
|
||||
const user = repositories.getUserCaseInsensitive(username);
|
||||
const valid = await bcrypt.compare(password, user?.password || DUMMY_PASSWORD_HASH);
|
||||
if (!user || !valid) {
|
||||
// Keep the message generic and still perform bcrypt work for unknown users.
|
||||
return res.status(user ? 403 : 400).json({error: "Invalid username or password"});
|
||||
}
|
||||
req.session.user = username;
|
||||
req.session.sessionVersion = Number(user.session_version || 0);
|
||||
await regenerateSession(req, {user: user.username, sessionVersion: Number(user.session_version || 0)});
|
||||
return res.json({message: "Login successful"});
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user