Pangeia ID - Guia Completo de Integracao (v2)

Esta pagina e o guia oficial para integrar login Pangeia ID em qualquer site/app, com foco em onboarding rapido e sem ambiguidade.

Contrato usado aqui: frontend sem apiKey obrigatoria, exchange server-to-server com Authorization: Bearer.


TL;DR (em 30 segundos)

  1. Adicione o script pangeia-id.js na sua pagina.
  2. Crie um botao com classe .pangeiaid-login e data-exchange-url.
  3. Implemente no seu backend o endpoint POST /auth/exchange-hash.
  4. Seu backend chama POST /api/auth/exchange-hash no Pangeia com bearer token.
  5. Com pid + user, crie sessao local e pronto.

Passo a Passo (modo newbie)

Passo 1 - Frontend: botao pronto

<button class="pangeiaid-login" data-exchange-url="/auth/exchange-hash">
  Entrar com Pangeia ID
</button>

<script defer src="https://id.pangeialabs.com/static/pangeia-id.js"></script>
<script defer src="/static/js/pangeia-auth.js"></script>

Exemplo de /static/js/pangeia-auth.js (arquivo externo, sem inline):

document.addEventListener("pangeia:authenticated", function () {
  window.location.reload();
});

document.addEventListener("pangeia:error", function (ev) {
  console.error("Erro Pangeia:", ev.detail);
  alert("Nao foi possivel autenticar agora.");
});

CSP recomendada (sem unsafe-inline)

Para evitar bloqueios de Content Security Policy, nao use onclick/onload no HTML e nao use <script>...</script> inline.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://id.pangeialabs.com;
  connect-src 'self' https://id.pangeialabs.com;
  frame-ancestors 'self';
  base-uri 'self';

Se voce realmente precisar de script inline, use nonce no seu backend e script-src 'nonce-...'. O recomendado para integracao do Pangeia ID continua sendo script externo com defer.

Passo 2 - Backend: endpoint local de exchange

Esse endpoint recebe login_hash do frontend e troca no Pangeia ID.

Exemplo Python (Flask)

import os
import requests
from flask import Flask, request, jsonify, session

app = Flask(__name__)
app.secret_key = "troque-isto"

PANGEIA_BASE = "https://id.pangeialabs.com"
PANGEIA_TOKEN = os.environ["PANGEIA_INTEGRATOR_TOKEN"]

@app.post("/auth/exchange-hash")
def auth_exchange_hash():
    data = request.get_json() or {}
    login_hash = (data.get("login_hash") or "").strip()
    if not login_hash:
        return jsonify({"ok": False, "error": "login_hash obrigatoria"}), 400

    resp = requests.post(
        f"{PANGEIA_BASE}/api/auth/exchange-hash",
        headers={
            "Authorization": f"Bearer {PANGEIA_TOKEN}",
            "Content-Type": "application/json",
        },
        json={"login_hash": login_hash},
        timeout=15,
    )

    out = resp.json()
    if not resp.ok or not out.get("ok"):
        return jsonify(out), resp.status_code

    # Sessao local do seu app
    session["pid"] = out["pid"]
    session["user_email"] = out["user"]["email"]

    return jsonify(out), 200

Exemplo Node.js (Express)

import express from "express";

const app = express();
app.use(express.json());

const PANGEIA_BASE = "https://id.pangeialabs.com";
const PANGEIA_TOKEN = process.env.PANGEIA_INTEGRATOR_TOKEN;

app.post("/auth/exchange-hash", async (req, res) => {
  const login_hash = (req.body?.login_hash || "").trim();
  if (!login_hash) {
    return res.status(400).json({ ok: false, error: "login_hash obrigatoria" });
  }

  const r = await fetch(`${PANGEIA_BASE}/api/auth/exchange-hash`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${PANGEIA_TOKEN}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ login_hash })
  });

  const out = await r.json();
  return res.status(r.status).json(out);
});

Passo 3 - Consultar dados pelo PID

curl -X POST https://id.pangeialabs.com/api/user/by-pid \
  -H "Authorization: Bearer SEU_TOKEN_DE_INTEGRADOR" \
  -H "Content-Type: application/json" \
  -d '{"pid":"pid_xxx"}'

Contrato HTTP oficial (v2)

POST /api/auth/exchange-hash

Headers obrigatorios

Authorization: Bearer <integrator_token>
Content-Type: application/json

Body

{ "login_hash": "temp_..." }

Resposta de sucesso

{
  "ok": true,
  "pid": "pid_...",
  "user": {
    "id": "...",
    "name": "...",
    "email": "...",
    "phone": "...",
    "taxid": "...",
    "birth_date": "...",
    "avatar_url": "...",
    "created_at": "...",
    "updated_at": "..."
  }
}

Erros padronizados

POST /api/user/by-pid

Authorization: Bearer <integrator_token>
Content-Type: application/json
{ "pid": "pid_..." }

Webhook v2 (seguranca e consumo)

Configure sua webhook_url. O Pangeia enviara eventos assinados.

Headers

Webhook secret no seu backend: use o webhook_secret configurado na integração no dashboard Pangeia ID (ou rotacione em "API Keys e integrações").

Exemplo de env no app integrador: PANGEIA_ID_WEBHOOK_SECRET=ws_live_...

Payload

{
  "version": "2",
  "event_id": "evt_...",
  "event_type": "user.updated",
  "timestamp": "2026-02-14T00:00:00Z",
  "pid": "pid_...",
  "user": {}
}

Tipos de evento

Validacao de assinatura (Python)

import hmac, hashlib
from flask import request

def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    # signature_header esperado: "sha256=..."
    if not signature_header or not signature_header.startswith("sha256="):
        return False
    sent = signature_header.split("=", 1)[1]
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(sent, expected)

@app.post("/webhook/pangeia-id")
def webhook():
    raw = request.get_data()
    sig = request.headers.get("X-Pangeia-Signature", "")
    if not verify_signature(raw, sig, secret="SEU_WEBHOOK_SECRET"):
        return {"ok": False, "error": "invalid signature"}, 401

    event = request.get_json() or {}
    # idempotencia recomendada por event_id
    # processar user.updated / user.revoked
    return {"ok": True}, 200

Configuracao por data-* (auto-bind)

Voce pode configurar o SDK diretamente no HTML:

<button
  class="pangeiaid-login"
  data-exchange-url="/auth/exchange-hash"
  data-exchange-method="POST"
  data-exchange-credentials="include"
  data-exchange-timeout-ms="15000"
  data-exchange-payload-key="login_hash"
>
  Entrar
</button>

FAQ rapido

Preciso enviar apiKey no frontend?
Não. No v2, o app e identificado pelo bearer token no backend.

Posso continuar usando modo programatico?
Sim. PangeiaID.openLogin(...) continua disponivel.

Quando usar pid?
Sempre como identificador do usuario no seu app e para conciliar webhooks.


Checklist final (go-live)

  1. Botao de login com .pangeiaid-login funcionando.
  2. /auth/exchange-hash local criado e testado.
  3. Bearer token configurado no backend (nunca no browser).
  4. Sessao local criada apos exchange com sucesso.
  5. Webhook recebendo, validando assinatura e tratando idempotencia.