infoalloggi-monorepo/apps/infoalloggi/src/server/controllers/auth.controller.ts
2025-09-04 11:49:15 +02:00

299 lines
6.4 KiB
TypeScript

import { TRPCError } from "@trpc/server";
import { deleteCookie, type OptionsType, setCookie } from "cookies-next";
import { add } from "date-fns";
import type { UsersId } from "~/schemas/public/Users";
import type { Context } from "~/server/api/trpc";
import {
ACCESS_TOKEN_COOKIE_NAME,
REFRESH_TOKEN_COOKIE_NAME,
signToken,
} from "~/server/auth/jwt";
import { db } from "~/server/db";
import { isBanned } from "~/server/services/banlist.service";
import {
comparePasswords,
generateSalt,
hashPassword,
} from "~/server/services/password.service";
import {
addUser,
findUser_byEmail,
findUser_byId,
findUser_byId_MINI,
} from "~/server/services/user.service";
import { checkCtx } from "~/server/utils/ctxChecker";
// [...] Cookie options
const cookieOptions: OptionsType = {
httpOnly: true,
path: "/",
sameSite: "lax",
//secure: env.NODE_ENV === "production",
};
const accessTokenExpiry = "1m";
export const refreshTokenExpiry = "5m";
const accessTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { minutes: 1 }),
};
const refreshTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { minutes: 5 }),
};
export const createUserAdmin = async ({
email,
password,
nome,
cognome,
telefono,
}: {
email: string;
password: string;
nome: string;
cognome: string;
telefono: string;
}) => {
const banned = await isBanned({ data: { email }, db });
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
const salt = generateSalt();
const hashedPassword = await hashPassword(password, salt);
const user = await addUser({
data: {
cognome: cognome.trim(),
email,
isAdminMade: true,
isVerified: true,
nome: nome.trim(),
password: hashedPassword,
salt,
telefono,
username: `${nome.trim()} ${cognome.trim()}`,
},
db,
});
if (!user) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore in fase di registrazione",
});
}
return { status: "success" as const };
};
export const logIn = async ({
email,
password,
ctx: { req, res },
}: {
email: string;
password: string;
ctx: Context;
}) => {
try {
checkCtx({ req, res });
// Get the user from the collection
const user = await findUser_byEmail({ email });
if (!user) {
console.error("User not found in login for email:", email);
throw new TRPCError({
code: "NOT_FOUND",
message: "Errore in Login: Utente non trovato",
});
}
//Check if user is blocked or unverified
if (user.isBlocked) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Errore in Login: Utente bloccato",
});
}
// If Admin and password is "changeme", skip password check
const skipPwCheck = user.isAdmin && user.password === "changeme";
if (!skipPwCheck) {
const passworIsMatch = await comparePasswords({
hashedPassword: user.password,
password,
salt: user.salt,
});
if (!passworIsMatch) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Errore in Login: Invalid email or password",
});
}
}
const payload = {
email: user.email,
id: user.id,
isAdmin: user.isAdmin,
isBlocked: user.isBlocked,
nome: user.nome,
username: user.username,
};
console.log("User authenticated, generating tokens for:", user.email);
const accessToken = await signToken(
payload,
accessTokenExpiry,
"accessToken",
);
const refreshToken = await signToken(
payload,
refreshTokenExpiry,
"refreshToken",
);
console.log("Generated tokens for user:", user.email);
try {
await setCookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...accessTokenCookieOptions,
req,
res,
});
await setCookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...refreshTokenCookieOptions,
req,
res,
});
console.log("Login successful, cookies set.");
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}`,
});
}
} catch (e) {
console.error("Login error:", e);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Errore login: ${(e as Error).message}`,
});
}
};
export const swapAuth = async ({
ctx: { req, res },
userId,
}: {
ctx: Context;
userId: UsersId;
}) => {
checkCtx({ req, res });
try {
const user = await findUser_byId_MINI({ userId });
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Utente non trovato",
});
}
const accessToken = await signToken(user, accessTokenExpiry, "accessToken");
const refreshToken = await signToken(
user,
refreshTokenExpiry,
"refreshToken",
);
// Send Access Token in Cookie
await setCookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...accessTokenCookieOptions,
req,
res,
});
await setCookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...refreshTokenCookieOptions,
req,
res,
});
} catch {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore interno",
});
}
};
export const logOut = async ({ ctx: { req, res } }: { ctx: Context }) => {
checkCtx({ req, res });
try {
await deleteCookie(ACCESS_TOKEN_COOKIE_NAME, { req, res });
await deleteCookie(REFRESH_TOKEN_COOKIE_NAME, { req, res });
return { status: "success" };
} catch (err: unknown) {
console.error(err);
throw err;
}
};
export const passwordChange = async ({
userId,
oldpassword,
newpassword,
}: {
userId: UsersId;
oldpassword: string;
newpassword: string;
}) => {
const dbUser = await findUser_byId({ id: userId });
if (!dbUser) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Utente non trovato",
});
}
const banned = await isBanned({ data: { email: dbUser.email }, db });
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
const isPasswordCorrect = await comparePasswords({
hashedPassword: dbUser.password,
password: oldpassword,
salt: dbUser.salt,
});
if (!isPasswordCorrect) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Password errata",
});
}
const salt = generateSalt();
const hashedPassword = await hashPassword(newpassword, salt);
await db
.updateTable("users")
.set({
password: hashedPassword,
salt,
})
.where("id", "=", userId)
.execute();
return {
status: "success",
};
};