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

294 lines
6.7 KiB
TypeScript
Raw Normal View History

2025-08-04 17:45:44 +02:00
import { TRPCError } from "@trpc/server";
import { deleteCookie, getCookies, type OptionsType } from "cookies-next";
import { setCookie } from "cookies-next/server";
import type { Context } from "~/server/api/trpc";
import {
findUser_byId,
findUser_byEmail,
addUser,
findUser_byId_MINI,
} from "~/server/services/user.service";
import {
ACCESS_TOKEN_COOKIE_NAME,
REFRESH_TOKEN_COOKIE_NAME,
signToken,
} from "~/server/auth/jwt";
import { checkCtx } from "~/server/utils/ctxChecker";
import type { UsersId } from "~/schemas/public/Users";
import { db } from "~/server/db";
import { isBanned } from "~/server/services/banlist.service";
import { env } from "~/env.mjs";
import { add } from "date-fns";
import {
comparePasswords,
generateSalt,
hashPassword,
} from "~/server/services/password.service";
// [...] Cookie options
const cookieOptions: OptionsType = {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "strict",
path: "/",
};
const accessTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { hours: 1 }),
};
const refreshTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { days: 1 }),
2025-08-04 17:45:44 +02:00
};
export const createUserAdmin = async ({
email,
password,
nome,
cognome,
telefono,
}: {
email: string;
password: string;
nome: string;
cognome: string;
telefono: string;
}) => {
const banned = await isBanned({ db, data: { email } });
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
const salt = generateSalt();
const hashedPassword = await hashPassword(password, salt);
const user = await addUser({
db,
data: {
email,
password: hashedPassword,
username: `${nome.trim()} ${cognome.trim()}`,
nome: nome.trim(),
cognome: cognome.trim(),
telefono,
isVerified: true,
isAdminMade: true,
salt,
},
});
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) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Utente non trovato",
});
}
//Check if user is blocked or unverified
if (user.isBlocked) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bloccato",
});
}
// If Admin and password is "changeme", skip password check
const skipPwCheck = user.isAdmin && user.password == "changeme";
if (!skipPwCheck) {
const passworIsMatch = await comparePasswords({
password,
salt: user.salt,
hashedPassword: user.password,
});
if (!passworIsMatch) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Invalid email or password",
});
}
}
const payload = {
id: user.id,
username: user.username,
email: user.email,
isAdmin: user.isAdmin,
isBlocked: user.isBlocked,
nome: user.nome,
};
const accessToken = await signToken(payload, "1h", "accessToken");
const refreshToken = await signToken(payload, "1d", "refreshToken");
2025-08-04 17:45:44 +02:00
// 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,
});
const cookies = await getCookies({ req, res });
if (!cookies) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore durante il login, cookies non impostati",
});
}
if (!cookies[ACCESS_TOKEN_COOKIE_NAME]) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore durante il login, access token non impostato",
});
}
if (!cookies[REFRESH_TOKEN_COOKIE_NAME]) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Errore durante il login, refresh token non impostato",
});
}
} catch (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, "1h", "accessToken");
const refreshToken = await signToken(user, "1d", "refreshToken");
2025-08-04 17:45:44 +02:00
// 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({ db, data: { email: dbUser.email } });
if (banned) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Utente bannato",
});
}
const isPasswordCorrect = await comparePasswords({
password: oldpassword,
salt: dbUser.salt,
hashedPassword: dbUser.password,
});
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",
};
};