# Autenticación

**mTLS en todas partes, sin VPN.** Cada tramo de esta integración, tanto las
llamadas de API como las entregas de webhooks en ambos sentidos, se autentica
mutuamente con certificados de cliente TLS. Encima de eso, las llamadas hacia
Nexa llevan una llave de API del cliente, de modo que el certificado dice
*quién está llamando* y la llave dice *en nombre de quién*.

## De un vistazo

| Tramo | Dirección | Autenticación de transporte | Autenticación de aplicación |
| --- | --- | --- | --- |
| Llamadas de API de Nexa a Tesser | N→T | Certificado de cliente de Nexa, con fijación (pinning) de la huella digital | La identidad se resuelve a partir de la huella digital del certificado |
| Envíos de webhook de Nexa a Tesser | N→T | Certificado de cliente de Nexa | La identidad resuelta debe coincidir con `{organizationId}` en la ruta |
| Llamadas de API de Tesser a Nexa | T→N | Certificado de cliente de Tesser | `Authorization: Bearer <felix_api_key>`, verificado contra el campo `customer` |
| Envíos de webhook de Tesser a Nexa | T→N | Certificado de cliente de Tesser (**compromiso de desarrollo**) | Encabezado Ed25519 `X-Tesser-Signature` |

## Nexa → Tesser (llamadas de API y envíos de webhook)

Esto se monta sobre el diseño mTLS **ya existente** de la plataforma Tesser
para socios bancarios, así que no hay nada nuevo que construir del lado de
Tesser.

1. Durante el onboarding, la huella digital SHA-256 del certificado de
   cliente de Nexa se registra contra la llave de API de Nexa en Tesser
   (`POST /v1/keys/{id}/certificates`).
2. El gateway de Tesser termina la conexión TLS, reenvía la huella digital
   del certificado de cliente y la plataforma resuelve a partir de ella la
   identidad de Nexa.
3. **La rotación es traslapada**: se registra el certificado nuevo, se pasa
   el tráfico y recién entonces se revoca el anterior. No existe ninguna
   ventana en la que Nexa tenga un solo certificado válido registrado.

El [receptor de webhooks](/api-tesser/webhooks#receive-nexa-webhook-op-4) <T />
agrega una verificación adicional: la identidad resuelta a partir del
certificado debe coincidir con el `{organizationId}` de la ruta de la
solicitud. Una discrepancia da `401`. `{organizationId}` es el ID de
organización de Nexa en Tesser, que se comparte durante el onboarding.

Antes de enviar eventos reales, Nexa puede confirmar la conectividad y las
credenciales con
[`HEAD /v1/webhooks/nexa/{organizationId}`](/api-tesser/webhooks#verify-webhook-connectivity-op-4)
<T />, que usa la misma ruta y la misma autenticación, sin cuerpo.

El mismo certificado autentica las llamadas de tesorería de Nexa:
[`GET /v1/accounts/{id}`](/api-tesser/accounts#get-an-account-by-id) <T /> y
[`POST /v1/treasury/withdrawals`](/api-tesser/treasury#create-withdrawal)
<T />.

## Tesser → Nexa (llamadas de API)

Tesser presenta **dos** credenciales en cada llamada a un endpoint alojado
por Nexa:

1. Un **certificado de cliente** que identifica a Tesser como el orquestador.
2. `Authorization: Bearer <felix_api_key>`, la llave de API de Nexa **del
   cliente**, que identifica el contexto del cliente. El campo `customer` del
   cuerpo de la solicitud sirve para verificarla de forma cruzada.

Tener credenciales por cliente significa que incorporar más adelante a una
segunda remesadora no exige rediseñar la autenticación: hace falta una llave
nueva, no un esquema nuevo.

**Recomendación a Nexa para validar el certificado de Tesser:** fijar
(pinning) la huella digital del certificado hoja (el mecanismo que la propia
Tesser utiliza) *o* validarlo contra una CA emisora proporcionada por Tesser.
Cualquiera de las dos opciones es aceptable; la decisión es de Nexa, y ambas
se documentan aquí de forma deliberada para que se pueda decidir en la
reunión técnica ([pregunta abierta 8](/es/open-questions)).

| Enfoque | Qué almacena Nexa | Rotación |
| --- | --- | --- |
| **Fijación (pinning) de la huella del certificado hoja** | La huella digital SHA-256 del certificado hoja de Tesser | Tesser envía la huella digital nueva antes del cambio; Nexa mantiene ambas registradas durante el traslape y luego descarta la anterior |
| **Validación contra la CA emisora** | El certificado de la CA emisora de Tesser | La rotación del certificado hoja no exige ninguna acción de Nexa mientras la CA no cambie |

El intercambio de certificados y el procedimiento de rotación en sí son parte
del **onboarding fuera de banda**, no operaciones de API. Ambas partes deben
acordar el canal de contacto y la ventana de traslape antes de que se mueva
el primer GTQ real.

Operaciones afectadas: [crear cotización](/api-nexa/quotes#create-quote-op-1),
[aceptar cotización](/api-nexa/quotes#accept-quote-op-2),
[reportar depósito](/api-nexa/deposits#report-usdc-deposit-op-3),
[crear pago](/api-nexa/payments#create-payment-op-5),
[consultar cotización](/api-nexa/quotes#get-quote-optional-op-8),
[consultar pago](/api-nexa/payments#get-payment-optional-op-9). Todas
son <N />.

## De Tesser a Nexa (webhooks): compromiso de desarrollo

Hoy el emisor de webhooks de Tesser firma los payloads con Ed25519 (el
encabezado `X-Tesser-Signature`) sobre HTTPS simple y **por ahora no puede
presentar un certificado de cliente**. Tesser lo va a ampliar para que
presente un certificado de cliente en las entregas hacia Nexa, de modo que
los webhooks cumplan el mismo estándar de mTLS que las llamadas de API. El
encabezado de firma Ed25519 se conserva. Ya está construido, y la protección
doble no cuesta nada.

Este trabajo corresponde a la ingeniería de plataforma de Tesser, está en la
ruta crítica del POC y se le da seguimiento como tal. Se necesita antes de
los webhooks en producción; **no** se necesita para el host de stubs ni para
este portal. Afecta las entregas hacia
[`POST /v1/webhooks/tesser`](/api-nexa/webhooks#receive-tesser-webhook-nexa-implements-this)
<N />.

## Comportamiento ante errores

Las fallas de autenticación usan el envoltorio de error compartido, con
`error_code: "UNAUTHENTICATED"` y HTTP `401`, en ambos lados y en todas las
operaciones:

```json
{
  "errors": [
    {
      "error_code": "UNAUTHENTICATED",
      "error_message": "Client certificate fingerprint is not registered."
    }
  ],
  "request_id": "req_01JA8F3K2M7Q"
}
```

Las operaciones incorporadas de la API principal de Tesser
([consultar cuenta](/api-tesser/accounts#get-an-account-by-id),
[crear retiro](/api-tesser/treasury#create-withdrawal)) usan en su lugar los
códigos de error propios de esa API principal, con `api-0004` para un 401.
Esa diferencia es intencional: esos endpoints ya están en producción y su
contrato no se va a redefinir para este POC.

## El host

Un solo entorno por ahora: un único host sirve los tres documentos de API,
separados por prefijo de ruta.

| | |
| --- | --- |
| **API alojada por Nexa** | `https://nexa-poc-main-c82e5ac.zuplo.app/nexa` |
| **API alojada por Tesser** | `https://nexa-poc-main-c82e5ac.zuplo.app/tesser` |

Hoy ese host es un stub sin estado: sin mTLS, sin llaves de API, sin
secuenciación. El modelo de autenticación descrito arriba corresponde a las
implementaciones reales, no al stub. Ver
[Pruebas y stubs](/es/testing-stubs).
