Skip to content

Commit b5081b4

Browse files
authored
Merge branch 'master' into develop
Signed-off-by: Benevanio <benevaniosantos930@gmail.com>
2 parents 246313b + 0fd2a30 commit b5081b4

8 files changed

Lines changed: 108 additions & 105 deletions

File tree

backend/src/app.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import cors from "cors";
2-
import express from "express";
2+
import express, { NextFunction, Request, Response } from "express";
33
import { corsOptions } from "./middleware/cors";
44
import { errorHandler } from "./middleware/errorHandler";
55
import { requireAuth } from "./middleware/requireAuth";
@@ -17,9 +17,15 @@ export function createJobsApiApp() {
1717
app.disable("x-powered-by");
1818
app.use(express.json({ limit: "16kb" }));
1919
app.use(securityHeaders);
20-
app.use(cors(corsOptions));
2120

22-
//Confia no proxy reverso (nginx) para lidar com HTTPS e IPs reais dos clientes
21+
// um wrapper para garantir se o CORS chega com erro 403
22+
app.use((req: Request, res: Response, next: NextFunction) => {
23+
cors(corsOptions)(req, res, (err) => {
24+
if (err) return next(err);
25+
next();
26+
});
27+
});
28+
2329
app.set("trust proxy", 1);
2430

2531
app.use("/api/auth", withSession, authRoutes);

backend/src/lib/session.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { SessionOptions } from "iron-session";
22

3+
const isProd = process.env.NODE_ENV === "production";
4+
35
export const sessionOptions: SessionOptions = {
46
password: process.env.SESSION_SECRET!,
57
cookieName: "vagas_session",
68

79
cookieOptions: {
8-
secure: process.env.NODE_ENV === "production",
10+
secure: isProd,
911
httpOnly: true,
10-
sameSite: "lax",
12+
sameSite: isProd ? "none" : "lax",
1113
},
1214
};

backend/src/middleware/errorHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function errorHandler(
66
res: Response,
77
_next: NextFunction,
88
): void {
9-
if (error.message === "Origin not allowed by CORS") {
9+
if (error.message.startsWith("Origin not allowed by CORS")) {
1010
res.status(403).json({ message: "Origem não permitida." });
1111
return;
1212
}

backend/src/modules/auth/credentials.controller.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ export class CredentialsController {
5353
if (!req.session.userId) {
5454
return res.status(401).json({ error: "Não autenticado" });
5555
}
56-
return res.json({ userId: req.session.userId });
56+
57+
const user = await this.service.findById(req.session.userId);
58+
if (!user) {
59+
await req.session.destroy();
60+
return res.status(401).json({ error: "Não autenticado" });
61+
}
62+
63+
return res.json({ user });
5764
}
5865
}

backend/src/modules/auth/credentials.service.ts

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ import {
1616

1717
const argonOptions = {
1818
type: argon2.argon2id,
19-
memoryCost: 65536, // 64 MB de memória
20-
timeCost: 3, // 3 iterações
21-
parallelism: 4, // Usa 4 threads paralelas
19+
memoryCost: 65536,
20+
timeCost: 3,
21+
parallelism: 4,
2222
};
2323

2424
export class CredentialsService {
25+
async findById(id: string): Promise<User | null> {
26+
const user = await db.query.users.findFirst({
27+
where: eq(users.id, id),
28+
});
29+
return user ?? null;
30+
}
31+
2532
async register(
2633
input: RegisterInput,
2734
): Promise<{ user: User; session: Session }> {
@@ -30,44 +37,25 @@ export class CredentialsService {
3037
const existingCredential = await db.query.credentials.findFirst({
3138
where: eq(credentials.email, email),
3239
});
33-
34-
if (existingCredential) {
35-
throw new Error("Email já cadastrado");
36-
}
40+
if (existingCredential) throw new Error("Email já cadastrado");
3741

3842
const existingUser = await db.query.users.findFirst({
3943
where: eq(users.email, email),
4044
});
41-
42-
if (existingUser) {
43-
throw new Error("Email já cadastrado");
44-
}
45+
if (existingUser) throw new Error("Email já cadastrado");
4546

4647
const passwordHash = await argon2.hash(password, argonOptions);
4748
const username = await generateUsername(name ?? email.split("@")[0], db);
4849

4950
const [user] = await db
5051
.insert(users)
51-
.values({
52-
email,
53-
displayName: name,
54-
username,
55-
emailVerified: false,
56-
})
52+
.values({ email, displayName: name, username, emailVerified: false })
5753
.returning();
5854

59-
await db.insert(credentials).values({
60-
userId: user.id,
61-
email,
62-
passwordHash,
63-
});
64-
55+
await db.insert(credentials).values({ userId: user.id, email, passwordHash });
6556
await db.insert(userPreferences).values({ userId: user.id });
6657

67-
return {
68-
user,
69-
session: { userId: user.id },
70-
};
58+
return { user, session: { userId: user.id } };
7159
}
7260

7361
async login(input: LoginInput): Promise<{ user: User; session: Session }> {
@@ -76,28 +64,16 @@ export class CredentialsService {
7664
const credential = await db.query.credentials.findFirst({
7765
where: eq(credentials.email, email),
7866
});
79-
80-
if (!credential) {
81-
throw new Error("Credenciais inválidas");
82-
}
67+
if (!credential) throw new Error("Credenciais inválidas");
8368

8469
const valid = await argon2.verify(credential.passwordHash, password);
85-
86-
if (!valid) {
87-
throw new Error("Credenciais inválidas");
88-
}
70+
if (!valid) throw new Error("Credenciais inválidas");
8971

9072
const user = await db.query.users.findFirst({
9173
where: eq(users.id, credential.userId),
9274
});
75+
if (!user) throw new Error("Usuário não encontrado");
9376

94-
if (!user) {
95-
throw new Error("Usuário não encontrado");
96-
}
97-
98-
return {
99-
user,
100-
session: { userId: user.id },
101-
};
77+
return { user, session: { userId: user.id } };
10278
}
10379
}

backend/tests/integration/routes/auth.routes.test.ts

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ vi.mock("../../../src/modules/auth/auth.service", () => ({
2121
const mockCredentialsService = vi.hoisted(() => ({
2222
register: vi.fn(),
2323
login: vi.fn(),
24+
findById: vi.fn(), // ← fix: adicionado
2425
}));
2526

2627
vi.mock("../../../src/modules/auth/credentials.service", () => ({
@@ -109,6 +110,8 @@ describe("Integration - Auth Routes", () => {
109110
session: { userId: fixtureUser.id },
110111
});
111112

113+
mockCredentialsService.findById.mockResolvedValue(fixtureUser);
114+
112115
app = createJobsApiApp();
113116
});
114117

@@ -252,10 +255,7 @@ describe("Integration - Auth Routes", () => {
252255
.get(`${BASE}/twitter/callback?code=abc&state=valid-state-abc123`)
253256
.expect(400);
254257

255-
expect(res.body).toHaveProperty(
256-
"error",
257-
"Parâmetros de callback inválidos",
258-
);
258+
expect(res.body).toHaveProperty("error", "Parâmetros de callback inválidos");
259259
});
260260

261261
it("retorna 400 quando code esta ausente (ZodError)", async () => {
@@ -288,10 +288,7 @@ describe("Integration - Auth Routes", () => {
288288

289289
describe("POST /register", () => {
290290
it("cria usuario e retorna 201", async () => {
291-
// CredentialsController usa req.session — injetado pelo withSession mock
292-
vi.mocked(getIronSession).mockResolvedValue(
293-
fixtureCredentialsSession as any,
294-
);
291+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
295292

296293
const res = await request(app)
297294
.post(`${BASE}/register`)
@@ -303,9 +300,7 @@ describe("Integration - Auth Routes", () => {
303300
});
304301

305302
it("chama register com email, password e name", async () => {
306-
vi.mocked(getIronSession).mockResolvedValue(
307-
fixtureCredentialsSession as any,
308-
);
303+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
309304

310305
await request(app).post(`${BASE}/register`).send(registerPayload);
311306

@@ -319,9 +314,7 @@ describe("Integration - Auth Routes", () => {
319314
});
320315

321316
it("retorna 400 para email invalido (ZodError)", async () => {
322-
vi.mocked(getIronSession).mockResolvedValue(
323-
fixtureCredentialsSession as any,
324-
);
317+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
325318

326319
const res = await request(app)
327320
.post(`${BASE}/register`)
@@ -332,9 +325,7 @@ describe("Integration - Auth Routes", () => {
332325
});
333326

334327
it("retorna 400 para senha curta (ZodError)", async () => {
335-
vi.mocked(getIronSession).mockResolvedValue(
336-
fixtureCredentialsSession as any,
337-
);
328+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
338329

339330
await request(app)
340331
.post(`${BASE}/register`)
@@ -343,9 +334,7 @@ describe("Integration - Auth Routes", () => {
343334
});
344335

345336
it("retorna 409 quando email ja esta cadastrado", async () => {
346-
vi.mocked(getIronSession).mockResolvedValue(
347-
fixtureCredentialsSession as any,
348-
);
337+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
349338
mockCredentialsService.register.mockRejectedValueOnce(
350339
new Error("Email já cadastrado"),
351340
);
@@ -359,9 +348,7 @@ describe("Integration - Auth Routes", () => {
359348
});
360349

361350
it("retorna 500 para erro inesperado no register", async () => {
362-
vi.mocked(getIronSession).mockResolvedValue(
363-
fixtureCredentialsSession as any,
364-
);
351+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
365352
mockCredentialsService.register.mockRejectedValueOnce(
366353
new Error("db connection failed"),
367354
);
@@ -377,9 +364,7 @@ describe("Integration - Auth Routes", () => {
377364

378365
describe("POST /login", () => {
379366
it("autentica e retorna 200 com user e session", async () => {
380-
vi.mocked(getIronSession).mockResolvedValue(
381-
fixtureCredentialsSession as any,
382-
);
367+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
383368

384369
const res = await request(app)
385370
.post(`${BASE}/login`)
@@ -391,9 +376,7 @@ describe("Integration - Auth Routes", () => {
391376
});
392377

393378
it("chama login com email e password corretos", async () => {
394-
vi.mocked(getIronSession).mockResolvedValue(
395-
fixtureCredentialsSession as any,
396-
);
379+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
397380

398381
await request(app).post(`${BASE}/login`).send(loginPayload);
399382

@@ -403,9 +386,7 @@ describe("Integration - Auth Routes", () => {
403386
});
404387

405388
it("retorna 400 para email invalido (ZodError)", async () => {
406-
vi.mocked(getIronSession).mockResolvedValue(
407-
fixtureCredentialsSession as any,
408-
);
389+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
409390

410391
await request(app)
411392
.post(`${BASE}/login`)
@@ -414,9 +395,7 @@ describe("Integration - Auth Routes", () => {
414395
});
415396

416397
it("retorna 400 para senha ausente (ZodError)", async () => {
417-
vi.mocked(getIronSession).mockResolvedValue(
418-
fixtureCredentialsSession as any,
419-
);
398+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
420399

421400
await request(app)
422401
.post(`${BASE}/login`)
@@ -425,9 +404,7 @@ describe("Integration - Auth Routes", () => {
425404
});
426405

427406
it("retorna 401 para credenciais invalidas", async () => {
428-
vi.mocked(getIronSession).mockResolvedValue(
429-
fixtureCredentialsSession as any,
430-
);
407+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
431408
mockCredentialsService.login.mockRejectedValueOnce(
432409
new Error("Credenciais inválidas"),
433410
);
@@ -441,9 +418,7 @@ describe("Integration - Auth Routes", () => {
441418
});
442419

443420
it("retorna 500 para erro inesperado no login", async () => {
444-
vi.mocked(getIronSession).mockResolvedValue(
445-
fixtureCredentialsSession as any,
446-
);
421+
vi.mocked(getIronSession).mockResolvedValue(fixtureCredentialsSession as any);
447422
mockCredentialsService.login.mockRejectedValueOnce(
448423
new Error("db timeout"),
449424
);
@@ -486,15 +461,18 @@ describe("Integration - Auth Routes", () => {
486461
// ── GET /me ───────────────────────────────────────────────────────────────
487462

488463
describe("GET /me", () => {
489-
it("retorna userId quando autenticado", async () => {
464+
it("retorna user completo quando autenticado", async () => {
490465
vi.mocked(getIronSession).mockResolvedValue({
491466
userId: "user-1",
492467
save: vi.fn(),
468+
destroy: vi.fn(),
493469
} as any);
494470

495471
const res = await request(app).get(`${BASE}/me`).expect(200);
496472

497-
expect(res.body).toEqual({ userId: "user-1" });
473+
expect(res.body).toHaveProperty("user");
474+
expect(res.body.user).toHaveProperty("id", fixtureUser.id);
475+
expect(res.body.user).toHaveProperty("email", fixtureUser.email);
498476
});
499477

500478
it("retorna 401 quando nao autenticado", async () => {

0 commit comments

Comments
 (0)