diff --git a/backend/src/modules/auth/auth.service.ts b/backend/src/modules/auth/auth.service.ts index b5f5dba..a3e51fb 100644 --- a/backend/src/modules/auth/auth.service.ts +++ b/backend/src/modules/auth/auth.service.ts @@ -69,7 +69,7 @@ export class AuthService { try { await emailService.sendWelcome({ email: user.email, - name: user.displayName ?? user.username, + name: user.displayName ?? user.username ?? "Usuário", }); } catch (error) { logError("Falha ao disparar e-mail de boas-vindas no login social.", { diff --git a/backend/src/modules/auth/connections.controller.ts b/backend/src/modules/auth/connections.controller.ts index df3cd74..f3f536f 100644 --- a/backend/src/modules/auth/connections.controller.ts +++ b/backend/src/modules/auth/connections.controller.ts @@ -18,7 +18,10 @@ export class ConnectionsController { const userId = req.session.userId as string; const provider = req.params.provider; - if (!(SUPPORTED_PROVIDERS as readonly string[]).includes(provider)) { + if ( + typeof provider !== "string" || + !(SUPPORTED_PROVIDERS as readonly string[]).includes(provider) + ) { throw AppError.validation("Provider inválido."); } diff --git a/backend/src/modules/auth/credentials.service.ts b/backend/src/modules/auth/credentials.service.ts index ef50c67..a774280 100644 --- a/backend/src/modules/auth/credentials.service.ts +++ b/backend/src/modules/auth/credentials.service.ts @@ -86,8 +86,8 @@ export class CredentialsService { // E-mail de boas-vindas: falha nunca derruba o registro (EMAIL-08). try { await emailService.sendWelcome({ - email: user.email, - name: user.displayName ?? user.username, + email: normalizedEmail, + name: name?.trim() || normalizedEmail.split("@")[0], }); } catch (error) { logError("Falha ao disparar e-mail de boas-vindas.", { diff --git a/backend/tests/integration/routes/connections.routes.test.ts b/backend/tests/integration/routes/connections.routes.test.ts index 6347ae4..1b9b65e 100644 --- a/backend/tests/integration/routes/connections.routes.test.ts +++ b/backend/tests/integration/routes/connections.routes.test.ts @@ -84,4 +84,20 @@ describe("connections routes", () => { expect(res.body.code).toBe("VALIDATION_ERROR"); expect(mocks.disconnectProvider).not.toHaveBeenCalled(); }); + + it("recusa provider não escalar antes de desconectar", async () => { + const controller = new ConnectionsController(); + + await expect( + controller.disconnect( + { + session: { userId: "user-A" }, + params: { provider: ["google", "github"] }, + } as any, + { json: vi.fn() } as any, + ), + ).rejects.toMatchObject({ code: "VALIDATION_ERROR" }); + + expect(mocks.disconnectProvider).not.toHaveBeenCalled(); + }); }); diff --git a/backend/tests/unit/modules/auth/auth.service.test.ts b/backend/tests/unit/modules/auth/auth.service.test.ts index 9382037..e919c55 100644 --- a/backend/tests/unit/modules/auth/auth.service.test.ts +++ b/backend/tests/unit/modules/auth/auth.service.test.ts @@ -216,5 +216,20 @@ describe("AuthService", () => { profile: mockProfile, }); }); + + it("uses a fallback name when the social profile has no name", async () => { + mocks.exchangeCode.mockResolvedValueOnce(mockProfile); + mocks.findOrCreateUser.mockResolvedValueOnce({ + user: { ...mockUser, displayName: null, username: null }, + isNewUser: true, + }); + + await service.handleCallback(validCallbackParams); + + expect(mocks.sendWelcome).toHaveBeenCalledWith({ + email: mockUser.email, + name: "Usuário", + }); + }); }); }); diff --git a/backend/tests/unit/modules/auth/credentials.service.test.ts b/backend/tests/unit/modules/auth/credentials.service.test.ts index 5ff325f..2583b1f 100644 --- a/backend/tests/unit/modules/auth/credentials.service.test.ts +++ b/backend/tests/unit/modules/auth/credentials.service.test.ts @@ -213,8 +213,8 @@ describe("CredentialsService", () => { await service.register(registerInput); expect(mocks.sendWelcome).toHaveBeenCalledWith({ - email: mockUser.email, - name: mockUser.displayName, + email: registerInput.email, + name: registerInput.name, }); });