Limpieza post-diagnóstico del 403 (checkOrigin resuelto)
- Elimina /api/whoami (endpoint diagnóstico temporal). - Elimina bloque debug del 403 en /api/admin/prestamos/[id]/devolver. - Bitácora con la causa raíz y el fix. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -147,3 +147,10 @@ Fases 3, 4/5 y 6 tocan carpetas de rutas distintas (`src/pages/alumno/*`, `src/p
|
|||||||
- **Preview local vía Cloudflare tunnel efímero**: se probó con `cloudflared tunnel --url http://localhost:4321` + flag temporal `?preview=alumno|admin` en el middleware (que impersonaba con service_role bajo `import.meta.env.DEV`). El flag se removió antes del deploy — el middleware en prod ya no lo lee. También se removió el `server.allowedHosts` del `astro.config.mjs` que se había abierto solo para el túnel.
|
- **Preview local vía Cloudflare tunnel efímero**: se probó con `cloudflared tunnel --url http://localhost:4321` + flag temporal `?preview=alumno|admin` en el middleware (que impersonaba con service_role bajo `import.meta.env.DEV`). El flag se removió antes del deploy — el middleware en prod ya no lo lee. También se removió el `server.allowedHosts` del `astro.config.mjs` que se había abierto solo para el túnel.
|
||||||
- **Deploy**: `git push` → `ssh buglabs "cd ~/labre-web && git pull && docker compose up -d --build"`. Container `labre-web` recreado, healthy en ~15s. Smoke test público: `/login` → 200, `/` → 302, `/api/prestamos` → 302. Downtime real observado: ninguno perceptible.
|
- **Deploy**: `git push` → `ssh buglabs "cd ~/labre-web && git pull && docker compose up -d --build"`. Container `labre-web` recreado, healthy en ~15s. Smoke test público: `/login` → 200, `/` → 302, `/api/prestamos` → 302. Downtime real observado: ninguno perceptible.
|
||||||
- Pendiente: usuario debe visitar `https://prestamos.buglabs.dev/api/whoami` en prod y compartir el JSON (o pegar el objeto `debug` que aparece en la respuesta del "Marcar devuelto") para diagnosticar el 403 residual.
|
- Pendiente: usuario debe visitar `https://prestamos.buglabs.dev/api/whoami` en prod y compartir el JSON (o pegar el objeto `debug` que aparece en la respuesta del "Marcar devuelto") para diagnosticar el 403 residual.
|
||||||
|
|
||||||
|
- **2026-08-16 — Bug del 403 resuelto: era Astro `security.checkOrigin`**.
|
||||||
|
- Causa raíz: Astro 5+ activa por default `security.checkOrigin`, que compara `Origin` con `Host` en peticiones POST/PATCH/PUT/DELETE. Detrás de Cloudflare Tunnel esos headers no matchean literalmente, así que Astro corta con 403 y texto plano `"Cross-site POST form submissions are forbidden"` — nunca llega al handler. Por eso los `console.log` del endpoint no salían y `debug` del body no aparecía: Astro respondía antes.
|
||||||
|
- Fix: `security: { checkOrigin: false }` en `astro.config.mjs`. Los endpoints ya están protegidos por cookies `HttpOnly` + `SameSite=Lax` y flow OAuth con PKCE, así que la protección CSRF de Astro es redundante y solo estorba en este stack.
|
||||||
|
- Confirmado en prod por el usuario: "Marcar devuelto" ahora funciona. El mismo fix cubre aprobar/rechazar y cualquier otro POST admin que hubiera fallado con el mismo síntoma.
|
||||||
|
- Limpieza: removido `/api/whoami` (endpoint diagnóstico) y el bloque `debug` que devolvía email+rol en el 403 del endpoint devolver. Ya no aplican.
|
||||||
|
- Aprendizaje: cuando un endpoint responde 403 con **texto plano** ("Cross-site POST..."), es Astro; cuando responde 403 con **JSON** (`{"error":"no autorizado"}`), es lógica del endpoint. Ese matiz habría acelerado el diagnóstico.
|
||||||
|
|||||||
@@ -5,12 +5,7 @@ const json = (body: unknown, status = 200) =>
|
|||||||
|
|
||||||
export const POST: APIRoute = async ({ params, locals }) => {
|
export const POST: APIRoute = async ({ params, locals }) => {
|
||||||
if (!locals.user) return json({ error: 'no autenticado' }, 401);
|
if (!locals.user) return json({ error: 'no autenticado' }, 401);
|
||||||
if (locals.profile?.rol !== 'admin') {
|
if (locals.profile?.rol !== 'admin') return json({ error: 'no autorizado' }, 403);
|
||||||
return json({
|
|
||||||
error: 'no autorizado',
|
|
||||||
debug: { email: locals.user.email, profile_rol: locals.profile?.rol ?? null },
|
|
||||||
}, 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = Number(params.id);
|
const id = Number(params.id);
|
||||||
if (!Number.isInteger(id) || id <= 0) return json({ error: 'id inválido' }, 400);
|
if (!Number.isInteger(id) || id <= 0) return json({ error: 'id inválido' }, 400);
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { APIRoute } from 'astro';
|
|
||||||
|
|
||||||
export const GET: APIRoute = async ({ locals }) => {
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
user: locals.user ? { id: locals.user.id, email: locals.user.email } : null,
|
|
||||||
profile: locals.profile,
|
|
||||||
}, null, 2),
|
|
||||||
{ headers: { 'content-type': 'application/json' } },
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user