Pular para o conteúdo
🧭 CONCEITOS

JWT — o crachá que o usuário carrega.

JSON Web Token é um crachá assinado: contém quem é o usuário, expira em pouco tempo, e o banco confia porque a assinatura bate. Mostra como o SuperDB gerencia access tokens, refresh tokens, expiração e como adicionar custom claims.

Dois tipos de token no SuperDB

O SuperDB usa dois formatos de token para propósitos distintos. É importante entender qual é qual antes de integrar:

TokenFormatoAlgoritmoUsado com
Token de end-user (access token) PASETO v4 — começa com v4.public. Ed25519 (chave por projeto) Auth Service (/auth/v1/*), Admin API (/platform/v1/*)
API keys do projeto (anon / service_role) JWT padrão — xxxxx.yyyyy.zzzzz HS256 PostgREST (api.superdb.com.br), Storage, Realtime
⚠️

Token de end-user (PASETO) não funciona com PostgREST. O PostgREST valida JWT HS256 — não PASETO. Para acessar dados via api.superdb.com.br, use as API keys (anon ou service_role) disponíveis no Dashboard em API Keys.

Token de end-user — PASETO v4

Um token PASETO v4 começa com o prefixo v4.public. e é assinado com Ed25519 (chave pública por projeto). O payload carrega:

PASETO v4 — claims do end-user
v4.public.eyJzdWIiOiI3MTM4...    ← token completo (começa com v4.public.)

// Payload decodificado:
{
  "sub": "7138c8b9-...",            // user id (UUID) — usado pelo PostgREST para lookup
  "email": "felipe@superdb.com.br",
  "role": "member",
  "aud": "proj_acme",              // projeto ao qual o usuário pertence
  "iss": "superdb-auth",
  "iat": "2026-05-19T10:00:00Z",
  "exp": "2026-05-19T11:00:00Z"
}

API keys — JWT HS256

As API keys (anon e service_role) são JWT padrão, assinadas com HS256. Você as copia do Dashboard e usa no header Authorization: Bearer ao chamar api.superdb.com.br:

JWT — anatomia
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9    ← header
.
eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGVyZGIifQ  ← payload
.
xKn2zX4_3wEW...                              ← signature

// Payload decodificado (anon key):
{ "role": "anon", "iss": "superdb" }

// Payload decodificado (service_role key):
{ "role": "service_role", "iss": "superdb" }
⚠️

O payload NÃO é criptografado. Qualquer um que vê o token consegue ler. O que protege é a assinatura: sem o secret, ninguém consegue forjar um token válido. Não bote dados sensíveis no payload (senhas, CPF cru).

Por que importa?

  • Stateless. O servidor não precisa guardar sessão num Redis — basta validar a assinatura. Escala horizontalmente sem coordenar.
  • Auto-descritivo. O payload já tem o que o backend precisa (user_id, role) — uma query a menos no banco.
  • Expira sozinho. Tokens com exp passado são rejeitados automaticamente.
  • Ed25519 para end-users. Algoritmo moderno e rápido — a chave pública do projeto pode ser usada por qualquer serviço para verificar sem ter o secret.

Como funciona no SuperDB

O SuperDB Auth emite dois tokens a cada login:

TokenTTL padrãoPra que serveOnde guardar
Access token (PASETO v4) 1 hora Autenticar no Auth Service e Admin API Memória ou cookie httpOnly
Refresh token 30 dias Trocar por novo access token quando o atual expira Cookie httpOnly (essencial)

Ciclo de vida

  1. User loga → server emite access PASETO v4 (1h) + refresh (30d)
  2. SDK guarda os dois (cookie httpOnly por padrão)
  3. SDK envia access em todo request para o Auth Service
  4. Quando o servidor responder 401, o SDK chama /auth/v1/token?grant_type=refresh_token
  5. Recebe um novo access + novo refresh (refresh token rotation)
  6. Logout → invalida o refresh no servidor (lista de revogados)

Assinatura

Tokens de end-user usam Ed25519 com chave pública por projeto (rotacionável). API keys usam HS256 com o JWT secret global do PostgREST.

Custom claims

Você pode adicionar campos custom ao payload via o JWT hook:

SQL function — JWT hook
CREATE OR REPLACE FUNCTION public.custom_jwt(uid uuid, email text)
RETURNS jsonb LANGUAGE sql STABLE AS $$
  SELECT jsonb_build_object(
    'plan', p.plan_name,
    'team_id', tm.team_id,
    'is_admin', u.is_admin
  )
  FROM auth.users u
  LEFT JOIN public.plans p ON p.user_id = u.id
  LEFT JOIN public.team_members tm ON tm.user_id = u.id
  WHERE u.id = uid;
$$;

-- Registrar como hook
INSERT INTO auth.hooks (hook_name, hook_table_name)
VALUES ('custom_access_token', 'public.custom_jwt');

Aí no SQL você lê com auth.jwt() ->> 'plan':

policy usando custom claim
CREATE POLICY "pro features"
  ON premium_data FOR SELECT
  USING (auth.jwt() ->> 'plan' = 'pro');

Quando usar (e quando não)

Use JWT pra…

  • Identificar usuário no backend — todo handler do SuperDB já faz isso
  • Passar claims pra RLSauth.uid(), plan, team_id
  • Permissões cross-service — mesmo token vale no REST e no Storage

NÃO use JWT pra…

  • Guardar dados sensíveis no payload. Não é encriptado — só assinado
  • Sessões longas sem rotação. Token vazado → atacante tem acesso pelo TTL inteiro. Mantenha access em 1h, não 24h
  • Revogação em tempo real. JWT é "stateless" — pra invalidar antes da expiração, você precisa de blocklist (custo de stateful)

Exemplos práticos

1. Decodar payload no client

browser — ler claims sem libs
function decodeJwt(token) {
  const [, payload] = token.split('.')
  return JSON.parse(atob(payload))
}

const { data: { session } } = await db.auth.getSession()
const claims = decodeJwt(session.access_token)
console.log(claims.email, claims.role, claims.exp)
// felipe@superdb.com.br authenticated 1715000000

2. Ler claim no SQL

SQL
-- Dentro de uma policy ou function
SELECT auth.jwt() ->> 'plan';      -- 'pro'
SELECT auth.jwt() -> 'team_ids';   -- ["t1","t2"]
SELECT auth.uid();                  -- '7138c8b9-...'

3. Forçar refresh manual

SDK
// Se você suspeita que o token está stale (ex: user mudou plan)
const { data, error } = await db.auth.refreshSession()
// data.session tem o novo JWT com os claims atualizados

Armadilhas comuns

  • Confiar em claim sem verificar assinatura. Nunca leia o payload diretamente no backend pra autorizar. Use o SDK (que valida a assinatura) ou o middleware oficial.
  • Guardar em localStorage. Aberto pra XSS — JS de outro origem pode ler. Prefira cookie httpOnly; Secure; SameSite=Lax.
  • TTL muito longo. Access de 24h significa 24h de acesso pro atacante caso vaze. 1h é o sweet spot — refresh cobre UX.
  • Não rotacionar refresh. Refresh token rotation (já default no SuperDB) detecta replay attacks — não desligue.
  • Custom claim com nome reservado. Não use sub, iss, aud, exp, iat, nbf — são da RFC 7519 e podem causar bugs.
  • Payload gigante. Cada request leva o JWT no header — 8KB de claims = 8KB extra por request. Evite arrays grandes; passe ID, faça lookup no banco.
💡

Debug: cole o token em jwt.io pra ver o payload formatado. Ele não verifica a assinatura sem a chave — útil só pra inspeção.

Termos relacionados

Essa página ajudou?