# 🎤 Guía de Exposición Técnica: Integración de Llave MX en Convocatorias FOCINE

Esta guía está diseñada como un **guion de presentación y apoyo técnico** para exponer a otros desarrolladores cómo se implementó la autenticación con **Llave MX (Gobierno de México)** en la convocatoria de *Acervos*, y cómo este modelo debe ser replicado o mantenido en el resto de los sistemas de IMCINE.

---

## 📋 Ficha de la Exposición

- **Tema:** Implementación de Llave MX (OAuth 2.0) en Convocatorias FOCINE.
- **Audiencia:** Equipo de Desarrollo y Mantenimiento Web (Back-end, Front-end, Administradores de BD).
- **Objetivo:** Comprender la arquitectura de autenticación, el ciclo de vida del usuario (nuevo vs. recurrente), el manejo de sesiones y el checklist de replicación.
- **Duración sugerida:** 25 a 35 minutos + sesión de preguntas.

---

## ⏱️ Estructura de la Presentación (Agenda)

```
1. 💡 El Problema y la Solución (3 min)
2. 🔄 Arquitectura y Flujo OAuth 2.0 (7 min)
3. 👥 Los Dos Caminos de Negocio: Nuevo vs. Existente (8 min)
4. 📂 Anatomía del Código y Archivos Clave (7 min)
5. 🛡️ Seguridad, Auditoría y Casos Borde (5 min)
6. ❓ Preguntas Frecuentes Técnicas (5 min)
```

---

## 1. 💡 El Problema y la Solución

### 🗣️ Qué decir al equipo:
> *"Anteriormente, cada postulante llenaba un formulario de registro tradicional, creaba una contraseña propia y nosotros teníamos que validar manualmente la veracidad de su CURP o personalidad fiscal. Esto generaba cuentas duplicadas, errores de captura y no cumplía con los nuevos lineamientos de identidad digital única del Gobierno Federal.*
> 
> *Con **Llave MX**, delegamos la autenticación al proveedor de identidad oficial (IdP). Obtenemos la **CURP ya verificada ante RENAPO**, datos oficiales del contribuyente y perfiles morales vinculados, todo bajo el protocolo estándar de la industria: **OAuth 2.0**."*

### 📌 Puntos clave a destacar:
- **Cero contraseñas locales:** No almacenamos hashes de contraseñas de postulantes que usen Llave MX.
- **Identificador Universal:** La **CURP** es la clave primaria lógica para ubicar al postulante en el sistema.
- **Experiencia de usuario unificada:** El postulante usa las mismas credenciales que en cualquier trámite federal.

---

## 2. 🔄 Arquitectura y Flujo OAuth 2.0

### 🗣️ Qué decir al equipo:
> *"Implementamos el flujo **Authorization Code Grant** de OAuth 2.0. El usuario nunca le da sus credenciales a nuestro servidor; es redirigido a la plataforma oficial de Llave MX, y nosotros recibimos un código temporal firmado que luego intercambiamos de servidor a servidor por un Access Token."*

### 📊 Diagrama de Flujo de la Exposición:

```mermaid
flowchart TD
    Inicio(["🧑 1. Postulante entra a acceso.php"]) --> ClicLogin["2. Da clic en 'Iniciar sesión con Llave MX'"]
    ClicLogin --> RedirLMX["3. Redirección al Portal Oficial Llave MX<br><i>(client_id, redirect_uri, state CSRF)</i>"]
    RedirLMX --> LoginLMX["🏛️ 4. Postulante se autentica en Llave MX<br>y autoriza permisos a la plataforma"]
    
    LoginLMX --> RetornoCallback["⚡ 5. Retorno automático a llavemx_callback.php<br><i>Recibe ?code=XXXXX&state=YYYYY</i>"]
    
    RetornoCallback --> IntercambioToken["⚙️ 6. control_llavemx.php:<br>Canjea Code por Access Token de servidor a servidor<br><i>POST /ws/rest/oauth/obtenerToken (Basic Auth)</i>"]
    
    IntercambioToken --> ConsultaAPIs["🌐 7. Consulta datos oficiales del ciudadano:<br>• <i>GET /ws/rest/oauth/datosUsuario (CURP, Nombre, RFC)</i><br>• <i>GET /ws/rest/oauth/perfil/moral (Empresas asociadas)</i>"]
    
    ConsultaAPIs --> EvaluaExistencia{"🗄️ 8. ¿La CURP ya existe<br>en la tabla `proyectos`?"}

    %% RAMA: USUARIO EXISTENTE (REINGRESO)
    EvaluaExistencia -- "SÍ (Usuario Registrado)" --> EvaluaEstatus{"¿Convocatoria abierta y<br>acceso_permitido == 1?"}
    
    EvaluaEstatus -- "NO (Bloqueado/Cancelado)" --> AccesoDenegado["🚫 Redirige a acceso_denegado.php<br><i>(Mensaje de bloqueo + cuenta regresiva 10s)</i>"]
    
    EvaluaEstatus -- "SÍ" --> ActualizaDatos["Actualiza datos oficiales en base de datos<br><i>(NO se envía correo duplicado de bienvenida)</i>"]
    ActualizaDatos --> IniciaSesion["Inicia Sesión PHP estándar<br><i>($_SESSION['usu'], $_SESSION['ses'], etc.)</i>"]
    IniciaSesion --> RedirDatos(["🎉 Redirige a datos.php o conclusion.php"])

    %% RAMA: USUARIO NUEVO (PRIMER INGRESO)
    EvaluaExistencia -- "NO (Primer Ingreso)" --> SesionTemp["Guarda datos en sesión temporal:<br><i>$_SESSION['registro_pendiente_llavemx']</i><br>⚠️ <b>NO se crea registro en BD todavía</b>"]
    
    SesionTemp --> PantallaConsentimiento["📋 Redirige a confirmar_registro.php<br><i>(Muestra CURP/Nombre bloqueados y selector fiscal)</i>"]
    
    PantallaConsentimiento --> DecisionCiudadano{"¿El postulante confirma datos<br>y otorga su consentimiento?"}
    
    DecisionCiudadano -- "❌ CANCELAR / NO ACEPTAR" --> CancelaRegistro["Limpia variables de sesión y audita cancelación<br><i>(CERO registros creados en la base de datos)</i>"]
    CancelaRegistro --> FinCancela(["Vuelve a acceso.php con aviso"])

    DecisionCiudadano -- "✅ CONFIRMAR Y ACEPTAR" --> CreaProyecto["💾 Inserta formalmente el proyecto en BD:<br>1. Guarda personalidad fiscal (Física / Moral)<br>2. Crea carpetas y filas en archivos_proyecto<br>3. Despacha correo oficial de bienvenida con folios"]
    CreaProyecto --> IniciaSesion
```

### 🔑 Detalle de endpoints de Llave MX:
| Operación | Método | Endpoint (Sandbox / Producción) | Autenticación Requerida |
| :--- | :---: | :--- | :--- |
| **Login Web** | `GET` | `/oauth.xhtml` | Parámetros GET (`client_id`, `redirect_uri`, etc.) |
| **Canje de Código** | `POST` | `/ws/rest/oauth/obtenerToken` | **HTTP Basic Auth** (`USER:PASS`) + Body JSON |
| **Datos Ciudadano** | `GET` | `/ws/rest/oauth/datosUsuario` | Basic Auth + Header `accessToken: <TOKEN>` |
| **Perfil Moral** | `GET` | `/ws/rest/oauth/perfil/moral` | Basic Auth + Header `accessToken: <TOKEN>` |

---

## 3. 👥 Los Dos Caminos de Negocio: Nuevo vs. Existente

### 🗣️ Qué decir al equipo:
> *"Uno de los retos más importantes de diseño fue: ¿Qué pasa cuando alguien entra por primera vez? **No debemos crear un proyecto en automático en la base de datos**. Si creáramos el registro inmediatamente, generaríamos folios basura si el usuario sólo entró por curiosidad. Además, por ley de protección de datos, el usuario debe autorizar expresamente participar en esta convocatoria y definir si participa como Persona Física o Persona Moral."*

---

### Camino 1: Usuario Nuevo (Primer Ingreso)

```
[ Llave MX API ] ──▶ Guarda datos en $_SESSION['registro_llavemx']
                                │
                                ▼
                   [ confirmar_registro.php ]
                                │
          ┌─────────────────────┴─────────────────────┐
          ▼                                           ▼
[ Elige Persona Física ]                    [ Elige Persona Moral ]
- CURP bloqueada (RENAPO)                   - CURP representante
- RFC 13 dígitos                            - RFC 12 dígitos
                                            - Razón Social obligatoria
          │                                           │
          └─────────────────────┬─────────────────────┘
                                ▼
             ☑️ Checkbox Consentimiento Obligatorio
                                │
            ┌───────────────────┴───────────────────┐
            ▼                                       ▼
    [ Botón CONFIRMAR ]                     [ Botón CANCELAR ]
            │                                       │
            ▼                                       ▼
- Inserta en `proyectos`               - Limpia variables de sesión
- Inicializa `archivos_proyecto`       - Audita rechazo voluntario
- Envía correo de bienvenida oficial   - Redirige a acceso.php
- Abre sesión y va a datos.php         - CERO registros en BD
```

---

### Camino 2: Usuario Existente (Reingreso)

```
[ Llave MX API ] ──▶ Búsqueda en BD por CURP
                                │
                                ▼
                       ¿Existe el proyecto?
                                │
             ┌──────────────────┴──────────────────┐
             ▼                                     ▼
     [ Convocatoria CERRADA ]              [ Convocatoria ABIERTA ]
             │                                     │
   ¿acceso_permitido == 1?                ¿Estatus == 98 (Cancelado)?
     ├── NO ──▶ acceso_denegado.php         ├── SI ──▶ acceso_denegado.php
     └── SI ──▶ Permite entrar a consulta   └── NO ──▶ Actualiza datos desde Llave MX
                                                       - NO reenvía correo de bienvenida
                                                       - Genera sesión PHP estándar
                                                       - Redirige a datos.php / conclusion.php
```

---

## 4. 📂 Anatomía del Código y Archivos Clave

### 🗣️ Qué decir al equipo:
> *"La solución se diseñó en capas limpias y modulares para que sea fácil de mantener y migrar a otras convocatorias sin alterar la lógica de negocio previa."*

```
acervos/
├── backEnd/
│   ├── variables_globales.php         <-- Configuración y credenciales OAuth2 / Basic Auth
│   ├── CONTROL/
│   │   ├── control_llavemx.php        <-- ORQUESTADOR: cURL, tokens, perfiles, auditoría
│   │   └── control_sistema.php       <-- Métodos: crearProyectoDesdeConsentimiento(), etc.
│   ├── DAO/
│   │   └── dao_sistema.php           <-- Consultas: buscarProyectoPorCurp(), registrarProyecto...
│   └── init_session.php              <-- Intercepta ?code= y blinda postulantes bloqueados
├── acceso.php                         <-- Vista login con diseño gob.mx y bloqueo si está cerrada
├── llavemx_callback.php               <-- Receptor del Redirect URI oficial
├── confirmar_registro.php             <-- Vista institucional de consentimiento
├── modal_datos_fiscales.php           <-- Componente selector Física/Moral/Institución
├── acceso_denegado.php                <-- Pantalla con cuenta regresiva de 10s ante bloqueos
└── logs/
    └── llavemx_audit.log              <-- Bitácora de auditoría detallada en tiempo real
```

### Métodos del Controlador a Explicar en Código:
1. `getUrlLogin()`: Construye el link seguro de entrada con `state` anti-CSRF.
2. `obtenerToken($code)`: Intercambia el código temporal por el `accessToken` mediante cURL con Basic Auth.
3. `obtenerDatosUsuario($token)` y `obtenerPerfilMoral($token)`: Obtienen CURP, nombres y personas morales.
4. `procesarAutenticacion($code)`: Función central que determina si el usuario va a confirmación o a sesión directa.
5. `auditarTransaccion($accion, $datos)`: Registra cada paso enmascarando secrets y credenciales.

---

## 5. 🛡️ Seguridad, Auditoría y Casos Borde

### 🗣️ Qué decir al equipo:
> *"En sistemas de gobierno, la trazabilidad y la seguridad son obligatorias. Implementamos tres capas de protección:"*

1. **Protección CSRF con `state` dinámico:**
   - Previene ataques de inyección de sesión verificando que el código que regresa pertenezca al usuario que inició la petición.
2. **Enmascaramiento en Bitácora (`llavemx_audit.log`):**
   - Las contraseñas, secretos de cliente y tokens se registran automáticamente con `***` para evitar fugas en logs.
3. **CURP Inmutable y Validación Cruzada:**
   - La CURP devuelta por RENAPO/Llave MX se bloquea en modo `readonly`. El postulante no puede editarla en el formulario para evitar que postule a nombre de un tercero.
4. **Control de Acceso Fino (`acceso_permitido` y Estatus `98`):**
   - Si un usuario fue inhabilitado administrativamente o canceló su participación, el sistema no le permite iniciar sesión aunque su cuenta de Llave MX sea válida.

---

## 6. ❓ Preguntas Frecuentes Técnicas (FAQ)

### P1: ¿Qué pasa si el usuario pierde conexión durante la pantalla de consentimiento?
**R:** Como los datos sólo estaban en `$_SESSION['registro_pendiente_llavemx']` y no en BD, no queda ningún registro huérfano. Al volver a entrar e identificarse con Llave MX, se le presentará nuevamente la pantalla de confirmación sin inconsistencias.

### P2: ¿Por qué usamos Basic Auth además del Client Secret?
**R:** Es el requerimiento específico de la infraestructura de API de Llave MX (INFOTEC / ATDT). El endpoint requiere autenticación básica a nivel transporte (`CURLOPT_USERPWD`) y los parámetros del cliente en el cuerpo JSON del POST.

### P3: ¿Cómo cambiamos de Sandbox (pruebas) a Producción?
**R:** En `backEnd/variables_globales.php`, sólo se cambia una constante:
```php
const LLAVEMX_AMBIENTE = "produccion"; // o "sandbox"
```
El controlador [control_llavemx.php](file:///home/convocatoriasdev/public_html/focine/acervos/backEnd/CONTROL/control_llavemx.php) conmuta automáticamente las URLs de login y las APIs correspondientes.

### P4: ¿El usuario puede cambiar su personalidad fiscal después?
**R:** Sí, el modal de datos fiscales queda disponible en su expediente para actualización si la convocatoria lo permite, pero la CURP vinculada a su cuenta nunca cambia.

---

## 7. 🚀 Checklist de Replicación para Otra Convocatoria

Para integrar Llave MX en una nueva convocatoria (ej. `documental`, `ficcion`, etc.):

- [ ] **1. Copiar archivos base:**
  - `llavemx_callback.php` en la raíz.
  - `confirmar_registro.php` y `modal_datos_fiscales.php` en la raíz.
  - `acceso_denegado.php` en la raíz.
  - `backEnd/CONTROL/control_llavemx.php`.
- [ ] **2. Configurar `variables_globales.php`:**
  - Colocar las constantes de Llave MX y ajustar `LLAVEMX_REDIRECT_URI` con el nombre de la nueva carpeta.
- [ ] **3. Incorporar métodos DAO en `dao_sistema.php`:**
  - `buscarProyectoPorCurp()`, `registrarProyectoDesdeLlaveMX()` y `actualizarDatosDesdeLlaveMX()`.
- [ ] **4. Incorporar métodos en `control_sistema.php`:**
  - `crearProyectoDesdeConsentimiento()` y `cancelarRegistroPendiente()`.
- [ ] **5. Actualizar `acceso.php` e `init_session.php`:**
  - Colocar el botón institucional de Llave MX e interceptar `$_GET['code']`.
- [ ] **6. Incluir estilos y crear carpeta de logs:**
  - Vincular `/api_imcine_v2/css_base/llaveMX.css`.
  - Crear carpeta `logs/` con permisos de escritura para `llavemx_audit.log`.

---

> 📖 **Documento de Referencia Técnica Completa:**  
> Para consultar el código fuente exacto y los snippets completos, revisar [GUIA_INTEGRACION_LLAVEMX.md](file:///home/convocatoriasdev/public_html/focine/acervos/GUIA_INTEGRACION_LLAVEMX.md) en el repositorio.
