Skip to main content
Al final de esta guía, tu función rechazará peticiones sin token válido.

Inicio rápido

1

Activa auth en tu función

index.ts
import { define, z } from "@jelou/functions";

export default define({
  description: "API protegida",
  input: z.object({ query: z.string() }),
  auth: true,
  handler: async (input, ctx) => {
    return { results: [], authenticated: ctx.auth?.authenticated };
  },
});
2

Configura el secret

jelou secrets set mi-funcion AUTH_SECRET=mi-token-secreto
Cuando usas auth: true, la plataforma busca un secret llamado AUTH_SECRET automáticamente.
3

Llama con el token

curl -X POST https://mi-funcion.fn.jelou.ai \
  -H "Authorization: Bearer mi-token-secreto" \
  -H "Content-Type: application/json" \
  -d '{"query": "test"}'
Respuesta exitosa:
{ "results": [], "authenticated": true }
Sin token o con token incorrecto:
{ "error": "Unauthorized" }

Variantes de auth

ValorComportamiento
trueBusca el secret AUTH_SECRET. Rechaza con 401 si falta o no coincide.
falseSin autenticación (valor por defecto).
{ secret: "API_TOKEN" }Busca un secret con ese nombre específico.
{ secret: ["API_TOKEN", "API_TOKEN_OLD"] }Acepta cualquiera de los secrets (útil para rotación de llaves).
{ secret: "API_TOKEN", required: false }Auth opcional — nunca retorna 401.

ctx.auth

Resultado de la verificación de autenticación disponible dentro del handler.
CampoTipoDescripción
ctx.authAuthResult | nullnull cuando auth no está activo o es opcional sin token
ctx.auth.authenticatedbooleantrue si el token fue verificado

Comportamiento de la plataforma

EscenarioResultado
Token válidoHandler se ejecuta, ctx.auth = { authenticated: true }
Token inválido o faltante401 Unauthorized + header WWW-Authenticate: Bearer
Secret no configurado en env500 fail-closed (nunca permite acceso si el secret no existe)
Trigger cronAuth se omite — los crons tienen firma criptográfica propia
Token > 4 KBRechazado con 401
Fail-closed: si olvidas configurar el secret con jelou secrets set, la plataforma retorna 500 en lugar de permitir acceso. Esto es intencional — nunca se degrada a “sin auth”.

Rotación de llaves

Acepta el token antiguo y el nuevo simultáneamente durante la migración:
export default define({
  description: "API con rotación de llaves",
  input: z.object({ id: z.string() }),
  auth: { secret: ["API_TOKEN", "API_TOKEN_OLD"] },
  handler: async (input, ctx) => {
    return { id: input.id };
  },
});
jelou secrets set mi-funcion API_TOKEN=nuevo-token-v2
jelou secrets set mi-funcion API_TOKEN_OLD=token-anterior-v1
Una vez que todos los clientes usen el nuevo token, elimina API_TOKEN_OLD del array y del secret.

Auth opcional

Endpoints públicos con acceso premium opcional:
export default define({
  description: "Catálogo con precios premium opcionales",
  input: z.object({ categoria: z.string() }),
  auth: { secret: "API_TOKEN", required: false },
  handler: async (input, ctx) => {
    const productos = await buscarProductos(input.categoria);

    if (ctx.auth?.authenticated) {
      return { productos, precios: await preciosMayorista(productos) };
    }

    return { productos };
  },
});
Con required: false:
  • Token válido → ctx.auth = { authenticated: true }
  • Sin token → ctx.auth = null, handler se ejecuta normalmente
  • Token inválido → ctx.auth = null (no retorna 401)

Auth en app()

Auth global

import { app, define, z } from "@jelou/functions";

export default app({
  config: { auth: { secret: "API_TOKEN" } },
  tools: {
    buscar: define({
      description: "Buscar registros",
      input: z.object({ q: z.string() }),
      handler: async (input) => ({ results: [] }),
    }),
    crear: define({
      description: "Crear registro",
      input: z.object({ nombre: z.string() }),
      handler: async (input) => ({ id: "abc-123" }),
    }),
  },
});
Todos los tools heredan el auth global.

Auth por tool + opt-out

export default app({
  config: { auth: { secret: "API_TOKEN" } },
  tools: {
    healthCheck: define({
      description: "Health check público",
      input: z.object({}),
      auth: false,
      handler: async () => ({ status: "ok" }),
    }),
    admin: define({
      description: "Operación de admin",
      input: z.object({ action: z.string() }),
      auth: { secret: "ADMIN_TOKEN" },
      handler: async (input) => ({ done: true }),
    }),
  },
});
ToolAuth
healthCheckSin auth (auth: false sobrescribe el global)
adminUsa ADMIN_TOKEN en lugar del global
  • Cron bypass: los triggers cron nunca pasan por auth. Si tu función usa isCron, verifica la lógica de negocio internamente.
  • Nunca hardcodees secrets en el código. Usa siempre jelou secrets set.
  • Fail-closed: un secret faltante causa 500, no acceso abierto.

Testing

import { assertEquals } from "jsr:@std/assert";
import {
  createMockAuthContext,
  createMockAuthRequest,
  createMockContext,
  createMockRequest,
} from "@jelou/functions/testing";

Deno.test("handler recibe auth cuando token es válido", async () => {
  const ctx = createMockAuthContext();
  const req = createMockAuthRequest("mi-token-secreto", { query: "test" });

  const result = await fn.handler({ query: "test" }, ctx, req);
  assertEquals(result.authenticated, true);
});

Deno.test("handler sin auth retorna ctx.auth null", async () => {
  const ctx = createMockContext({ auth: null });
  const req = createMockRequest({ query: "test" });

  const result = await fn.handler({ query: "test" }, ctx, req);
  assertEquals(result.authenticated, undefined);
});

createMockAuthContext()

Referencia completa de los mocks de autenticación.