Pular para o conteúdo principal
Ao final deste guia, sua função rejeitará requisições sem um token válido.

Quick start

1

Ative a autenticação na sua função

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

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

Configure o secret

jelou secrets set my-function AUTH_SECRET=my-secret-token
Quando você usa auth: true, a plataforma procura automaticamente um secret chamado AUTH_SECRET.
3

Faça a chamada com o token

curl -X POST https://my-function.fn.jelou.ai \
  -H "Authorization: Bearer my-secret-token" \
  -H "Content-Type: application/json" \
  -d '{"query": "test"}'
Resposta com sucesso:
{ "results": [], "authenticated": true }
Sem token ou com token incorreto:
{ "error": "Unauthorized" }

Variantes de auth

ValorComportamento
trueProcura o secret AUTH_SECRET. Rejeita com 401 se ausente ou incorreto.
falseSem autenticação (valor padrão).
{ secret: "API_TOKEN" }Procura um secret com esse nome específico.
{ secret: ["API_TOKEN", "API_TOKEN_OLD"] }Aceita qualquer um dos secrets (útil para rotação de chaves).
{ secret: "API_TOKEN", required: false }Auth opcional — nunca retorna 401.

ctx.auth

Resultado da verificação de autenticação disponível dentro do handler.
CampoTipoDescrição
ctx.authAuthResult | nullnull quando auth não está ativa ou é opcional sem token
ctx.auth.authenticatedbooleantrue se o token foi verificado

Comportamento da plataforma

CenárioResultado
Token válidoHandler executa, ctx.auth = { authenticated: true }
Token inválido ou ausente401 Unauthorized + header WWW-Authenticate: Bearer
Secret não configurado no envFalha com 500 (nunca permite acesso se o secret não existir)
Trigger cronAuth é ignorada — crons têm sua própria assinatura criptográfica
Token > 4 KBRejeitado com 401
Fail-closed: se você esquecer de configurar o secret com jelou secrets set, a plataforma retorna 500 em vez de permitir acesso. Isso é intencional — nunca degrada para “sem auth”.

Rotação de chaves

Aceite o token antigo e o novo simultaneamente durante a migração:
export default define({
  description: "API with key rotation",
  input: z.object({ id: z.string() }),
  auth: { secret: ["API_TOKEN", "API_TOKEN_OLD"] },
  handler: async (input, ctx) => {
    return { id: input.id };
  },
});
jelou secrets set my-function API_TOKEN=new-token-v2
jelou secrets set my-function API_TOKEN_OLD=previous-token-v1
Assim que todos os clientes usarem o novo token, remova API_TOKEN_OLD do array e do secret.

Auth opcional

Endpoints públicos com acesso premium opcional:
export default define({
  description: "Catalog with optional premium pricing",
  input: z.object({ category: z.string() }),
  auth: { secret: "API_TOKEN", required: false },
  handler: async (input, ctx) => {
    const products = await searchProducts(input.category);

    if (ctx.auth?.authenticated) {
      return { products, prices: await wholesalePrices(products) };
    }

    return { products };
  },
});
Com required: false:
  • Token válido → ctx.auth = { authenticated: true }
  • Sem token → ctx.auth = null, handler executa normalmente
  • Token inválido → ctx.auth = null (não retorna 401)

Auth em app()

Auth global

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

export default app({
  config: { auth: { secret: "API_TOKEN" } },
  tools: {
    search: define({
      description: "Search records",
      input: z.object({ q: z.string() }),
      handler: async (input) => ({ results: [] }),
    }),
    create: define({
      description: "Create record",
      input: z.object({ name: z.string() }),
      handler: async (input) => ({ id: "abc-123" }),
    }),
  },
});
Todas as ferramentas herdam a auth global.

Auth por ferramenta + opt-out

export default app({
  config: { auth: { secret: "API_TOKEN" } },
  tools: {
    healthCheck: define({
      description: "Public health check",
      input: z.object({}),
      auth: false,
      handler: async () => ({ status: "ok" }),
    }),
    admin: define({
      description: "Admin operation",
      input: z.object({ action: z.string() }),
      auth: { secret: "ADMIN_TOKEN" },
      handler: async (input) => ({ done: true }),
    }),
  },
});
FerramentaAuth
healthCheckSem auth (auth: false sobrescreve o global)
adminUsa ADMIN_TOKEN em vez do global
  • Bypass de cron: triggers de cron nunca passam pela auth. Se sua função usa isCron, verifique a lógica de negócio internamente.
  • Nunca codifique secrets no código. Sempre use jelou secrets set.
  • Fail-closed: um secret ausente causa 500, não acesso aberto.

Testes

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

Deno.test("handler receives auth when token is valid", async () => {
  const ctx = createMockAuthContext();
  const req = createMockAuthRequest("my-secret-token", { query: "test" });

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

Deno.test("handler without auth returns 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()

Referência completa para mocks de autenticação.