Skip to content

Autenticacao

A API usa JWT Bearer tokens emitidos pelo Supabase Auth. Toda requisicao (exceto /health e o webhook ASAAS) deve incluir o header Authorization.


Obtendo o token

Faca um POST diretamente na API do Supabase com email e senha:

POST https://<PROJECT>.supabase.co/auth/v1/token?grant_type=password
import requests

SUPABASE_URL = "https://<PROJECT>.supabase.co"
SUPABASE_ANON_KEY = "<ANON_KEY>"

def get_token(email: str, password: str) -> str:
    resp = requests.post(
        f"{SUPABASE_URL}/auth/v1/token?grant_type=password",
        headers={
            "apikey": SUPABASE_ANON_KEY,
            "Content-Type": "application/json",
        },
        json={"email": email, "password": password},
    )
    resp.raise_for_status()
    data = resp.json()
    return data["access_token"], data["refresh_token"]

access_token, refresh_token = get_token("admin@empresa.com", "senha123")
const SUPABASE_URL = "https://<PROJECT>.supabase.co";
const SUPABASE_ANON_KEY = "<ANON_KEY>";

async function getToken(email, password) {
  const resp = await fetch(
    `${SUPABASE_URL}/auth/v1/token?grant_type=password`,
    {
      method: "POST",
      headers: {
        apikey: SUPABASE_ANON_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email, password }),
    }
  );
  if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`);
  const { access_token, refresh_token } = await resp.json();
  return { access_token, refresh_token };
}
SUPABASE_URL="https://<PROJECT>.supabase.co"
SUPABASE_ANON_KEY="<ANON_KEY>"

AUTH_RESP=$(curl -s -X POST \
  "${SUPABASE_URL}/auth/v1/token?grant_type=password" \
  -H "apikey: ${SUPABASE_ANON_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@empresa.com","password":"senha123"}')

TOKEN=$(echo "$AUTH_RESP" | jq -r '.access_token')
REFRESH_TOKEN=$(echo "$AUTH_RESP" | jq -r '.refresh_token')
require 'net/http'
require 'json'

SUPABASE_URL = "https://<PROJECT>.supabase.co"
SUPABASE_ANON_KEY = "<ANON_KEY>"

def get_token(email, password)
  uri = URI("#{SUPABASE_URL}/auth/v1/token?grant_type=password")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  req = Net::HTTP::Post.new(uri)
  req['apikey'] = SUPABASE_ANON_KEY
  req['Content-Type'] = 'application/json'
  req.body = JSON.dump(email: email, password: password)

  resp = http.request(req)
  raise "Auth failed: #{resp.code}" unless resp.is_a?(Net::HTTPSuccess)

  data = JSON.parse(resp.body)
  [data['access_token'], data['refresh_token']]
end

access_token, refresh_token = get_token('admin@empresa.com', 'senha123')

Usando o token nas requisicoes

Inclua o token no header Authorization em todas as requisicoes autenticadas:

Authorization: Bearer <access_token>
BASE_URL = "https://fast-deliv-backend.vercel.app"

headers = {"Authorization": f"Bearer {access_token}"}
resp = requests.get(f"{BASE_URL}/api/v1/deliveries", headers=headers)
const BASE_URL = "https://fast-deliv-backend.vercel.app";

const headers = { Authorization: `Bearer ${access_token}` };
const resp = await fetch(`${BASE_URL}/api/v1/deliveries`, { headers });
curl -s "${BASE_URL}/api/v1/deliveries" \
  -H "Authorization: Bearer ${TOKEN}"
BASE_URL = "https://fast-deliv-backend.vercel.app"

req = Net::HTTP::Get.new(URI("#{BASE_URL}/api/v1/deliveries"))
req['Authorization'] = "Bearer #{access_token}"

Expiracao e refresh

O token expira em 1 hora. Use o refresh_token para obter um novo access_token sem reautenticar:

POST https://<PROJECT>.supabase.co/auth/v1/token?grant_type=refresh_token
def refresh_access_token(refresh_token: str) -> str:
    resp = requests.post(
        f"{SUPABASE_URL}/auth/v1/token?grant_type=refresh_token",
        headers={
            "apikey": SUPABASE_ANON_KEY,
            "Content-Type": "application/json",
        },
        json={"refresh_token": refresh_token},
    )
    resp.raise_for_status()
    return resp.json()["access_token"]
async function refreshToken(refreshToken) {
  const resp = await fetch(
    `${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`,
    {
      method: "POST",
      headers: {
        apikey: SUPABASE_ANON_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ refresh_token: refreshToken }),
    }
  );
  const { access_token } = await resp.json();
  return access_token;
}
NEW_TOKEN=$(curl -s -X POST \
  "${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token" \
  -H "apikey: ${SUPABASE_ANON_KEY}" \
  -H "Content-Type: application/json" \
  -d "{\"refresh_token\":\"${REFRESH_TOKEN}\"}" \
  | jq -r '.access_token')
def refresh_access_token(refresh_token)
  uri = URI("#{SUPABASE_URL}/auth/v1/token?grant_type=refresh_token")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  req = Net::HTTP::Post.new(uri)
  req['apikey'] = SUPABASE_ANON_KEY
  req['Content-Type'] = 'application/json'
  req.body = JSON.dump(refresh_token: refresh_token)

  resp = http.request(req)
  JSON.parse(resp.body)['access_token']
end

Boas praticas

Armazene o refresh_token de forma segura (variavel de ambiente ou secret manager). Nunca exponha tokens em logs ou codigo fonte.


Roles: admin vs driver

O JWT contem um campo user_metadata.role que define o que o usuario pode acessar:

Role Acesso
admin Criar/listar/cancelar entregas, ver todos motoristas e localizacoes, gerenciar precos
driver Aceitar/iniciar/completar entregas proprias, acessar propria carteira, atualizar GPS

Acesso negado

Chamar um endpoint com role insuficiente retorna 403 Forbidden. Veja Erros & Status para detalhes.


Endpoints publicos (sem autenticacao)

Endpoint Descricao
GET /health Health check
POST /api/v1/webhooks/asaas Webhook ASAAS (usa HMAC-SHA256 no header asaas-signature)