Pular para o conteúdo
📖 COOKBOOK

Entrar com Google, do botão à sessão.

O botão abre o Google, a pessoa escolhe a conta e volta logada no seu app. O SuperDB cuida do OAuth e do PKCE; o seu app faz duas chamadas e cadastra uma URL de volta. Vale para cadastro e login: no primeiro acesso, a conta é criada.

O que vamos fazer

  • Ligar o Google no painel, sem chave nenhuma (modo gerenciado) ou com o app Google do seu produto.
  • Cadastrar a URL para onde o login volta.
  • Um botão que chama db.auth.signInWithOAuth() e abre o Google.
  • Uma página de volta que troca o ?code= pela sessão com db.auth.exchangeCodeForSession().

O exemplo é uma SPA em React com Vite. No fim, a mesma receita no celular com Expo.

Como o fluxo funciona

  1. O botão chama signInWithOAuth({ provider: 'google', redirectTo }). O SDK gera o par PKCE, guarda o verificador no armazenamento da sessão e devolve data.url, o endereço do Google. Ele não redireciona sozinho: quem abre a URL é o seu código.
  2. A pessoa escolhe a conta no Google, que devolve para o SuperDB (https://auth.superdb.com.br/auth/v1/callback/google).
  3. O SuperDB cria ou encontra a conta e manda a pessoa para o seu redirectTo com um ?code= de uso único.
  4. A página de volta chama exchangeCodeForSession(code). O SDK confere o verificador PKCE, recebe a sessão e a guarda. Pronto: db.from() já consulta como o usuário logado.

Pré-requisitos

  • Um projeto no SuperDB, em qualquer plano, e acesso de dono ou admin a ele (para mexer em Autenticação).
  • @superdb/client 0.2.2 ou mais novo, com o cliente criado com o project:
src/lib/superdb.ts
import { createClient } from '@superdb/client'

export const db = createClient(
  import.meta.env.VITE_SUPERDB_URL,       // https://auth.superdb.com.br
  import.meta.env.VITE_SUPERDB_ANON_KEY,
  { project: import.meta.env.VITE_SUPERDB_PROJECT },
)

Passo 1: ligar o Google no painel

No painel, abra o projeto e vá em Autenticação → Login social (OAuth) → Google. Há dois modos:

GerenciadoCredenciais próprias
O que configurarNada: é só ligarClient ID e Client Secret do Google Cloud
Tela de consentimentoMostra “SuperDB”Mostra o nome e o logo do seu app
Quando usarProtótipo, app interno, testeApp que vai para o público

Para usar credenciais próprias, marque usar minhas próprias credenciais e siga o passo a passo que aparece ao lado, no próprio painel. O essencial:

  1. No Google Cloud Console, preencha a Tela de permissão OAuth (nome, e-mail de suporte, logo). É ela que o usuário vê ao entrar.
  2. Em Público, publique o app. Enquanto ele estiver em teste, só entram os e-mails cadastrados como testadores.
  3. Crie a credencial em Credenciais → Criar credenciais → ID do cliente OAuth → Aplicativo da Web.
  4. Em URIs de redirecionamento autorizados, cole exatamente https://auth.superdb.com.br/auth/v1/callback/google.
  5. Copie o Client ID e o Client Secret para o painel do SuperDB e salve.
⚠️

No Google Console vai o endereço do SuperDB, não o do seu app. O Google devolve sempre para auth.superdb.com.br; o endereço do seu app entra no passo 2. Se inverter, o Google responde redirect_uri_mismatch.

Passo 2: cadastrar a URL de volta

Em Autenticação → URLs de redirecionamento, adicione cada endereço para onde o login pode voltar, e salve:

Autenticação → URLs de redirecionamento
https://app.exemplo.com.br/auth/callback
http://localhost:5173/auth/callback

A comparação é exata: protocolo, domínio, porta e caminho. Um endereço que não está na lista não recebe o code (é o que impede alguém de usar o seu projeto para mandar o login para um site dele). O erro, nesse caso, é oauth_state_unbound, e a mensagem diz qual endereço faltou cadastrar.

Passo 3: o botão

src/EntrarComGoogle.tsx
import { useState } from 'react'
import { db } from './lib/superdb'

export function EntrarComGoogle() {
  const [erro, setErro] = useState<string | null>(null)

  async function entrar() {
    setErro(null)
    const { data, error } = await db.auth.signInWithOAuth({
      provider: 'google',
      redirectTo: `${location.origin}/auth/callback`, // igual ao cadastrado no passo 2
    })
    if (error) {
      return setErro(error.code === 'oauth_not_enabled'
        ? 'O login com Google não está ligado neste projeto.'
        : 'Não deu para abrir o Google agora. Tente de novo.')
    }
    location.assign(data.url) // o SDK devolve a URL; quem abre é você
  }

  return (
    <>
      <button type="button" onClick={entrar}>Entrar com Google</button>
      {erro && <p role="alert">{erro}</p>}
    </>
  )
}

Passo 4: a página de volta

A rota /auth/callback lê o code da URL e troca pela sessão. Ela precisa rodar no mesmo navegador que clicou no botão, porque o verificador PKCE ficou guardado nele.

src/Callback.tsx (rota /auth/callback)
import { useEffect, useRef, useState } from 'react'
import { db } from './lib/superdb'

export function Callback({ aoEntrar }: { aoEntrar: () => void }) {
  const [erro, setErro] = useState<string | null>(null)
  const jaTrocou = useRef(false)

  useEffect(() => {
    // Em desenvolvimento, o StrictMode roda o efeito duas vezes. O code só vale uma:
    // a segunda troca falharia e mostraria erro para quem acabou de entrar.
    if (jaTrocou.current) return
    jaTrocou.current = true

    const code = new URLSearchParams(location.search).get('code')
    if (!code) return setErro('O login não voltou com o código de acesso.')

    db.auth.exchangeCodeForSession(code).then(({ error }) => {
      history.replaceState(null, '', location.pathname) // o code já foi usado
      if (error) return setErro('O login não foi concluído. Tente entrar de novo.')
      aoEntrar()
    })
  }, [aoEntrar])

  if (erro) return <p role="alert">{erro} <button onClick={() => location.assign('/login')}>Voltar para o login</button></p>
  return <p>Entrando…</p>
}
⚠️

A sessão vale 1 hora. Desde a versão 0.3, o SDK renova sozinho um minuto antes de vencer, uma vez só mesmo com várias abas abertas. Numa versão anterior, chame db.auth.refreshSession() antes de vencer, ou use o sessao.ts dos templates oficiais. Detalhes em SDK JS → Auth.

Passo 5: quem entrou

src/lib/usuario.ts
import { db } from './superdb'

export async function usuarioAtual() {
  const { data, error } = await db.auth.getUser()
  if (error || !data.user) return null
  // id, email, display_name (o nome da conta Google), email_verified_at...
  return data.user
}

Como o SuperDB cuida da conta:

  • Primeiro acesso: a conta é criada com o e-mail e o nome que o Google informou. Se o Google confirma o e-mail, ele já nasce verificado.
  • Já existia conta com o mesmo e-mail e o Google confirma o e-mail: o login entra nessa conta, em vez de criar uma segunda.
  • As contas aparecem no painel, na lista de Usuários do projeto (pelo card da Visão geral). Dados do seu app, como foto e preferências, ficam numa tabela sua, com RLS por auth.uid().

No celular, com Expo

O fluxo é o mesmo. A diferença é o endereço de volta: um deep link do app, que também vai em URLs de redirecionamento.

src/entrarComGoogle.ts (Expo)
import * as WebBrowser from 'expo-web-browser'
import * as Linking from 'expo-linking'
import { db } from './superdb'

export async function entrarComGoogle() {
  const volta = Linking.createURL('auth') // meuapp://auth no app instalado
  const { data, error } = await db.auth.signInWithOAuth({ provider: 'google', redirectTo: volta })
  if (error) throw new Error(error.message)

  const r = await WebBrowser.openAuthSessionAsync(data.url, volta)
  if (r.type !== 'success') return null // a pessoa fechou o navegador

  const { queryParams } = Linking.parse(r.url) // o URL do React Native não lê a query
  const code = typeof queryParams?.code === 'string' ? queryParams.code : null
  if (!code) return null
  const troca = await db.auth.exchangeCodeForSession(code)
  return troca.error ? null : troca.data.session
}
💡

No Expo Go, Linking.createURL('auth') devolve um endereço exp:// com o IP da sua máquina, e ele muda quando a rede muda. Para testar com calma, cadastre o que aparecer no log ou use um development build, que tem o scheme fixo do app. No celular, a sessão precisa de um armazenamento persistente (AsyncStorage): o template Expo já vem assim.

GitHub e Apple

O código é o mesmo; muda o provider. Os dois também têm modo gerenciado, e o painel mostra o passo a passo das credenciais próprias:

outros provedores
await db.auth.signInWithOAuth({ provider: 'github', redirectTo })
await db.auth.signInWithOAuth({ provider: 'apple', redirectTo })

No GitHub, a URL de callback do app é https://auth.superdb.com.br/auth/v1/callback/github. Na Apple, credenciais próprias pedem um Services ID, a chave .p8 e uma conta paga no Apple Developer. Microsoft ainda não é self-serve.

Erros comuns

oauth_state_unbound depois de escolher a conta

O redirectTo não bate com nenhuma URL cadastrada no passo 2. Compare caractere por caractere: localhost e 127.0.0.1 são endereços diferentes, e uma barra no fim também conta.

oauth_not_enabled ao clicar no botão

O Google não está ligado neste projeto. Confira em Autenticação → Login social (OAuth), e se o project do cliente é o projeto certo.

redirect_uri_mismatch na tela do Google

Só acontece com credenciais próprias: falta https://auth.superdb.com.br/auth/v1/callback/google nas URIs autorizadas do Google Cloud Console.

no_pkce_verifier na página de volta

A troca rodou num navegador (ou armazenamento) diferente do que clicou no botão, ou o exchangeCodeForSession foi chamado duas vezes. Refaça o login pelo botão; no React, mantenha a guarda do useRef do passo 4.

Só alguns e-mails conseguem entrar

O app do Google ainda está em modo de teste. Publique-o em Tela de permissão OAuth → Público.

Essa página ajudou?