Toda función de IA empieza con el mismo atajo.
El modelo es una API HTTP. El navegador habla HTTP. Hay un botón en una página y lo que necesita está a una petición de distancia, así que ¿para qué hacerlo rebotar antes por tu propio servidor? Un salto menos, una cosa menos que construir, y funciona de inmediato.
Funciona de inmediato. Ese es el problema.
Lo que estás entregando
Cuando el navegador llama al modelo directamente, cuatro cosas salen de tu control a la vez, y sólo la primera es evidente.
La credencial. La clave que autoriza esa llamada está ahora en el cliente. No escondida: dentro. Minificada, en base64, tras un paso de compilación, da igual: cualquiera que abra la pestaña de red la tiene, y puede usarla contra el proveedor directamente, para lo que quiera, hasta que te des cuenta.
El gasto. Todos los topes del capítulo anterior asumían un servidor tomando una decisión. Un navegador que llama a un proveedor toma esas decisiones él mismo, o no las toma. Tu techo, tus carriles, tu presupuesto por cliente: todos evitados.
El prompt. Si el navegador monta la petición, el navegador monta las instrucciones. Todas las reglas del capítulo 3 —la cláusula de alcance, la forma de la negativa, el contrato de salida— pasan a ser una sugerencia que cualquier usuario puede sustituir por la suya.
La identidad. El proveedor sabe que la llamada ocurrió. No tiene ni idea de quién la hizo. No puedes atribuirla, ni limitarla, ni facturarla, ni investigarla, porque la única identidad en juego es la clave que todos comparten.
Las dos últimas son las que se olvidan, y son la razón de que «pues pongo la clave en un proxy» no sea la respuesta completa.
El fallo que nos lo enseñó
Aquí va el fallo real, porque es más interesante que la versión de manual.
La función es edición en línea: un comerciante está en su propio sitio publicado, pincha en un campo y pide una frase mejor. Eso necesitaba una credencial, y la tenía: un token de edición efímero y por sitio, emitido al abrir la sesión de edición.
El token se enviaba en la URL. El navegador llamaba directamente al servicio de IA, con el token en la cadena de consulta, y el servicio de IA lo usaba para decidir si continuar.
Salvo que no podía. Ese servicio no tenía forma de validar ese token, porque no era suyo. La tabla que asocia tokens con sitios vivía en otro servicio. El servicio de IA recibía una cadena opaca, no tenía nada contra lo que comprobarla e hizo lo único que podía: continuar.
Así que la credencial era decorativa. El endpoint estaba, en la práctica, abierto —para cualquiera capaz de construir la petición, con el prompt que quisiera, facturado a nosotros y limitado por nada.
La lección se generaliza mucho más allá de esta base de código:
La autenticación tiene que ocurrir donde están los hechos. Un servicio que no es dueño de la tabla no puede imponer la regla. Darle un token sólo hace que parezca que puede.
Si te llevas una idea de este capítulo, llévate esa. Es la razón de que el arreglo sea una frontera de servicio nueva y no una comprobación más estricta en el sitio de antes.