infoalloggi-monorepo/apps/infoalloggi/src/server/controllers/auth.controller.ts

340 lines
7.3 KiB
TypeScript
Raw Normal View History

2025-08-04 17:45:44 +02:00
import { TRPCError } from "@trpc/server";
import { deleteCookie, setCookie } from "cookies-next";
2025-08-28 18:27:07 +02:00
import { add } from "date-fns";
import type { UsersId } from "~/schemas/public/Users";
2025-08-04 17:45:44 +02:00
import type { Context } from "~/server/api/trpc";
import { signToken } from "~/server/auth/jwt";
2025-08-04 17:45:44 +02:00
import { db } from "~/server/db";
import { isBanned } from "~/server/services/banlist.service";
import {
2025-08-28 18:27:07 +02:00
comparePasswords,
generateSalt,
hashPassword,
2025-08-04 17:45:44 +02:00
} from "~/server/services/password.service";
2025-08-28 18:27:07 +02:00
import {
addUser,
findUser_byEmail,
findUser_byId,
findUser_byId_MINI,
} from "~/server/services/user.service";
import { checkCtx } from "~/server/utils/ctxChecker";
import { TOKEN_CONFIG } from "../auth/configs";
2025-08-04 17:45:44 +02:00
export const createUserAdmin = async ({
2025-08-28 18:27:07 +02:00
email,
password,
nome,
cognome,
telefono,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
email: string;
password: string;
nome: string;
cognome: string;
telefono: string;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-29 16:18:32 +02:00
const banned = await isBanned({ data: { email }, db });
2025-08-28 18:27:07 +02:00
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const salt = generateSalt();
const hashedPassword = await hashPassword(password, salt);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const user = await addUser({
data: {
cognome: cognome.trim(),
2025-08-29 16:18:32 +02:00
email,
2025-08-28 18:27:07 +02:00
isAdminMade: true,
2025-08-29 16:18:32 +02:00
nome: nome.trim(),
password: hashedPassword,
2025-08-28 18:27:07 +02:00
salt,
2025-08-29 16:18:32 +02:00
telefono,
username: `${nome.trim()} ${cognome.trim()}`,
2025-08-28 18:27:07 +02:00
},
2025-08-29 16:18:32 +02:00
db,
2025-08-28 18:27:07 +02:00
});
if (!user) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore in fase di registrazione",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return { status: "success" as const };
2025-08-04 17:45:44 +02:00
};
export const logIn = async ({
2025-08-28 18:27:07 +02:00
email,
password,
ctx: { req, res },
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
email: string;
password: string;
ctx: Context;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
try {
checkCtx({ req, res });
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
// Get the user from the collection
const user = await findUser_byEmail({ email });
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!user) {
return {
status: "not-found" as const,
message:
"Questo utente non esiste. Devi avere un account per effettuare il login.",
};
2025-08-28 18:27:07 +02:00
}
//Check if user is blocked or unverified
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (user.isBlocked) {
return {
status: "fail" as const,
message: "Errore in Login, non è possibile effettuare il login.",
};
2025-08-28 18:27:07 +02:00
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
// If Admin and password is "changeme", skip password check
const skipPwCheck = user.isAdmin && user.password === "changeme";
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!skipPwCheck) {
const passworIsMatch = await comparePasswords({
2025-08-29 16:18:32 +02:00
hashedPassword: user.password,
2025-08-28 18:27:07 +02:00
password,
salt: user.salt,
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!passworIsMatch) {
return {
status: "fail" as const,
message: "Password errata, riprova.",
};
2025-08-28 18:27:07 +02:00
}
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const payload = {
email: user.email,
2025-08-29 16:18:32 +02:00
id: user.id,
2025-08-28 18:27:07 +02:00
isAdmin: user.isAdmin,
isBlocked: user.isBlocked,
nome: user.nome,
2025-08-29 16:18:32 +02:00
username: user.username,
mustChangePassword: user.mustChangePassword,
2025-08-28 18:27:07 +02:00
};
2025-09-04 11:32:17 +02:00
const accessToken = await signToken(
payload,
TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
2025-09-04 11:32:17 +02:00
"accessToken",
);
const refreshToken = await signToken(
payload,
TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY,
2025-09-04 11:32:17 +02:00
"refreshToken",
);
2025-08-04 17:45:44 +02:00
2025-09-03 21:43:41 +02:00
try {
await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
hours: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
}),
req,
res,
});
await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
days: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY,
}),
req,
res,
});
return { status: "success" as const };
} catch (cookieError) {
console.error("Error setting cookies:", cookieError);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore durante l'impostazione dei cookies: ${(cookieError as Error).message}`,
});
}
2025-08-28 18:27:07 +02:00
} catch (e) {
2025-09-03 21:43:41 +02:00
console.error("Login error:", e);
2025-08-28 18:27:07 +02:00
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore login: ${(e as Error).message}`,
});
}
2025-08-04 17:45:44 +02:00
};
export const swapAuth = async ({
2025-08-28 18:27:07 +02:00
ctx: { req, res },
userId,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
ctx: Context;
userId: UsersId;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
checkCtx({ req, res });
try {
const user = await findUser_byId_MINI({ userId });
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Utente non trovato",
});
}
2025-08-04 17:45:44 +02:00
const accessToken = await signToken(
user,
TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
"accessToken",
);
2025-09-04 11:32:17 +02:00
const refreshToken = await signToken(
user,
TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY,
2025-09-04 11:32:17 +02:00
"refreshToken",
);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
// Send Access Token in Cookie
await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
hours: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
}),
2025-08-28 18:27:07 +02:00
req,
res,
});
await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
days: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY,
}),
2025-08-28 18:27:07 +02:00
req,
res,
});
} catch {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore interno",
});
}
2025-08-04 17:45:44 +02:00
};
export const logOut = async ({ ctx: { req, res } }: { ctx: Context }) => {
2025-08-28 18:27:07 +02:00
checkCtx({ req, res });
try {
await deleteCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, { req, res });
await deleteCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, { req, res });
2025-08-28 18:27:07 +02:00
return { status: "success" };
} catch (err: unknown) {
console.error(err);
throw err;
}
2025-08-04 17:45:44 +02:00
};
export const passwordChange = async ({
2025-08-28 18:27:07 +02:00
userId,
oldpassword,
newpassword,
ctx,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
userId: UsersId;
oldpassword: string;
newpassword: string;
ctx: Context;
2025-08-04 17:45:44 +02:00
}) => {
const { req, res } = ctx;
if (!req || !res) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Request/Response context mancante",
});
}
2025-08-28 18:27:07 +02:00
const dbUser = await findUser_byId({ id: userId });
if (!dbUser) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Utente non trovato",
});
}
2025-08-29 16:18:32 +02:00
const banned = await isBanned({ data: { email: dbUser.email }, db });
2025-08-28 18:27:07 +02:00
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const isPasswordCorrect = await comparePasswords({
2025-08-29 16:18:32 +02:00
hashedPassword: dbUser.password,
2025-08-28 18:27:07 +02:00
password: oldpassword,
salt: dbUser.salt,
});
if (!isPasswordCorrect) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Password errata",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const salt = generateSalt();
const hashedPassword = await hashPassword(newpassword, salt);
await db
.updateTable("users")
.set({
password: hashedPassword,
salt,
mustChangePassword: false,
2025-08-28 18:27:07 +02:00
})
.where("id", "=", userId)
.execute();
// Refresh tokens with updated mustChangePassword value
const payload = {
email: dbUser.email,
id: dbUser.id,
isAdmin: dbUser.isAdmin,
isBlocked: dbUser.isBlocked,
nome: dbUser.nome,
username: dbUser.username,
mustChangePassword: false,
};
const accessToken = await signToken(
payload,
TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
"accessToken",
);
const refreshToken = await signToken(
payload,
TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY,
"refreshToken",
);
await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
hours: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
}),
req,
res,
});
await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
days: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY,
}),
req,
res,
});
2025-08-28 18:27:07 +02:00
return {
status: "success" as const,
2025-08-28 18:27:07 +02:00
};
2025-08-04 17:45:44 +02:00
};