Saltar a contenido

Despliegue del frontend

El paso 9 del protocolo, y el mismo para cualquier framework: el frontend y la API se sirven desde el mismo origen.

Frontend y API en el mismo origen

La sesión va en una cookie SameSite=Strict, y el navegador solo la envía si el frontend y la API son el mismo sitio. Que lo sean depende de cómo se despliegan:

Despliegue ¿Mismo sitio? Qué hace falta
Un dominio con rutas (https://app.cliente.com y /api) sí, y mismo origen nada
Una IP con puertos distintos (on-premise) sí, pero distinto origen CORS con los orígenes exactos
Dos Web Apps de Azure (app-x.azurewebsites.net y api-x.azurewebsites.net) no el proxy /api

En Azure, dos Web Apps no son el mismo sitio

azurewebsites.net está en la Public Suffix List, así que para el navegador app-x.azurewebsites.net y api-x.azurewebsites.net son sitios distintos, como dos dominios cualquiera. La cookie de la API sería de terceros, y Safari, Firefox en modo estricto y Chrome en incógnito la bloquean: la sesión fallaría en silencio en parte de los navegadores.

La solución es la misma para todos los casos: el frontend hace de proxy de /api hacia la API. El navegador ve un solo origen.

El proxy

Sirve para vite dev y para vite preview:

// vite.config.ts
import { taiApiProxy } from './src/lib/api/vite'

export default defineConfig({ plugins: [svelte(), taiApiProxy()] })

Reenvía /api/* a la variable de entorno API_URL, que se lee al arrancar el servidor, no al compilar: la misma imagen sirve para dev, pre y prod, y el destino se cambia en la Web App.

API_URL=http://localhost:8000 npm run dev
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit'

export const handle: Handle = async ({ event, resolve }) => {
  if (event.url.pathname.startsWith('/api/')) {
    const destino = new URL(event.url.pathname.slice(4) + event.url.search, process.env.API_URL)
    return fetch(destino, {
      method: event.request.method, headers: event.request.headers,
      body: event.request.body, duplex: 'half',
    } as RequestInit)
  }
  return resolve(event)
}
location /api/ {
    proxy_pass http://api:8000/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Lo que tiene que saber la API

El proxy quita /api de la ruta. Para que la cookie vaya a /api/auth y la documentación enlace bien, la API tiene que saber que se la sirve ahí. En su config.py:

ROOT_PATH = "/api"
ALLOWED_ORIGINS = []      # con el mismo origen no hace falta ninguno

Con la sesión en cookie, la API no arranca con ALLOWED_ORIGINS = ["*"], que es lo que deja tai-api init: con credenciales, el comodín aceptaría peticiones desde cualquier sitio.

tai-api generate lee el config.py (sin ejecutarlo) y avisa de las dos cosas: un ROOT_PATH distinto del base_path del bloque frontend —la sesión que se pierde al recargar— y el comodín en ALLOWED_ORIGINS. Un valor que no es un literal (os.environ[...]) no se puede comprobar al generar, y no se avisa.

En modo Keycloak, además, PUBLIC_URL es la URL de la API tal como la ve el navegador, con su /api: https://app-x.azurewebsites.net/api. Ver Keycloak.

vite preview en producción

Es como se despliegan hoy muchos frontends, y el proxy funciona sobre él, pero la documentación de Vite lo desaconseja como servidor de producción.