> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Federado com Firebase

> O Firebase Authentication é dono da conta. Verifique o ID token no servidor, assine o uid e o UserKit confia.

O Firebase Authentication é a sua fonte da verdade. O UserKit precisa saber *qual*
dos seus usuários está na página, e não vai aceitar a palavra da página — o seu
servidor assina a afirmação.

<Warning>
  **O identity secret nunca entra no bundle.** Nunca prefixe com `NEXT_PUBLIC_`,
  nunca renderize no HTML, nunca mande para o navegador para economizar uma ida e
  volta. Quem tem esse segredo emite sessão **verificada** para qualquer um dos
  seus usuários.
</Warning>

O contrato é o da página de
[identidade federada](/pt-br/customer-auth/federated), e nada aqui o altera:

```
hash = hex( HMAC-SHA256( identity_secret, external_id ) )
```

## O que o Firebase fornece

Um valor: `uid`. O Firebase é o provedor que não chega como cookie que o seu
servidor pode ler — o navegador guarda um **ID token** e o envia, e um token é uma
string que qualquer um pode compor. É o `verifyIdToken` que transforma isso em
fato.

```ts lib/firebase.ts theme={null}
import "server-only";

import { getAuth } from "firebase-admin/auth";

export async function currentExternalId(idToken: string | null): Promise<string | null> {
  if (!idToken) return null;
  try {
    const decoded = await getAuth(admin()).verifyIdToken(idToken);
    return decoded.uid; // ← o external_id
  } catch {
    return null;
  }
}
```

<Warning>
  O `user.uid` também está ali no navegador, e deliberadamente não é o que se envia.
  Um id que a página fornece é um id que a página escolheu — assiná-lo deixaria
  qualquer visitante ser qualquer pessoa.
</Warning>

## A metade do servidor

```ts app/api/userkit-boot/route.ts theme={null}
import { createHmac } from "node:crypto";
import { NextResponse, type NextRequest } from "next/server";

import { currentExternalId } from "@/lib/firebase";

export const dynamic = "force-dynamic";
// O Admin SDK é uma biblioteca Node; não roda no runtime edge.
export const runtime = "nodejs";

export async function GET(request: NextRequest) {
  const header = request.headers.get("authorization") ?? "";
  const idToken = header.toLowerCase().startsWith("bearer ") ? header.slice(7) : null;

  const externalId = await currentExternalId(idToken);
  if (!externalId) {
    return NextResponse.json({ error: "not_signed_in" }, { status: 401, headers: NO_STORE });
  }

  const hash = createHmac("sha256", process.env.USERKIT_IDENTITY_SECRET!)
    .update(externalId)
    .digest("hex");

  return NextResponse.json({ external_id: externalId, hash }, { headers: NO_STORE });
}

// Este corpo identifica uma pessoa. Um CDN que o guardasse entregaria ao próximo
// visitante a prova da identidade de outra pessoa.
const NO_STORE = { "Cache-Control": "no-store" };
```

Um ID token que não verifica responde `401`, exatamente como um ausente. A
diferença é um detalhe da sua verificação para o qual o navegador não tem uso.

## A metade do cliente

```tsx theme={null}
"use client";

import { createClient } from "@userkit/js";

const userkit = createClient({
  publishableKey: process.env.NEXT_PUBLIC_USERKIT_PUBLISHABLE_KEY!,
});

const user = getAuth().currentUser;

if (!user) {
  // Continua sendo um visitante. O boot anônimo é onde a atribuição de primeiro
  // toque cai.
  await userkit.boot();
} else {
  const response = await fetch("/api/userkit-boot", {
    cache: "no-store",
    headers: { Authorization: `Bearer ${await user.getIdToken()}` },
  });
  const { external_id, hash } = await response.json();
  await userkit.boot({ externalId: external_id, hash });
}
```

`getState().verified` só é `true` quando o HMAC conferiu. Logado no Firebase com
`verified: false` significa que o segredo é do outro ambiente ou que a mensagem
assinada não era exatamente o `external_id`.

## Session cookies também funcionam

`createSessionCookie` é a outra forma, e deixa este endpoint com a cara do exemplo
do Supabase: leia o cookie, verifique com `verifySessionCookie`, assine o uid. Nada
acima da linha do provedor muda.

## Configuração

| Variável                                                        | Onde vive                                                                           |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `USERKIT_IDENTITY_SECRET`                                       | **Só no servidor.** `GET /v1/organization/environments/{id}/identity`, por ambiente |
| `NEXT_PUBLIC_USERKIT_PUBLISHABLE_KEY`                           | No navegador. Identifica o ambiente e não autentica nada                            |
| A service account (`project_id`, `client_email`, `private_key`) | **Só no servidor.** É com ela que o Admin SDK verifica                              |
| A config web do Firebase                                        | No navegador. A `apiKey` identifica um projeto e não autentica nada                 |

## O que não viaja

Um ID token do Firebase carrega `email` e a claim `email_verified`. São afirmações
sobre os registros do Firebase, e não viram identidade no UserKit: um e-mail
enviado pelo `/v1/boot` é guardado como atributo, não resolve para um contato
existente e não vira aresta de identidade. O HMAC prova o `external_id` e só ele.

Para provar um endereço, mande um [magic link ou um código por
e-mail](/pt-br/customer-auth/email-proof) — os dois fluxos que chegam nele.

<Card title="O exemplo completo" icon="github" href="https://github.com/userkithq/monorepo/tree/main/examples/federated-firebase">
  Um app Next executável com esses arquivos, um `.env.example` e as partes que esta
  página deixa de fora.
</Card>
