Files
FEDEO/backend/src/routes/auth/auth-authenticated.ts
florianfederspiel 1dc74947f4
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 32s
Build and Push Docker Images / build-frontend (push) Successful in 1m13s
Fix #104
2026-02-15 12:52:34 +01:00

146 lines
4.7 KiB
TypeScript

import { FastifyInstance } from "fastify"
import bcrypt from "bcrypt"
import { eq } from "drizzle-orm"
import jwt from "jsonwebtoken"
import { secrets } from "../../utils/secrets"
import { authUsers } from "../../../db/schema" // wichtig: Drizzle Schema importieren!
export default async function authRoutesAuthenticated(server: FastifyInstance) {
server.post("/auth/refresh", {
schema: {
tags: ["Auth"],
summary: "Refresh JWT for current authenticated user",
response: {
200: {
type: "object",
properties: {
token: { type: "string" },
},
required: ["token"],
},
401: {
type: "object",
properties: {
error: { type: "string" },
},
required: ["error"],
},
},
},
}, async (req, reply) => {
if (!req.user?.user_id) {
return reply.code(401).send({ error: "Unauthorized" })
}
const token = jwt.sign(
{
user_id: req.user.user_id,
email: req.user.email,
tenant_id: req.user.tenant_id,
},
secrets.JWT_SECRET!,
{ expiresIn: "6h" }
)
reply.setCookie("token", token, {
path: "/",
httpOnly: true,
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
secure: process.env.NODE_ENV === "production",
maxAge: 60 * 60 * 6,
})
return { token }
})
server.post("/auth/password/change", {
schema: {
tags: ["Auth"],
summary: "Change password (after login or forced reset)",
body: {
type: "object",
required: ["old_password", "new_password"],
properties: {
old_password: { type: "string" },
new_password: { type: "string" },
},
},
response: {
200: {
type: "object",
properties: {
success: { type: "boolean" },
},
},
},
},
}, async (req, reply) => {
try {
const { old_password, new_password } = req.body as {
old_password: string
new_password: string
}
const userId = req.user?.user_id
if (!userId) {
//@ts-ignore
return reply.code(401).send({ error: "Unauthorized" })
}
// -----------------------------------------------------
// 1) User laden
// -----------------------------------------------------
const [user] = await server.db
.select({
id: authUsers.id,
passwordHash: authUsers.passwordHash,
mustChangePassword: authUsers.must_change_password
})
.from(authUsers)
.where(eq(authUsers.id, userId))
.limit(1)
if (!user) {
//@ts-ignore
return reply.code(404).send({ error: "User not found" })
}
// -----------------------------------------------------
// 2) Altes PW prüfen
// -----------------------------------------------------
const valid = await bcrypt.compare(old_password, user.passwordHash)
if (!valid) {
//@ts-ignore
return reply.code(401).send({ error: "Old password incorrect" })
}
// -----------------------------------------------------
// 3) Neues PW hashen
// -----------------------------------------------------
const newHash = await bcrypt.hash(new_password, 10)
// -----------------------------------------------------
// 4) Updaten
// -----------------------------------------------------
await server.db
.update(authUsers)
.set({
passwordHash: newHash,
must_change_password: false,
updatedAt: new Date(),
})
.where(eq(authUsers.id, userId))
return { success: true }
} catch (err) {
console.error("POST /auth/password/change ERROR:", err)
//@ts-ignore
return reply.code(500).send({ error: "Internal Server Error" })
}
})
}