npm package: @gemasa/auth_back
Description: **Autenticación modular y robusta para NestJS.**
Latest version: 1.0.5

# 🔐 @gemasa/auth_back

**Autenticación modular y robusta para NestJS.**

Esta librería proporciona un sistema de autenticación completo y listo para producción utilizando **Prisma**, **Passport** y **JWT**. Soporta **Login Local**, **Google OAuth2** y manejo seguro de sesiones mediante **Cookies HttpOnly**.

Diseñada para ser agnóstica de la base de datos (tú inyectas el `PrismaService`) y fácil de integrar en arquitecturas Monorepo.

---

## 🛠️ Prerrequisitos de Base de Datos

⚠️ **IMPORTANTE:** Esta librería **NO incluye Prisma** por sí misma. Esto es intencional para evitar conflictos de versiones y permitirte tener el control total de tu esquema.

### 1. Actualiza tu `schema.prisma`

Debes agregar el siguiente modelo `User` a tu archivo `schema.prisma`. Puedes agregar más campos si lo necesitas, pero estos son los requeridos por la librería:

```prisma
model User {
  id        String   @id @default(uuid())
  email     String   @unique
  password  String?  // Opcional para usuarios de Google
  name      String?
  googleId  String?  @unique

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

### 2. Genera el Cliente

Una vez actualizado el esquema, no olvides ejecutar:

```bash
npx prisma generate
```

---

## 📦 Instalación

Instala la librería usando tu gestor de paquetes favorito:

### npm

```bash
npm install @gemasa/auth_back
```

### pnpm

```bash
pnpm add @gemasa/auth_back
```

O si estás en un monorepo, asegúrate de que esté en tus dependencias del `package.json`.

---

## ⚙️ Configuración del Backend (NestJS)

### 1. Importar el Módulo

En tu `app.module.ts` (o el módulo donde quieras usar la autenticación), importa `AuthModule.register`.

```typescript
import { Module } from "@nestjs/common";
import { AuthModule } from "@gemasa/auth_back";
import { PrismaService } from "./prisma.service"; // Tu servicio de Prisma existente

@Module({
  imports: [
    AuthModule.register({
      prismaService: PrismaService, // 👈 Inyecta TU clase de servicio
      jwtSecret: process.env.JWT_SECRET || "super_secret",
      jwtExpiresIn: "7d",
      frontendUrl: process.env.FRONTEND_URL || "http://localhost:3000",

      // Configuración de Cookies
      cookieName: "auth_token",
      cookieMaxAge: 7 * 24 * 60 * 60 * 1000, // 7 días en milisegundos
      cookieSecure: process.env.NODE_ENV === "production",
      cookieSameSite: "lax",

      // Opcional: Google OAuth2
      googleClientId: process.env.GOOGLE_CLIENT_ID,
      googleClientSecret: process.env.GOOGLE_CLIENT_SECRET,
      googleCallbackUrl: "http://localhost:4000/auth/google/callback",
    }),
  ],
})
export class AppModule {}
```

### Opciones de Configuración

| Propiedad            | Tipo                          | Requerido | Descripción                                                                                |
| :------------------- | :---------------------------- | :-------: | :----------------------------------------------------------------------------------------- |
| `prismaService`      | `Type<any>`                   |    ✅     | Tu clase `PrismaService`. La librería la instanciará o usará la inyección de dependencias. |
| `jwtSecret`          | `string`                      |    ✅     | Clave secreta para firmar los tokens JWT.                                                  |
| `frontendUrl`        | `string`                      |    ✅     | URL de tu frontend (usada para redirecciones de OAuth y CORS).                             |
| `jwtExpiresIn`       | `string`                      |    ❌     | Tiempo de expiración del token (ej. `'1d'`, `'3600s'`). Default: `'1d'`.                   |
| `cookieName`         | `string`                      |    ❌     | Nombre de la cookie HttpOnly. Default: `'access_token'`.                                   |
| `cookieMaxAge`       | `number`                      |    ❌     | Tiempo de vida de la cookie en milisegundos.                                               |
| `cookieSecure`       | `boolean`                     |    ❌     | Si `true`, la cookie solo se envía por HTTPS.                                              |
| `cookieSameSite`     | `'lax' \| 'strict' \| 'none'` |    ❌     | Política SameSite de la cookie.                                                            |
| `googleClientId`     | `string`                      |    ❌     | ID de cliente de Google Cloud Console.                                                     |
| `googleClientSecret` | `string`                      |    ❌     | Secreto de cliente de Google.                                                              |
| `googleCallbackUrl`  | `string`                      |    ❌     | URL de callback registrada en Google.                                                      |

### 2. Configuración en `main.ts`

Para que las cookies funcionen correctamente entre el frontend y el backend, necesitas configurar **CORS** con `credentials: true` y usar `cookie-parser`.

```typescript
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import * as cookieParser from "cookie-parser";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 1. Habilitar Cookie Parser
  app.use(cookieParser());

  // 2. Configurar CORS
  app.enableCors({
    origin: process.env.FRONTEND_URL || "http://localhost:3000", // Debe coincidir con el origen de tu frontend
    credentials: true, // 👈 Obligatorio para enviar/recibir cookies
  });

  await app.listen(4000);
}
bootstrap();
```

---

## 🛡️ Uso de Guards

Para proteger una ruta y requerir un usuario autenticado, usa el `JwtAuthGuard` exportado por la librería.

```typescript
import { Controller, Get, UseGuards, Request } from "@nestjs/common";
import { JwtAuthGuard } from "@gemasa/auth_back"; // 👈 Importar desde la librería

@Controller("profile")
export class ProfileController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProfile(@Request() req) {
    // req.user estará disponible aquí gracias al Guard
    return req.user;
  }
}
```

---

## 🌐 Notas de Frontend (Next.js)

Para que la integración funcione, ten en cuenta estos puntos en tu aplicación Next.js:

### 1. Middleware

Si usas middleware para proteger rutas en Next.js, asegúrate de leer la **misma cookie** que configuraste en el backend.

```typescript
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  // Debe coincidir con 'cookieName' del backend
  const token = request.cookies.get("auth_token")?.value;

  if (!token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
  return NextResponse.next();
}
```

### 2. Peticiones HTTP

Cuando hagas peticiones al backend (fetch, axios, etc.), debes incluir las credenciales para que la cookie viaje.

```javascript
// Ejemplo con fetch
fetch("http://localhost:4000/profile", {
  method: "GET",
  credentials: "include", // 👈 ¡CRUCIAL!
});
```

---

## 🎨 Estructura de la Interfaz de Usuario

La librería expone automáticamente los siguientes endpoints que espera tu frontend:

### `/auth/register` (POST)

Espera un JSON con:

- `email` (string, email válido)
- `password` (string, min 6 caracteres)
- `name` (string)

### `/auth/login` (POST)

Espera un JSON con:

- `email` (string)
- `password` (string)

---

Hecho con ❤️ por el equipo de **Gemasa**.