Sistema de Tokens e Autenticação
📚 Índice
- Introdução
- Como Funciona a Autenticação
- Estrutura do Token
- Escopos Disponíveis
- Escopos Simples vs Granulares
- Criar Tokens
- Listar Tokens
- Atualizar Tokens
- Revogar Tokens
- Casos de Uso Avançados
- Boas Práticas de Segurança
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|abcdef1234567890abcdef1234567890abcdef12Mé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: abcdef1234567890abcdef1234567890abcdef12Estrutura 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
| Campo | Tipo | Descrição |
|---|---|---|
id | UUID | Identificador único do token |
client_id | UUID | ID do cliente proprietário |
name | string | Nome descritivo do token |
scopes | object/array | Permissões do token |
status | enum | active ou inactive |
last_used_at | timestamp | Última vez que o token foi usado |
expires_at | timestamp | Data de expiração (null = nunca expira) |
created_at | timestamp | Data de criação |
updated_at | timestamp | Data da última atualização |
Estados do Token
| Status | Descrição | Comportamento |
|---|---|---|
active | Token ativo | ✅ Pode ser usado |
inactive | Token desativado | ❌ Bloqueado temporariamente |
| Expirado | expires_at < now | ❌ Não pode ser usado |
| Deletado | Removido do banco | ❌ Não existe mais |
Escopos Disponíveis
Lista Completa de Escopos
| Escopo | Permite | Necessário Para |
|---|---|---|
document:create | Criar novos documentos | POST /.../{key}, POST .../bulk |
document:read | Ler documentos e histórico | GET /.../{key}, GET .../history |
document:update | Atualizar documentos | PUT /.../{key}, POST .../restore |
document:delete | Deletar documentos | DELETE /.../{key} |
schema:manage | Gerenciar schemas JSON | POST/GET/DELETE .../schema |
webhook:manage | Gerenciar webhooks | POST/GET/PUT/DELETE /webhooks |
token:manage | Gerenciar tokens de acesso | POST/GET/PUT/DELETE /tokens |
Matriz de Permissões
| Endpoint | Método | Escopo Necessário |
|---|---|---|
/context/{ctx}/type/{type}/{key} | POST | document:create |
/context/{ctx}/type/{type}/{key} | GET | document:read |
/context/{ctx}/type/{type}/{key} | PUT | document:update |
/context/{ctx}/type/{type}/{key} | DELETE | document:delete |
/context/{ctx}/type/{type} | GET | document:read |
/context/{ctx}/type/{type}/bulk | POST | document:create + document:update |
/context/{ctx}/type/{type}/{key}/history | GET | document:read |
/context/{ctx}/type/{type}/{key}/history/{id}/restore | POST | document:update |
/context/{ctx}/type/{type}/schema | POST | schema:manage |
/context/\{ctx}/type/\{type}/schema | GET | schema:manage |
/context/\{ctx}/type/\{type}/schema | DELETE | schema:manage |
/webhooks | GET/POST/PUT/DELETE | webhook:manage |
/tokens | GET/POST/PUT/DELETE | token: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
scopes| Campo | Tipo | Descrição |
|---|---|---|
permissions | array | Permissões globais aplicadas a todos os recursos |
document_rules | array | Lista de regras específicas para documentos |
Campos de uma document_rule
document_rule| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
environment | string | Não | Aplica-se apenas a este environment |
context | string | Não | Aplica-se apenas a este context |
type | string | Não | Aplica-se apenas a este type |
permissions | array | Sim | Permissões concedidas quando a regra corresponde |
Como as Regras Funcionam
Uma requisição é autorizada se:
- A permissão necessária está em
permissionsOU - 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}/tokensAutenticaçã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"
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | Sim | Nome descritivo do token |
scopes | array/object | Sim | Permissões (simples ou granular) |
expires_at | timestamp | Não | Data 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}/tokensExemplo
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:activeouinactive) - ❌ 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": null4. 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 tempo7. 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:
- Token inválido ou malformado
- Token expirado
- Token revogado/deletado
- 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árioErro 403 - Forbidden
Sintomas:
{
"error": "Forbidden",
"message": "Insufficient permissions"
}Causas:
- Token válido mas sem o escopo necessário
- 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 tokenToken Não Aparece na Listagem
Causa: Token foi deletado ou pertence a outro cliente
Solução: Verifique o client_id correto
Updated 6 days ago
