Files
Palmr/apps/server/src/modules/auth/controller.ts
Daniel Luiz Alves 2e56b7e59f feat(auth): enhance client header handling in proxy requests
- Introduced a new utility function `getClientHeaders` to extract real client IP and user agent from requests.
- Updated authentication routes to utilize the new utility for improved header management.
- Refactored `getClientInfo` method in AuthController to support additional proxy headers.
2025-07-10 23:57:49 -03:00

173 lines
5.5 KiB
TypeScript

import { FastifyReply, FastifyRequest } from "fastify";
import { env } from "../../env";
import {
CompleteTwoFactorLoginSchema,
createResetPasswordSchema,
LoginSchema,
RequestPasswordResetSchema,
} from "./dto";
import { AuthService } from "./service";
export class AuthController {
private authService = new AuthService();
private getClientInfo(request: FastifyRequest) {
const realIP = request.headers["x-real-ip"] as string;
const realUserAgent = request.headers["x-user-agent"] as string;
const userAgent = realUserAgent || request.headers["user-agent"] || "";
const ipAddress = realIP || request.ip || request.socket.remoteAddress || "";
return { userAgent, ipAddress };
}
async login(request: FastifyRequest, reply: FastifyReply) {
try {
const input = LoginSchema.parse(request.body);
const { userAgent, ipAddress } = this.getClientInfo(request);
const result = await this.authService.login(input, userAgent, ipAddress);
if ("requiresTwoFactor" in result) {
return reply.send(result);
}
const user = result;
const token = await request.jwtSign({
userId: user.id,
isAdmin: user.isAdmin,
});
reply.setCookie("token", token, {
httpOnly: true,
path: "/",
secure: env.SECURE_SITE === "true" ? true : false,
sameSite: env.SECURE_SITE === "true" ? "lax" : "strict",
});
return reply.send({ user });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async completeTwoFactorLogin(request: FastifyRequest, reply: FastifyReply) {
try {
const input = CompleteTwoFactorLoginSchema.parse(request.body);
const { userAgent, ipAddress } = this.getClientInfo(request);
const user = await this.authService.completeTwoFactorLogin(
input.userId,
input.token,
input.rememberDevice,
userAgent,
ipAddress
);
const token = await request.jwtSign({
userId: user.id,
isAdmin: user.isAdmin,
});
reply.setCookie("token", token, {
httpOnly: true,
path: "/",
secure: env.SECURE_SITE === "true" ? true : false,
sameSite: env.SECURE_SITE === "true" ? "lax" : "strict",
});
return reply.send({ user });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async logout(request: FastifyRequest, reply: FastifyReply) {
reply.clearCookie("token", { path: "/" });
return reply.send({ message: "Logout successful" });
}
async requestPasswordReset(request: FastifyRequest, reply: FastifyReply) {
try {
const { email, origin } = RequestPasswordResetSchema.parse(request.body);
await this.authService.requestPasswordReset(email, origin);
return reply.send({
message: "If an account exists with this email, a password reset link will be sent.",
});
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async resetPassword(request: FastifyRequest, reply: FastifyReply) {
try {
const schema = await createResetPasswordSchema();
const input = schema.parse(request.body);
await this.authService.resetPassword(input.token, input.password);
return reply.send({ message: "Password reset successfully" });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async getCurrentUser(request: FastifyRequest, reply: FastifyReply) {
try {
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({ error: "Unauthorized: a valid token is required to access this resource." });
}
const user = await this.authService.getUserById(userId);
if (!user) {
return reply.status(404).send({ error: "User not found" });
}
return reply.send({ user });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async getTrustedDevices(request: FastifyRequest, reply: FastifyReply) {
try {
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({ error: "Unauthorized: a valid token is required to access this resource." });
}
const devices = await this.authService.getTrustedDevices(userId);
return reply.send({ devices });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async removeTrustedDevice(request: FastifyRequest, reply: FastifyReply) {
try {
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({ error: "Unauthorized: a valid token is required to access this resource." });
}
const { id } = request.params as { id: string };
await this.authService.removeTrustedDevice(userId, id);
return reply.send({ success: true, message: "Trusted device removed successfully" });
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async removeAllTrustedDevices(request: FastifyRequest, reply: FastifyReply) {
try {
const userId = (request as any).user?.userId;
if (!userId) {
return reply.status(401).send({ error: "Unauthorized: a valid token is required to access this resource." });
}
const result = await this.authService.removeAllTrustedDevices(userId);
return reply.send(result);
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
}