Por que isso não é trivial
Cada produto Googa hoje é servido por um projeto Supabase — Postgres com Row Level Security (RLS), Supabase Auth e Edge Functions. Isso resolve muito bem um problema: autenticar o usuário final (o leitor) dentro do próprio app oficial. A API de parceiro precisa resolver um problema diferente e mais amplo:- Duas identidades, não uma. O parceiro (ex: o backend da Algar) autentica como sistema, não como pessoa — client credentials, sem sessão de navegador. O assinante final autentica como pessoa, via SSO federado pelo “Minha Algar”. São dois fluxos de auth completamente diferentes que convergem na mesma API.
- Multi-projeto. NovelAI e NovelAudio já compartilham um projeto Supabase; HistorinhAI roda
em outro, isolado — decisão deliberada, dado o tratamento de dados de crianças (ver
Proteção de dados de menores). A API de parceiro precisa apresentar
uma superfície única em três domínios —
api.googa.com.br,api.novelai.com.br(que também atende o NovelAudio) eapi.historinhai.com.br— sem expor essa fragmentação para quem integra. Não existeapi.novelaudio.com.br(ver NovelAudio). - Domínio próprio, não
*.supabase.co. Nenhum parceiro enterprise aceita apontar para o domínio de um fornecedor de infra. O desenho usaapi.novelai.com.br,api.historinhai.com.breapi.googa.com.br, com TLS e sem redirect visível.
Capacidades nativas do Supabase
Custom Domains — mapear api.novelai.com.br para o projeto
Custom Domains — mapear api.novelai.com.br para o projeto
O plano Supabase Pro (e superiores) inclui Custom Domains: você aponta um
CNAME do seu
domínio para <project-ref>.supabase.co, o Supabase provisiona e renova o certificado TLS
automaticamente, e a partir daí tanto PostgREST (/rest/v1) quanto Edge Functions
(/functions/v1) e Auth (/auth/v1) passam a responder sob o domínio próprio.Para os três domínios de API, isso significa: api.novelai.com.br é o único backend
físico tanto para NovelAI quanto para NovelAudio — os dois apps já compartilham o mesmo
projeto Supabase hoje (“Novel”), então não faz sentido criar um api.novelaudio.com.br
separado só para bater na mesma infraestrutura; a Edge Function resolve por rota/recurso
(texto vs. áudio), não por domínio. api.historinhai.com.br aponta para o projeto Supabase
próprio do HistorinhAI — isolado de propósito. api.googa.com.br (Platform API) usa um
projeto Supabase dedicado, só para a camada de parceiro — ver Multi-tenant.SSO/OIDC — Supabase Auth como Relying Party do Minha Algar (planejado)
SSO/OIDC — Supabase Auth como Relying Party do Minha Algar (planejado)
Planejado — ainda não implementado: nenhum IdP de parceiro está configurado hoje.Supabase Auth suporta provedores OIDC customizados (além dos sociais padrão). Para o fluxo
“login federado pelo Minha Algar, sem nova senha”, o desenho correto é: o Minha Algar é o
Identity Provider (IdP), e o Supabase Auth de cada produto atua como Relying Party (RP)
— exatamente o papel para o qual foi construído.Fluxo (
/oauth2/authorize do lado da Algar):- O assinante toca “Ler novela” dentro do app Minha Algar.
- Minha Algar redireciona para
api.novelai.com.br/auth/v1/authorize?provider=algar(endpoint nativo do Supabase Auth, configurado com o Client ID/Secret do provedor OIDC da Algar). - Supabase Auth troca o
authorization_codepeloid_tokenda Algar, valida a assinatura, e cria (ou recupera) o usuário Supabase correspondente — osubdo token da Algar fica gravado emauth.identities, e osubscriber_idda Algar vai paraapp_metadata. - O app recebe uma sessão Supabase normal (JWT
access_token/refresh_token) — daqui em diante, RLS funciona exatamente como para um usuário que criou conta com e-mail/senha.
app_metadata.algar_subscriber_id nas políticas
de RLS que hoje já usam auth.uid().Webhooks de saída — Database Webhooks nativos
Webhooks de saída — Database Webhooks nativos
Os eventos que o parceiro precisa (
subscriber.updated, subscription.canceled) nascem de
mudanças na tabela de entitlements de parceiro. Supabase tem Database Webhooks nativos:
um trigger Postgres (pg_net) dispara uma chamada HTTP assíncrona sempre que uma linha muda.
O desenho implementado encadeia isso a uma função intermediária de despacho
(webhook-dispatch, projeto Platform), que assina o payload com HMAC-SHA256
(X-Googa-Signature, X-Googa-Event) e entrega ao webhook_url do parceiro em uma única
tentativa — o retry nativo do Database Webhook cobre falha ao invocar a função de despacho,
mas não existe fila própria de reentrega ao endpoint do parceiro. Não precisa de Kafka nem
de um broker separado para o volume de eventos de um parceiro de billing — é a ferramenta
certa para o tamanho do problema. Ver a verificação de assinatura em
Platform API — Eventos.Isolamento multi-tenant — Row Level Security
Isolamento multi-tenant — Row Level Security
RLS já é a linha de defesa principal hoje (cada leitor só vê as próprias linhas). Para a API
de parceiro, o desenho implementado é diferente do RLS por política do leitor: as tabelas de
domínio de parceiro (
partners, partner_credentials, partner_subscribers…) têm RLS
habilitado com zero policies — nenhum acesso por anon/authenticated; só as Edge
Functions do Partner Gateway, via service_role, as tocam. O isolamento entre parceiros é
aplicado dentro dessas functions: toda consulta filtra pelo partner_id extraído do JWT de
serviço verificado — nunca por um valor enviado pelo cliente na própria requisição. Ver
Modelo multi-tenant.Partner Gateway — a camada que conecta as duas pontas
Supabase não oferece, nativamente, um servidor de autorização OAuth2 para emitir credenciais a outros sistemas (client credentials grant) — ele é um IdP para pessoas, não uma central de emissão de chaves de API para máquinas. Isso é esperado: nenhum BaaS genérico resolve isso, porque é uma decisão de produto, não de infraestrutura. O Partner Gateway é a camada fina construída por cima para resolver exatamente isso.1
Tabela partner_credentials + partner_id em todo domínio de negócio
Um projeto Supabase dedicado (“Platform”) guarda
partners (com webhook_url/webhook_secret
de cada parceiro), partner_credentials (client_id, hash do client_secret, escopos
permitidos, status) e partner_api_keys (chave rotacionável para leituras simples — a tabela
existe, mas a verificação por API key ainda não está implementada em nenhum endpoint).
Nenhum dado de leitor mora aqui — só metadado de integração.2
Edge Function 'Partner Gateway' — troca client credentials por um JWT de curta duração
Uma Edge Function em
api.googa.com.br/oauth2/token implementa o grant
client_credentials do OAuth2 (RFC 6749 §4.4): recebe client_id + client_secret, valida
contra partner_credentials, e devolve um JWT assinado com os escopos do parceiro e TTL curto
(10 minutos). Esse JWT é o que autentica as chamadas seguintes a Entitlements/Billing — ele
nunca é um JWT de usuário Supabase, é um JWT de serviço, verificado pelas outras Edge
Functions via a chave pública do projeto Platform.3
Roteamento cross-projeto
Como NovelAI/NovelAudio e HistorinhAI vivem em projetos Supabase diferentes, o Partner Gateway
(projeto Platform) não acessa os dados diretamente — ele chama as Edge Functions de domínio de
cada produto (
entitlements-novelai, entitlements-historinhai) passando o JWT de serviço
junto, e cada produto valida esse JWT contra a chave pública do Platform antes de aplicar sua
própria lógica de RLS/negócio local. Isso mantém o isolamento entre produtos sem duplicar a
lógica de autenticação de parceiro em cada projeto.Exceção deliberada (planejada — ainda não implementada) — HistorinhAI emitir a própria
credencial. Para o produto que trata dados de crianças, o desenho de destino vai um passo
além: api.historinhai.com.br teria seu próprio emissor de token e seu próprio par de chaves
de assinatura, independentes do Platform Gateway, servindo o futuro endpoint de engajamento
(ver API HistorinhAI). Hoje isso não existe: o HistorinhAI
valida o token emitido pelo Platform (mesma chave pública), e o emissor próprio foi
deliberadamente adiado para uma revisão dedicada. Quando existir, será defesa em
profundidade: um comprometimento da chave de assinatura do Platform não dá acesso a nenhuma
credencial do HistorinhAI, e vice-versa — ao custo de um cadastro de credencial duplicado por
parceiro, um trade-off aceito dado o que está em jogo nesse produto especificamente.4
Rate-limit & WAF na borda
Nem PostgREST nem Edge Functions têm rate-limit configurável por parceiro/rota fora do que o
Supabase já aplica para proteção própria da plataforma. A camada de rate-limit por parceiro
(e WAF) fica na borda, antes do Custom Domain — um proxy leve (Cloudflare na frente do CNAME é
a opção mais simples: regras de rate-limit por token de API, sem mudar nada do lado do
Supabase).
Resumindo: quem autentica o quê
Nenhuma dessas quatro linhas exige trocar o Supabase por outra coisa — todas são construídas
em cima do que já existe, com uma única peça nova (o Partner Gateway) fazendo a ponte entre
“autenticação de máquina” e o modelo de “autenticação de pessoa” que o Supabase já resolve bem.