Você entra sem problema, mas cada vez que atualiza a página (ou depois de um tempinho) é jogado de volta para a tela de login. Esse é o bug de autenticação mais comum em apps feitos com vibe coding. Quase sempre é uma destas cinco causas: pule para a que combina com o seu caso.
Descubra sua causa rápido
- A. Expulso a cada atualização → seu app verifica "estou logado?" *antes* de a biblioteca de autenticação restaurar a sessão (a mais comum)
- B. Cai depois de ~30–60 min → a renovação automática do token está desativada
- C. Nunca é salvo (anônimo / cookies bloqueados) → o armazenamento da sessão (localStorage·cookies) está bloqueado
- D. Só quebrou depois do deploy (Next.js etc.) → falta o middleware do lado do servidor
- E. Frontend e backend em domínios diferentes → configuração errada de SameSite/Secure no cookie
A. Expulso ao atualizar — não espera a sessão ser restaurada (a mais comum)
Tanto o Supabase quanto o Firebase levam um instante mínimo (assíncrono) para carregar o login salvo do armazenamento. Se o código gerado por IA executa if (no user) redirect to login no exato momento em que a tela é montada, ele confunde esse breve intervalo de "ainda carregando" com um estado deslogado e te expulsa toda vez.
Chave: mantenha um estado separado de "ainda verificando" e mostre um loader até a autenticação ser resolvida.
Firebase (v9 modular):
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// logged in — render the app
} else {
// only redirect to login when truly signed out
}
});
O onAuthStateChanged dispara *depois* que o SDK restaura a sessão salva — decida dentro desse callback. Não leia auth.currentUser na montagem (nesse instante ainda pode ser null).
Supabase:
// once on load: read the saved session
const { data: { session } } = await supabase.auth.getSession();
// then subscribe to changes
supabase.auth.onAuthStateChange((event, session) => {
// session present = logged in
});
No React, adicione uma flag loading, mantenha em true até chegar o primeiro callback e nunca redirecione enquanto loading estiver ativo — apenas mostre um spinner.
Prompt para a sua IA:
> "Adicione um estado de loading enquanto a autenticação está sendo verificada e não redirecione para a página de login até o primeiro callback de onAuthStateChanged / onAuthStateChange ter sido executado."
B. Cai depois de um tempo — verifique a renovação automática do token
Os access tokens costumam expirar em cerca de uma hora. Se não forem renovados automaticamente, você fica "deslogado sem fazer nada."
Ao criar o cliente do Supabase, garanta que estas opções estejam ativadas (no navegador elas vêm ativadas por padrão — reative-as se você as desativou):
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: {
persistSession: true, // save session to localStorage
autoRefreshToken: true, // refresh token before it expires
detectSessionInUrl: true, // detect the session in the URL after OAuth
},
})
persistSession salva a sessão no localStorage; autoRefreshToken renova o token antes de ele expirar. Um erro comum é passar uma configuração de auth personalizada que acidentalmente deixa essas opções como false.
O Firebase usa por padrão a persistência local, então ela sobrevive ao fechamento do navegador e renova os tokens para você automaticamente.
C. Nunca é salvo — anônimo / cookies bloqueados
A sessão fica no localStorage (ou em cookies). Se esse armazenamento estiver bloqueado, ela cai mesmo logo após o login.
- Verifique se está em modo anônimo/privativo, com cookies bloqueados ou com cookies/dados de terceiros bloqueados. A persistência
localdo Firebase só funciona "desde que o navegador suporte esse mecanismo de armazenamento, por exemplo, com cookies/dados de terceiros habilitados." - A prevenção de rastreamento do Safari (ITP) pode limitar o armazenamento entre domínios.
- Para fixar a persistência do Firebase explicitamente, defina-a antes de fazer login:
import { getAuth, setPersistence, browserLocalPersistence } from "firebase/auth";
const auth = getAuth();
await setPersistence(auth, browserLocalPersistence); // then call signIn
Se estiver definida como browserSessionPersistence (limpa ao fechar a aba) ou inMemoryPersistence (limpa ao atualizar), é esperado que deslogue. Mude para browserLocalPersistence.
D. Deslogado depois do deploy (Next.js) — falta middleware no servidor
Com o Supabase no Next.js (App Router) fazendo renderização no servidor, a sessão precisa ficar em cookies, não no localStorage. Mas os Server Components não conseguem escrever cookies, então, a menos que um middleware renove o token, o token expirado permanece e você é deslogado ao atualizar.
Correção:
- Use
@supabase/ssr:createBrowserClientno navegador,createServerClientno servidor. - Adicione um
middleware.tsna raiz do projeto que renove a sessão a cada requisição (o padrão oficialupdateSession). - No servidor, não confie no
getSession()para proteger páginas — usegetClaims()(ougetUser()). Não há garantia de que ogetSession()revalide o token.
Copie o middleware direto da documentação oficial de SSR do Supabase (nas fontes abaixo) — é o caminho mais seguro.
Prompt para a sua IA:
> "Separe em createBrowserClient/createServerClient do @supabase/ssr, adicione um middleware.ts que rode updateSession a cada requisição e proteja as rotas do servidor com getUser."
E. Domínios diferentes — SameSite/Secure do cookie
Se o seu próprio backend (Express, etc.) guarda o login em um cookie e o frontend e a API estão em domínios diferentes, o cookie não é armazenado/enviado e você é deslogado.
- Mesmo domínio:
SameSite=Lax(o padrão quando não especificado). Costuma resolver. - Domínios diferentes (cross-site): você precisa enviar
SameSite=None; Securejuntos. Os navegadores rejeitamSameSite=NonesemSecure(HTTPS). - Por isso, testar em http localhost pode descartar o cookie
Securee quebrar a persistência. Faça um proxy para a mesma origem localmente, ou sirva por https.
Exemplo de cookie:
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/
(Use SameSite=None; Secure entre domínios.)
SameSite=Strict pode descartar o cookie quando você chega de outro site (por exemplo, ao voltar de um provedor de OAuth), te jogando de volta para o login. Lax é a resposta certa na maioria dos casos.
Ainda travado — quando você não consegue identificar a causa
Aqui não existe uma solução universal de uma linha; a causa depende do seu stack. Vá restringindo na ordem:
- Abra o DevTools → Application → Local Storage / Cookies e confirme que o valor da sessão é realmente salvo após o login. Não salvo → C (armazenamento bloqueado) ou E (configuração do cookie).
- Salvo, mas some ao atualizar → A (timing da restauração) ou D (middleware SSR).
- Só cai depois de um tempo → B (renovação do token).
- Se nada disso ajudar, cole o código de exemplo oficial das fontes abaixo e faça primeiro uma reprodução mínima funcionar.
