SessionSecurityError
Una mutación no superó la comprobación de Origin o de CSRF de la sesión, con el código INVALID_ORIGIN o INVALID_CSRF; las respuestas de API y RPC la convierten en 403.
import { SessionSecurityError } from "@meekojs/meeko/server";SessionSecurityError señala que una petición que cambia datos no superó una comprobación de la sesión. Su code es:
INVALID_ORIGIN: la cabeceraOriginfalta, esnullo no es el origen de la aplicación ni uno deSessionManager.allowedOrigins;INVALID_CSRF: la petición trae la cookie de sesión, pero no la cabecerax-meeko-csrfcon el token de esa sesión.
La lanzan SessionManager.assertMutationAllowed y, desde 1.2, session.create() en una mutación sin un Origin permitido. API, RPC y errorResponse la convierten en 403 con su código.
Uso#
import { MemorySessionStore, SessionManager, SessionSecurityError } from "@meekojs/meeko/server";
const sessions = new SessionManager({ store: new MemorySessionStore(), secure: false });
export async function guard(request: Request): Promise<Response | undefined> {
const session = await sessions.load(request);
try {
sessions.assertMutationAllowed(request, session);
return undefined;
} catch (error) {
if (error instanceof SessionSecurityError) return Response.json({ code: error.code }, { status: 403 });
throw error;
}
}Tipo#
class SessionSecurityError extends Error {
constructor(code: 'INVALID_ORIGIN' | 'INVALID_CSRF');
}Parámetros#
new SessionSecurityError(code):
| Parámetro | Tipo | Descripción |
|---|---|---|
code |
"INVALID_ORIGIN" | "INVALID_CSRF" |
El motivo; también es el message. |
Valor devuelto#
Un Error con code. No hereda de ApiError, pero errorResponse lo reconoce: 403 {"code":"INVALID_CSRF","status":403,"requestId":"…"}.
Ejemplos#
Una petición de otro origen#
POST /api/profile HTTP/1.1
Host: app.example.com
Origin: https://evil.example
Cookie: __Host-meeko.sid=…Respuesta: 403 {"code":"INVALID_ORIGIN",…}, antes de validar la entrada o ejecutar el handler.
Un script que llama a la API con la cookie#
// Desde el navegador, en la misma aplicación: el cliente api.* ya lo hace.
const token = document.querySelector('meta[name="meeko-csrf-token"]').content;
await fetch("/api/profile", {
method: "PUT",
headers: { "content-type": "application/json", "x-meeko-csrf": token },
body: JSON.stringify({ name: "Ada" }),
});El navegador añade Origin por sí mismo. Tras cada respuesta que rota la sesión, el token nuevo llega en x-meeko-csrf-token.
Aceptar el panel de otro subdominio#
new SessionManager({ store, production, allowedOrigins: ["https://admin.example.com"] });Errores comunes#
Llamar a la API con fetch sin el token#
await fetch("/api/profile", { method: "PUT", body: JSON.stringify({ name: "Ada" }) });Con sesión, la respuesta es 403 INVALID_CSRF. Use el cliente generado (api.profile.put({ body })), que envía el token, o añada x-meeko-csrf.
await api.profile.put({ body: { name: "Ada" } });Un proxy sin MEEKO_PUBLIC_ORIGIN#
Navegador → https://app.example.com → proxy TLS → http://127.0.0.1:4173 (sin MEEKO_PUBLIC_ORIGIN)La aplicación ve su propio origen como http://127.0.0.1:4173; el Origin del navegador (https://app.example.com) no coincide y cada mutación autenticada responde 403 INVALID_ORIGIN.
MEEKO_PUBLIC_ORIGIN=https://app.example.comUn cliente nativo sin Origin#
curl -X POST https://app.example.com/api/auth/login -d '{…}'Desde 1.8, toda mutación sin Origin responde 403 INVALID_ORIGIN, también sin sesión (antes, sólo con origin: "required" o en session.create()). Envíe el origen de la aplicación, o declare origin: "none" en una ruta que no puede recibirlo y verifique otra prueba, como una firma.
curl -X POST https://app.example.com/api/auth/login -H 'origin: https://app.example.com' -H 'content-type: application/json' -d '{…}'Notas#
- Qué protege. Origin y CSRF son la defensa frente a una web ajena que envía formularios o
fetchcon la cookie del usuario. Una petición sin cookie no tiene token, así que desde1.8sólo se comprueba suOrigin, en toda mutación salvo conorigin: "none". - Métodos.
GET,HEADyOPTIONSno se comprueban: no deben cambiar datos. - Socket. Un upgrade con un
Originajeno responde403 INVALID_ORIGINpor HTTP y nunca llega a la sesión. - Respuesta prevista. Un
403no llega aonErrordel runtime.