Sistema de Tokens e Autenticação

📚 Índice

Introdução

O Document Hub utiliza um sistema de autenticação baseado em tokens de acesso (access tokens) com controle granular de permissões através de escopos (scopes).

Por que Tokens?

  • 🔐 Segurança: Cada aplicação pode ter seu próprio token
  • 🎯 Granularidade: Controle preciso de permissões
  • 📊 Auditoria: Rastreie quem fez o quê
  • 🔄 Revogação: Revogue tokens comprometidos sem afetar outros
  • Expiração: Tokens podem ter data de validade

Como Funciona a Autenticação

Fluxo Básico

1. Cliente possui um UUID
   └── Token 1 (permissões específicas)
   └── Token 2 (outras permissões)
   └── Token 3 (acesso total)

2. Aplicação faz requisição com Token 1
   └── API valida o token
   └── Verifica permissões (escopos)
   └── Processa requisição se autorizado

Formato de Autenticação

Todas as requisições à API devem incluir o header de autenticação:

Authorization: Bearer {TOKEN_ID}|{PLAIN_TEXT_TOKEN}

Exemplo:

Authorization: Bearer 9fc42fe9-1234-5678-9abc-def123456789|abcdef1234567890abcdef1234567890abcdef12

Métodos Alternativos (menos comuns)

Embora não recomendado, você também pode usar headers separados:

X-Client-Key: 9fc42fe9-1234-5678-9abc-def123456789
X-Client-Token: abcdef1234567890abcdef1234567890abcdef12

Estrutura do Token

Modelo de Dados

{
  "id": "9fc42fe9-1234-5678-9abc-def123456789",
  "client_id": "9fc42fe9-a4fd-443c-8d49-b85ece2151b9",
  "name": "Token para Aplicação Mobile",
  "scopes": {
    "permissions": ["document:read", "document:create"],
    "document_rules": [...]
  },
  "status": "active",
  "last_used_at": "2024-01-15T10:30:00.000000Z",
  "expires_at": "2025-12-31T23:59:59.000000Z",
  "created_at": "2024-01-01T00:00:00.000000Z",
  "updated_at": "2024-01-15T10:30:00.000000Z"
}

Campos

CampoTipoDescrição
idUUIDIdentificador único do token
client_idUUIDID do cliente proprietário
namestringNome descritivo do token
scopesobject/arrayPermissões do token
statusenumactive ou inactive
last_used_attimestampÚltima vez que o token foi usado
expires_attimestampData de expiração (null = nunca expira)
created_attimestampData de criação
updated_attimestampData da última atualização

Estados do Token

StatusDescriçãoComportamento
activeToken ativo✅ Pode ser usado
inactiveToken desativado❌ Bloqueado temporariamente
Expiradoexpires_at < now❌ Não pode ser usado
DeletadoRemovido do banco❌ Não existe mais

Escopos Disponíveis

Lista Completa de Escopos

EscopoPermiteNecessário Para
document:createCriar novos documentosPOST /.../{key}, POST .../bulk
document:readLer documentos e históricoGET /.../{key}, GET .../history
document:updateAtualizar documentosPUT /.../{key}, POST .../restore
document:deleteDeletar documentosDELETE /.../{key}
schema:manageGerenciar schemas JSONPOST/GET/DELETE .../schema
webhook:manageGerenciar webhooksPOST/GET/PUT/DELETE /webhooks
token:manageGerenciar tokens de acessoPOST/GET/PUT/DELETE /tokens

Matriz de Permissões

EndpointMétodoEscopo Necessário
/context/{ctx}/type/{type}/{key}POSTdocument:create
/context/{ctx}/type/{type}/{key}GETdocument:read
/context/{ctx}/type/{type}/{key}PUTdocument:update
/context/{ctx}/type/{type}/{key}DELETEdocument:delete
/context/{ctx}/type/{type}GETdocument:read
/context/{ctx}/type/{type}/bulkPOSTdocument:create + document:update
/context/{ctx}/type/{type}/{key}/historyGETdocument:read
/context/{ctx}/type/{type}/{key}/history/{id}/restorePOSTdocument:update
/context/{ctx}/type/{type}/schemaPOSTschema:manage
/context/\{ctx}/type/\{type}/schemaGETschema:manage
/context/\{ctx}/type/\{type}/schemaDELETEschema:manage
/webhooksGET/POST/PUT/DELETEwebhook:manage
/tokensGET/POST/PUT/DELETEtoken:manage

Escopos Simples vs Granulares

O campo scopes aceita dois formatos para máxima flexibilidade.

1. Formato Simples (Array)

Concede as permissões para todos os documentos, sem distinção.

{
  "name": "Token Simples",
  "scopes": ["document:read", "document:create"]
}

Uso recomendado:

  • ✅ Protótipos e testes
  • ✅ Aplicações que acessam todos os tipos de documentos
  • ✅ Tokens administrativos

2. Formato Granular (Objeto)

Permite definir permissões específicas para diferentes tipos de documentos.

{
  "name": "Token Granular",
  "scopes": {
    "permissions": ["document:create"],
    "document_rules": [
      {
        "environment": "production",
        "context": "orders",
        "type": "invoice",
        "permissions": ["document:read", "document:update"]
      }
    ]
  }
}

Uso recomendado:

  • ✅ Produção
  • ✅ Múltiplas aplicações/serviços
  • ✅ Compliance e auditoria
  • ✅ Princípio do menor privilégio

Estrutura do Formato Granular

Campos do Objeto scopes

CampoTipoDescrição
permissionsarrayPermissões globais aplicadas a todos os recursos
document_rulesarrayLista de regras específicas para documentos

Campos de uma document_rule

CampoTipoObrigatórioDescrição
environmentstringNãoAplica-se apenas a este environment
contextstringNãoAplica-se apenas a este context
typestringNãoAplica-se apenas a este type
permissionsarraySimPermissões concedidas quando a regra corresponde

Como as Regras Funcionam

Uma requisição é autorizada se:

  1. A permissão necessária está em permissions OU
  2. Corresponde a pelo menos uma document_rule

Para uma regra corresponder:

  • Todos os campos definidos na regra devem ser iguais aos da requisição
  • Campos omitidos funcionam como wildcard (qualquer valor)

Exemplos de Regras

Exemplo 1: Acesso Total a um Contexto

{
  "scopes": {
    "document_rules": [
      {
        "environment": "production",
        "context": "orders",
        "permissions": ["document:read", "document:create", "document:update"]
      }
    ]
  }
}

✅ Autoriza: production/orders/invoice/*
✅ Autoriza: production/orders/quote/*
❌ Bloqueia: production/customers/*
❌ Bloqueia: staging/orders/*

Exemplo 2: Somente Leitura de Tipo Específico

{
  "scopes": {
    "document_rules": [
      {
        "type": "logs",
        "permissions": ["document:read"]
      }
    ]
  }
}

✅ Autoriza: */*/logs/* (qualquer ambiente, qualquer contexto, tipo=logs)
❌ Bloqueia: Qualquer outro tipo
❌ Bloqueia: Criar, atualizar ou deletar logs

Exemplo 3: Acesso Múltiplo

{
  "scopes": {
    "permissions": ["document:create"],
    "document_rules": [
      {
        "environment": "production",
        "context": "invoices",
        "permissions": ["document:read", "document:update"]
      },
      {
        "environment": "staging",
        "context": "users",
        "permissions": ["document:read", "document:delete"]
      }
    ]
  }
}

✅ Criar documentos em qualquer lugar
✅ Ler e atualizar faturas de produção
✅ Ler e deletar usuários de staging
❌ Bloqueia: Outras operações não listadas

Criar Tokens

Endpoint

POST /api/v1/client/{client}/tokens

Autenticação Necessária

Para criar tokens, você precisa de um token com escopo token:manage.

Body da Requisição

{
  "name": "Nome Descritivo",
  "scopes": { /* formato simples ou granular */ },
  "expires_at": "2025-12-31T23:59:59Z"
}
CampoTipoObrigatórioDescrição
namestringSimNome descritivo do token
scopesarray/objectSimPermissões (simples ou granular)
expires_attimestampNãoData de expiração (null = nunca expira)

Exemplo 1: Token Simples de Leitura

curl -X POST "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Token Somente Leitura",
    "scopes": ["document:read"],
    "expires_at": "2025-12-31T23:59:59Z"
  }'

Exemplo 2: Token com Acesso Total

curl -X POST "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Admin Token",
    "scopes": [
      "token:manage",
      "webhook:manage",
      "schema:manage",
      "document:create",
      "document:read",
      "document:update",
      "document:delete"
    ]
  }'

Exemplo 3: Token Granular para Faturas

curl -X POST "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Token para Faturas de Produção",
    "scopes": {
      "document_rules": [
        {
          "environment": "production",
          "context": "invoices",
          "permissions": ["document:read", "document:update"]
        }
      ]
    },
    "expires_at": "2025-12-31T23:59:59Z"
  }'

Exemplo 4: Token para Múltiplos Contextos

curl -X POST "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Token Backend Completo",
    "scopes": {
      "permissions": ["document:create", "webhook:manage"],
      "document_rules": [
        {
          "environment": "production",
          "context": "orders",
          "permissions": ["document:read", "document:update", "document:delete"]
        },
        {
          "environment": "production",
          "context": "customers",
          "permissions": ["document:read", "document:update"]
        },
        {
          "environment": "staging",
          "permissions": ["document:read", "document:create", "document:update", "document:delete"]
        }
      ]
    }
  }'

Resposta (201 Created)

{
  "message": "Token created successfully. This is the only time the token will be displayed.",
  "token": "9fc42fe9-1234-5678-9abc-def123456789|abcdef1234567890abcdef1234567890abcdef12",
  "token_details": {
    "id": "9fc42fe9-1234-5678-9abc-def123456789",
    "client_id": "9fc42fe9-a4fd-443c-8d49-b85ece2151b9",
    "name": "Token para Faturas de Produção",
    "scopes": {
      "document_rules": [
        {
          "environment": "production",
          "context": "invoices",
          "permissions": ["document:read", "document:update"]
        }
      ]
    },
    "status": "active",
    "last_used_at": null,
    "expires_at": "2025-12-31T23:59:59.000000Z",
    "created_at": "2024-01-15T10:30:00.000000Z",
    "updated_at": "2024-01-15T10:30:00.000000Z"
  }
}

⚠️ IMPORTANTE: O campo token contém o token completo. Guarde-o em um local seguro, pois ele nunca mais será exibido!

Listar Tokens

Endpoint

GET /api/v1/client/{client}/tokens

Exemplo

curl -X GET "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens" \
  -H "Authorization: Bearer {SEU_TOKEN}"

Resposta (200 OK)

{
  "current_page": 1,
  "data": [
    {
      "id": "9fc42fe9-1234-5678-9abc-def123456789",
      "client_id": "9fc42fe9-a4fd-443c-8d49-b85ece2151b9",
      "name": "Token Admin",
      "scopes": ["token:manage", "webhook:manage", "document:create"],
      "status": "active",
      "last_used_at": "2024-01-20T14:30:00.000000Z",
      "expires_at": null,
      "created_at": "2024-01-01T00:00:00.000000Z",
      "updated_at": "2024-01-15T10:30:00.000000Z"
    },
    {
      "id": "9fc42fe9-5678-1234-9abc-def123456789",
      "name": "Token Leitura",
      "scopes": ["document:read"],
      "status": "active",
      "last_used_at": "2024-01-21T09:15:00.000000Z",
      "expires_at": "2025-12-31T23:59:59.000000Z",
      "created_at": "2024-01-10T00:00:00.000000Z",
      "updated_at": "2024-01-10T00:00:00.000000Z"
    }
  ],
  "per_page": 15,
  "total": 2
}

Observação: O token em si (plain text) não é retornado por segurança. Apenas os metadados.

Atualizar Tokens

Endpoint

PUT /api/v1/client/{client}/tokens/{tokenId}

O que pode ser atualizado

  • ✅ Nome (name)
  • ✅ Escopos (scopes)
  • ✅ Status (status: active ou inactive)
  • Não é possível alterar o token em si

Exemplo 1: Renomear Token

curl -X PUT "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Novo Nome do Token"
  }'

Exemplo 2: Adicionar Permissões

curl -X PUT "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": ["document:read", "document:create", "document:update"]
  }'

Exemplo 3: Desativar Token Temporariamente

curl -X PUT "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "inactive"
  }'

Exemplo 4: Atualizar Regras Granulares

curl -X PUT "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {SEU_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": {
      "permissions": ["document:read"],
      "document_rules": [
        {
          "environment": "production",
          "context": "invoices",
          "permissions": ["document:update"]
        }
      ]
    }
  }'

Resposta (200 OK)

{
  "id": "9fc42fe9-1234-5678-9abc-def123456789",
  "name": "Novo Nome do Token",
  "scopes": { /* escopos atualizados */ },
  "status": "active",
  ...
}

Revogar Tokens

Endpoint

DELETE /api/v1/client/{client}/tokens/{tokenId}

Exemplo

curl -X DELETE "https://document-hub-api-xp.wake.tech/api/v1/client/{CLIENT_ID}/tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {SEU_TOKEN}"

Resposta (204 No Content)

Sem corpo de resposta. O token é permanentemente deletado.

Quando Revogar?

  • 🔴 Token comprometido ou vazado
  • 🔴 Aplicação ou serviço desativado
  • 🔴 Troca de equipe/funcionário
  • 🔴 Token não usado há muito tempo
  • 🔴 Migração para novo token

Alternativa: Desativar Temporariamente

Se você não quer deletar permanentemente, use status: inactive:

curl -X PUT "https://api.../tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {TOKEN}" \
  -d '{"status": "inactive"}'

Casos de Uso Avançados

Caso 1: Token para Aplicação Mobile (Somente Leitura)

{
  "name": "App Mobile - Visualização",
  "scopes": ["document:read"],
  "expires_at": "2025-12-31T23:59:59Z"
}

Caso 2: Token para Backend (CRUD Completo)

{
  "name": "Backend API",
  "scopes": {
    "permissions": [
      "document:create",
      "document:read",
      "document:update",
      "document:delete",
      "webhook:manage"
    ]
  }
}

Caso 3: Token para Integração Específica

{
  "name": "Integração ERP - Faturas",
  "scopes": {
    "document_rules": [
      {
        "environment": "production",
        "context": "invoices",
        "permissions": ["document:create", "document:read"]
      }
    ]
  },
  "expires_at": "2025-03-31T23:59:59Z"
}

Caso 4: Token Multi-Ambiente para Testes

{
  "name": "QA Team Token",
  "scopes": {
    "document_rules": [
      {
        "environment": "staging",
        "permissions": ["document:create", "document:read", "document:update", "document:delete"]
      },
      {
        "environment": "testing",
        "permissions": ["document:create", "document:read", "document:update", "document:delete"]
      }
    ]
  }
}

Caso 5: Token de Auditoria (Somente Leitura + Histórico)

{
  "name": "Token de Auditoria",
  "scopes": {
    "permissions": ["document:read"]
  }
}

Caso 6: Token para Logs (Criar e Ler Apenas Logs)

{
  "name": "Logger Service",
  "scopes": {
    "document_rules": [
      {
        "type": "logs",
        "permissions": ["document:create", "document:read"]
      }
    ]
  }
}

Boas Práticas de Segurança

1. Princípio do Menor Privilégio

Conceda apenas as permissões necessárias

// ✅ Bom: Token específico para leitura de invoices
{
  "name": "Invoice Reader",
  "scopes": {
    "document_rules": [
      {
        "context": "invoices",
        "permissions": ["document:read"]
      }
    ]
  }
}

// ❌ Ruim: Token com acesso total desnecessário
{
  "name": "Invoice Reader",
  "scopes": [
    "document:create",
    "document:read",
    "document:update",
    "document:delete",
    "token:manage",
    "webhook:manage"
  ]
}

2. Use Nomes Descritivos

// ✅ Bom
"name": "Integração ERP - Faturas Produção - Somente Leitura"

// ❌ Ruim
"name": "Token 1"

3. Defina Datas de Expiração

// ✅ Bom: Token expira
"expires_at": "2025-12-31T23:59:59Z"

// ⚠️ Aceitável: Tokens administrativos podem não expirar
"expires_at": null

4. Rotação Regular de Tokens

  • 🔄 Tokens de aplicação: Renovar a cada 6-12 meses
  • 🔄 Tokens temporários: Renovar mensalmente
  • 🔄 Tokens de integração: Renovar trimestralmente

5. Nunca Compartilhe Tokens

  • ❌ Não coloque tokens em repositórios Git
  • ❌ Não envie tokens por email/chat
  • ✅ Use variáveis de ambiente
  • ✅ Use secrets managers (AWS Secrets, HashiCorp Vault)

6. Monitore Uso de Tokens

# Liste tokens e veja last_used_at
curl -X GET "https://api.../tokens" \
  -H "Authorization: Bearer {TOKEN}"

# Revogue tokens não usados há muito tempo

7. Use Diferentes Tokens por Ambiente

✅ Boa prática:
- Token A: production only
- Token B: staging only
- Token C: development/testing

❌ Má prática:
- Token único para todos os ambientes

8. Armazene Tokens com Segurança

# ✅ Bom: Variável de ambiente
export DOCUMENT_HUB_TOKEN="9fc42fe9-1234|abcdef..."

# ✅ Bom: Arquivo de configuração (.gitignore)
# config/secrets.yml (não versionado)

# ❌ Ruim: Hardcoded no código
const token = "9fc42fe9-1234|abcdef...";

9. Revogue Tokens Comprometidos Imediatamente

# Se um token vazou, revogue IMEDIATAMENTE
curl -X DELETE "https://api.../tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {OUTRO_TOKEN_ADMIN}"

# E crie um novo
curl -X POST "https://api.../tokens" ...

10. Use HTTPS Sempre

  • Sempre use HTTPS em produção
  • ❌ Nunca use HTTP (tokens trafegam em plain text)

Troubleshooting

Erro 401 - Unauthorized

Sintomas:

{
  "error": "Unauthorized",
  "message": "Invalid or missing token"
}

Causas:

  1. Token inválido ou malformado
  2. Token expirado
  3. Token revogado/deletado
  4. Header de autenticação incorreto

Soluções:

# Verifique o formato
Authorization: Bearer {TOKEN_ID}|{PLAIN_TEXT_TOKEN}

# Verifique se o token existe
curl -X GET "https://api.../tokens" \
  -H "Authorization: Bearer {OUTRO_TOKEN}"

# Crie um novo token se necessário

Erro 403 - Forbidden

Sintomas:

{
  "error": "Forbidden",
  "message": "Insufficient permissions"
}

Causas:

  1. Token válido mas sem o escopo necessário
  2. Regras granulares não correspondem

Soluções:

# Verifique os escopos do token
curl -X GET "https://api.../tokens" \
  -H "Authorization: Bearer {TOKEN}"

# Atualize o token com os escopos corretos
curl -X PUT "https://api.../tokens/{TOKEN_ID}" \
  -H "Authorization: Bearer {ADMIN_TOKEN}" \
  -d '{"scopes": ["document:read", "document:create"]}'

# Ou crie um novo token

Token Não Aparece na Listagem

Causa: Token foi deletado ou pertence a outro cliente

Solução: Verifique o client_id correto