mirror of
https://github.com/kyantech/Palmr.git
synced 2025-11-02 04:53:26 +00:00
- 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.
173 lines
5.5 KiB
TypeScript
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 });
|
|
}
|
|
}
|
|
}
|