# Módulo IAM — Autenticación y autorización empresarial (PHP 8.1+ / MySQL)

Módulo sin dependencias (sin Composer) para iniciar sesión, gestionar usuarios, roles y permisos, y proteger
rutas de cualquier sistema (inventario, ventas, logística, RRHH…).

## Qué incluye
Login/logout · registro configurable por el administrador (activar/desactivar, aprobación manual, dominios
permitidos, rol por defecto) · verificación de correo · recuperación de contraseña por correo · usuarios, roles y
permisos (con acciones por lotes) · sesiones con expiración y revocación · perfil y cambio de contraseña ·
bitácora de auditoría · interfaz tipo dashboard (barra lateral colapsable en móvil, indicadores, gráfico de accesos de 7 días y actividad reciente según permisos) con modo oscuro.

## Requisitos
PHP 8.1+ con `pdo_mysql` (opcionales: `mbstring`, `apcu`, `sodium`) · MySQL 8+ o MariaDB 10.4+.

## Instalación (5 pasos)
1. **Copie** la carpeta al servidor. Solo `public/` debe ser accesible desde la web.
2. **Base de datos:** `mysql -u root -p < database/install.sql` y cree un usuario con privilegios mínimos:
   ```sql
   CREATE USER 'iam_app'@'localhost' IDENTIFIED BY 'UNA_CLAVE_LARGA';
   GRANT SELECT, INSERT, UPDATE, DELETE ON iam_auth.* TO 'iam_app'@'localhost';
   ```
3. **Configuración:** `cp .env.example .env` y complete `APP_URL`, `DB_*`, `MAIL_*` y **`APP_PEPPER`**
   (`php -r "echo bin2hex(random_bytes(32));"`). Guarde el pepper: si se pierde o cambia, las contraseñas dejan de ser válidas.
4. **Primer administrador:** `php bin/create-admin.php correo@empresa.com "Nombre Apellido"` (pide la contraseña por consola).
5. **Servidor web** con `public/` como raíz:
   - Apache: ya incluye `public/.htaccess` (requiere `mod_rewrite`).
   - Nginx:
     ```nginx
     root /var/www/auth-module/public;
     location / { try_files $uri /index.php?$query_string; }
     location = /index.php { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/index.php; fastcgi_pass unix:/run/php/php-fpm.sock; }
     ```
   - Prueba local: `php -S 127.0.0.1:8080 -t public` (con `APP_ENV=local`, `COOKIE_SECURE=0`, `MAIL_DRIVER=log`).

**Actualizar una instalación existente:** `php bin/upgrade.php --user=root --pass=` (agrega la cédula a los usuarios; se puede repetir).

**Actualizar una instalación existente** (administración de módulos): `mysql -u root -p iam_auth < database/upgrade-modules.sql` y luego `php bin/sync-modules.php`.

Cron diario recomendado (limpieza): `php /ruta/auth-module/bin/gc.php`

## Integrarlo en otro sistema
Publique `public/` bajo una ruta de su dominio (p. ej. `/auth`, con `APP_BASE_PATH=/auth`) y en cada página o endpoint protegido:
```php
require '/ruta/auth-module/src/bootstrap.php';
use Iam\Auth;

$user = Auth::requirePermission('inventario.ver');   // sin sesión → /login?next=…  ·  sin permiso → 403
if (Auth::can('inventario.ajustar')) { /* mostrar el botón; la acción se vuelve a validar en el servidor */ }
```
Para endpoints JSON/AJAX (`Accept: application/json`) responde 401/403 en JSON. Ver `examples/inventario.php`.

**Permisos de cada módulo nuevo** (idempotente, una sola consulta):
```php
Iam\Core\App::roles()->registerPermissions([['ventas.anular', 'ventas', 'Anular facturas']]);
```
Aparecen al instante en *Roles → Editar*. El rol **Administrador** es superusuario: obtiene automáticamente los permisos futuros.
Los sistemas anfitriones deben compartir el dominio (cookie de sesión) y la misma base de datos.

## Crear un módulo nuevo (inventario, ventas, RRHH…)
Estructura sugerida: `htdocs/acceso/` (este módulo, publicado en `/acceso/public`) y `htdocs/inventario/` (su módulo).
1. **Registrarlo** en **Sistema → Módulos** (permiso `modules.manage`): código, nombre, dirección, ícono, orden y quién lo ve.
   Aparece en "Mis módulos" de la barra lateral y en el panel **solo para los usuarios con permiso**:
   - *cualquier permiso del módulo* (`codigo.*`, recomendado),
   - *todos los usuarios con sesión*, o
   - *un permiso puntual*.
   La pantalla avisa si hay permisos de un módulo que todavía no está registrado.
   Alternativa por archivo: `config/modules.php` (cada módulo **dentro** de `return [ ... ];`) y `php bin/sync-modules.php`, que lo importa a la tabla.
2. **Página protegida con el diseño del panel** (`inventario/index.php`):
   ```php
   require 'C:/xampp/htdocs/acceso/src/bootstrap.php';
   use Iam\{Auth, Layout};
   $user = Layout::begin('Inventario', 'inventario.ver');   // sesión + permiso + cabecera/menú
   // ... HTML del módulo ...
   Layout::end();
   ```
3. **Acciones que cambian datos** (POST): `Auth::requireCsrf(); Auth::requirePermission('inventario.ajustar');` y en el formulario `<?= Auth::csrfField() ?>`. Los botones se ocultan con `Auth::can()`, pero la validación real siempre es la del servidor.
4. **Datos propios:** cree sus tablas en la misma BD con un prefijo (`inv_productos`…), use `App::db()` (consultas preparadas, `insertBatch`, `transaction`) y registre eventos con `App::audit()->log(...)`.
5. **Asignar permisos:** en *Roles → Editar* aparecen agrupados por módulo. Convención de nombres: `modulo.accion`.
6. **API/AJAX:** `Auth::requirePermission()` responde 401/403 en JSON si la petición envía `Accept: application/json`.
Ver `examples/inventario.php` (borre `examples/` en producción).

## Seguridad implementada
- Contraseñas: **Argon2id** (bcrypt de respaldo) sobre HMAC-SHA256 con *pepper*; rehash automático; nunca en texto plano ni expuestas.
- Todo se valida **en el servidor**: sesión, permisos (también en cada acción por lotes) y datos de entrada. Consultas 100 % preparadas; salida escapada; CSP estricta sin scripts ni estilos inline.
- Sesión: cookie `HttpOnly`, `SameSite=Lax`, `Secure`/`__Host-` en HTTPS, modo estricto, ID regenerado al entrar/salir, expiración por inactividad y absoluta, huella del navegador, revocación y revalidación contra la BD.
- **CSRF** en todo POST. Límite de intentos por correo e IP (login, registro, recuperación, cambio de clave).
- **Sin enumeración de correos:** login, registro y recuperación responden igual exista o no la cuenta; el trabajo dependiente del usuario (y el correo) se ejecuta *después* de responder; se gasta el mismo tiempo de hash.
- Tokens de 256 bits, un solo uso, solo se guarda su SHA-256; el enlace de recuperación se mueve a la sesión y no queda en la URL ni en el HTML. Al cambiar/restablecer la contraseña se cierran las demás sesiones.
- Protección anti-bloqueo: siempre debe existir un administrador activo; nadie puede bloquearse a sí mismo; redirección `next` limitada a rutas locales; la IP solo se toma de `X-Forwarded-For` desde `TRUSTED_PROXIES`.

## Rendimiento
Una petición autenticada típica hace **0 consultas** de seguridad: usuario y permisos vienen de la sesión y de la caché (APCu o archivos, solo datos no sensibles); la sesión se revalida en BD cada `SESSION_REVALIDATE_SECONDS` (60 s). Los listados no tienen N+1 y las acciones masivas (activar, bloquear, asignar roles, guardar permisos, configuración) usan una transacción y `INSERT`/`UPDATE` agrupados.

## Lista para producción
`APP_ENV=production` · HTTPS con `COOKIE_SECURE=auto` · `APP_PEPPER` de 64 hex · `MAIL_DRIVER=smtp` (el driver `log` guarda enlaces con token: solo desarrollo) · `.env` y `storage/` fuera del alcance web · usuario de BD con privilegios mínimos · copias de seguridad de la BD y del pepper.
Con varios servidores, use `CACHE_DRIVER=file` sobre almacenamiento compartido (APCu es local a cada servidor; los cambios de permisos tardan como máximo 5 min en verse en los otros nodos).

## Estructura
```
config/    configuración (lee .env)        database/  install.sql (tablas + datos iniciales)
public/    index.php, assets/ (única carpeta web)     bin/  create-admin.php, gc.php
src/Core/ infraestructura · src/Repositories/ acceso a datos · src/Services/ reglas de negocio
src/Http/ router + controladores · src/Auth.php fachada pública · views/ plantillas · examples/ integración
```

## Fuera de alcance (extensiones naturales)
Doble factor (TOTP), cambio de correo con reconfirmación, SSO/LDAP y límites de sesiones concurrentes.

## Configurar y probar el correo
En `.env`: `MAIL_DRIVER=smtp`, `MAIL_HOST`, `MAIL_PORT`, `MAIL_ENCRYPTION` (`tls` = puerto 587, `ssl` = 465), `MAIL_USER`, `MAIL_PASS`, `MAIL_FROM`.
Con `MAIL_DRIVER=log` **no se envía nada** (solo desarrollo). Pruebe con `php bin/test-mail.php destino@empresa.com`; los fallos quedan en `storage/logs/error.log`.
Gmail: `smtp.gmail.com`, 587, `tls`, con una *contraseña de aplicación* (requiere verificación en dos pasos). Microsoft 365: `smtp.office365.com`, 587, `tls`.
En XAMPP/Windows, si falla el certificado TLS, configure `openssl.cafile` en `php.ini` o, solo en local, `MAIL_VERIFY_TLS=0`.

## Si no puede iniciar sesión
- `php bin/diagnose.php correo@empresa.com` revisa .env, pepper, base de datos, carpetas, sesión PHP, estado del usuario, bloqueos y comprueba la contraseña (`--unlock` quita los bloqueos).
- `php bin/set-password.php correo@empresa.com` define una nueva contraseña (necesario si cambió `APP_PEPPER` después de crear los usuarios).
- Los errores del sistema quedan en `storage/logs/error.log`.


## Auditoría
Sistema → Auditoría: filtros por fechas, módulo, usuario y texto, con paginación. **Exportar a Excel** descarga un CSV (UTF-8, separador «;», fecha en hora local y detalle completo) con todos los eventos que coinciden con los filtros. Requiere el permiso `audit.view`; cada exportación queda registrada (`audit.exported`).
