core/ZITADEL_SETUP.md

225 lines
8.1 KiB
Markdown

# Zitadel + Next.js App Router Integration Setup
Guia corporativo atuando como Especialista em Engenharia de Software e DevOps para inicialização do ecossistema Zitadel on-premise (Baremetal/Binário direto) integrado à um ambiente moderno Next.js.
## Parte 1: Setup do Serviço de Autenticação (Zitadel)
### 1. Download e Instalação (Binário Oficial)
O Zitadel é distribuído em um binário em Go altamente otimizado. Para ambientes baseados em Linux/MacOS:
```bash
# Definir a versão alvo
export ZITADEL_VERSION="v2.66.3" # ou a tag latest estável
# Download macOS (usar darwin_arm64 para Apple Silicon ou darwin_amd64 para Intel)
# Download Linux (usar linux_amd64 ou linux_arm64)
curl -sLO "https://github.com/zitadel/zitadel/releases/download/${ZITADEL_VERSION}/zitadel_Linux_x86_64.tar.gz"
# Extrair
tar -xvf zitadel_Linux_x86_64.tar.gz
# Mover binário pro path
sudo mv zitadel /usr/local/bin/
```
### 2. Configuração Básica e Local (Sem TLS / Insecure)
Crie um arquivo `config.yaml` voltado para acoplamento interno. Ele especifica conexões sem segredos e expõe a interface sem demandar certificados TLS para localhost.
**`config.yaml`**:
```yaml
ExternalSecure: false
Port: 8080
# Conexão com sua base PostgreSQL limpa inicializada (Pode ser local/Docker)
Database:
postgres:
Host: localhost
Port: 5432
Database: zitadel
User:
Username: "postgres"
Password: "your-password"
SSL:
Mode: disable
Admin:
Username: "postgres"
Password: "your-password"
SSL:
Mode: disable
```
### 3. Setup e Start
A injeção dos esquemas base e a inicialização do container de autenticação:
```bash
# 1. Aplicar migrações ao PostgreSQL provisionado
zitadel setup --masterkey "a-32-byte-master-key-must-be-set" --config config.yaml
# 2. Levantar o Listener HTTP
zitadel start --masterkey "a-32-byte-master-key-must-be-set" --config config.yaml
```
*O setup inicial gerará um log contendo as informações do usuário `machine` primário (`zitadel-admin`). Guarde este log/json.*
### 4. Gerar chaves (Service User \/ Machine Key)
1. Acesse `http://localhost:8080/ui/console`.
2. Vá em **Projects** > (Seu projeto) > **Service Users**.
3. Crie um novo. Confirme.
4. Na página de gerenciar esse Service User, vá em **Keys** e adicione uma nova.
5. Selecione formato **JSON**. Ele fará o download da `machinekey.json`. Guarde esse documento num `secrets/` do Next.
---
## Parte 2: Dashboard Next.js (App Router + Tailwind)
### 1. Inicializando Projetos
```bash
npx create-next-app@latest admin-dashboard --typescript --tailwind --eslint --app
cd admin-dashboard
npm install @zitadel/nextjs @zitadel/server
```
### 2. Middleware de Proteção
Crie um arquivo em `src/middleware.ts` para capturar a sessão sem bloqueios pesados.
```typescript
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// A SDK usa os tokens armazenados em cookies.
const hasZitadelCookie = request.cookies.some((c) => c.name.includes("zitadel"));
if (!hasZitadelCookie && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/api/auth/signin", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
```
### 3. Server Component de Dashboard
Interface estilizada de alto nível para exibição do IAM e profile contextual, injetante de tailwind corporativo de forma nativa e sem `use client`.
`src/app/dashboard/page.tsx`
```tsx
import { getSession } from '@zitadel/nextjs';
// A API Wrapper SDK para o client Admin gRPC/REST do Zitadel
import { createZitadelClient } from '@zitadel/server';
import fs from 'fs/promises';
export default async function DashboardPage() {
const session = await getSession();
if (!session?.user) {
return <p className="text-zinc-400 font-mono">Não autorizado.</p>;
}
// 1. Instanciando Client IAM via Machine JSON
const keyFile = await fs.readFile(process.cwd() + '/secrets/machinekey.json', 'utf8');
const zitadelClient = createZitadelClient({
token: keyFile,
apiUrl: process.env.ZITADEL_API_URL!,
});
// 2. Coletando Orgs
let organizations: any[] = [];
try {
const { orgs } = await zitadelClient.management.listOrgs({});
organizations = orgs || [];
} catch (error) {
console.error("Falha ao buscar organizações", error);
}
return (
<div className="min-h-screen bg-zinc-950 text-zinc-100 flex flex-col p-8 font-sans">
<div className="max-w-5xl w-full mx-auto space-y-6">
<header className="border-b border-zinc-800 pb-4 mb-8">
<h1 className="text-3xl font-bold tracking-tight">Identity Control Plane</h1>
<p className="text-zinc-500 mt-2">Visão Administrativa Zitadel</p>
</header>
<section className="bg-zinc-900 rounded-xl border border-zinc-800 p-6 shadow-2xl">
<h2 className="text-xl font-semibold mb-4 text-white">Perfil Atual</h2>
<div className="grid grid-cols-2 gap-4">
<div className="bg-black/50 p-4 rounded-lg">
<span className="text-xs text-zinc-500 uppercase">Usuário</span>
<p className="font-medium font-mono text-zinc-300 mt-1">{session.user.name}</p>
</div>
<div className="bg-black/50 p-4 rounded-lg">
<span className="text-xs text-zinc-500 uppercase">E-mail ID</span>
<p className="font-medium font-mono text-blue-400 mt-1">{session.user.email}</p>
</div>
</div>
</section>
<section className="bg-zinc-900 rounded-xl border border-zinc-800 p-6 shadow-2xl">
<h2 className="text-xl font-semibold mb-4 flex items-center justify-between">
<span>Organizações <span className="ml-2 text-xs bg-indigo-500/20 text-indigo-400 px-2 py-1 rounded-md">Service API</span></span>
</h2>
<ul className="space-y-3">
{organizations.length === 0 ? (
<li className="text-zinc-500 text-sm">Nenhuma organização encontrada sob este tenant.</li>
) : (
organizations.map((org) => (
<li key={org.id} className="flex justify-between items-center p-4 bg-black/40 rounded-lg border border-zinc-800 hover:border-indigo-500 transition-colors cursor-pointer">
<div className="flex flex-col">
<span className="font-medium text-zinc-200">{org.name}</span>
<span className="text-xs font-mono text-zinc-600 mt-1">ID: {org.id}</span>
</div>
<span className="text-xs px-3 py-1 bg-emerald-500/10 text-emerald-400 rounded-full border border-emerald-500/20">Active</span>
</li>
))
)}
</ul>
</section>
</div>
</div>
);
}
```
---
## Parte 3: Guia de Integração Ambiente Local
### 1. Preparação Local Next.js (`.env.local`)
Adicione o endpoint do provedor OIDC e credenciais do dashboard para sua integração funcionar na rota cliente:
```env
# SDK NextAuth
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="uma-string-secreta-gigante-aqui-random"
# Zitadel Configuration (A porta que você mapeou no config.yaml)
ZITADEL_ISSUER="http://localhost:8080"
ZITADEL_CLIENT_ID="AQUI_VAI_O_CLIENT_ID_DO_SEU_WEB_APP"
ZITADEL_CLIENT_SECRET="AQUI_VAI_O_SECRET_DO_WEB_APP"
# API Endpoint (Utilizado pelo Client JWT Server para as Orgs)
ZITADEL_API_URL="http://localhost:8080"
```
### 2. Rodando o Ecossistema Completamente Local
Para rodar os serviços paralelos no seu ambiente de terminal:
**No Terminal 1 (O IAM Authority Zitadel):**
```bash
./zitadel start --masterkey "a-32-byte-master-key-must-be-set" --config config.yaml
```
**No Terminal 2 (O Dashboard Edge-side):**
```bash
cd admin-dashboard
npm run dev
```
Essa arquitetura garante que a requisição de contexto do OIDC caia por trás dos painéis locais e que você utilize a `machinekey` (Service Account) internamente em rotas NodeJS sem jamais vazar a chave sensível da infraestrutura, além do Tailwind em Dark Mode proporcionar uma visualização profissional para auditoria de redes.