Autenticação e Segurança
O acesso à Web API Aéreo é protegido por uma camada dupla de credenciais: Segurança de Desenvolvedor (via cabeçalhos HTTP) e Identificação de Agência (no corpo das requisições).
1. Cabeçalhos HTTP Obrigatórios
Todas as chamadas à API devem incluir os seguintes headers:
| Header | Tipo | Descrição |
|---|---|---|
developer-token | String (GUID) | Identificador único do desenvolvedor fornecido pela Wooba. |
developer-access-code | String (Base64) | Código de acesso diário criptografado em RSA (PKCS#1 v1.5) com a data atual. |
Content-Type | String | application/json |
Accept | String | application/json |
2. Como Gerar o developer-access-code
O developer-access-code deve ser recalculado diariamente para garantir a segurança da chamada:
- Mensagem original: Combine sua chave secreta com a data atual (formato
DD/MM/AAAA), separadas por pipe|:Exemplo:{CHAVE_SECRETA_DO_DESENVOLVEDOR}|{DD/MM/AAAA}MINHA_CHAVE_SECRETA_123|15/10/2026 - Criptografia RSA: Criptografe essa string utilizando a Chave Pública RSA fornecida pela Wooba, obrigatoriamente no modo de preenchimento PKCS#1 v1.5.
- Conversão Base64: Converta o array de bytes criptografado para uma string em Base64.
3. Implementação da Criptografia em Diferentes Linguagens
A chave pública é fornecida no formato XML padrão da Wooba:
<RSAKeyValue>
<Modulus>m3Kx...==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
3.1 Exemplo em C# (.NET)
using System;
using System.Security.Cryptography;
using System.Text;
public static class WoobaAuth
{
public static string GerarDeveloperAccessCode(string secretKey, string publicKeyXml)
{
string dataHoje = DateTime.UtcNow.ToString("dd/MM/yyyy");
string texto = $"{secretKey}|{dataHoje}";
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(publicKeyXml);
byte[] dados = Encoding.UTF8.GetBytes(texto);
// Segundo parâmetro = false ativa o modo PKCS#1 v1.5
byte[] criptografado = rsa.Encrypt(dados, false);
return Convert.ToBase64String(criptografado);
}
}
}
3.2 Exemplo em Node.js (JavaScript / TypeScript)
const crypto = require('crypto');
function xmlKeyToPem(xmlKey) {
const modulusMatch = xmlKey.match(/<Modulus>(.*?)<\/Modulus>/);
const exponentMatch = xmlKey.match(/<Exponent>(.*?)<\/Exponent>/);
if (!modulusMatch || !exponentMatch) throw new Error('Chave XML inválida');
const modulus = Buffer.from(modulusMatch[1], 'base64');
const exponent = Buffer.from(exponentMatch[1], 'base64');
return crypto.createPublicKey({
key: {
kty: 'RSA',
n: modulus.toString('base64url'),
e: exponent.toString('base64url'),
},
format: 'jwk',
});
}
function gerarDeveloperAccessCode(secretKey, publicKeyXml) {
const hoje = new Date();
const dia = String(hoje.getUTCDate()).padStart(2, '0');
const mes = String(hoje.getUTCMonth() + 1).padStart(2, '0');
const ano = hoje.getUTCFullYear();
const texto = `${secretKey}|${dia}/${mes}/${ano}`;
const publicKey = xmlKeyToPem(publicKeyXml);
const buffer = Buffer.from(texto, 'utf8');
const encrypted = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
buffer
);
return encrypted.toString('base64');
}
module.exports = { gerarDeveloperAccessCode };
3.3 Exemplo em Python (3.x)
from datetime import datetime
import base64
import xml.etree.ElementTree as ET
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
def xml_to_rsa_key(xml_str):
root = ET.fromstring(xml_str)
modulus = int.from_bytes(base64.b64decode(root.find('Modulus').text), 'big')
exponent = int.from_bytes(base64.b64decode(root.find('Exponent').text), 'big')
return RSAPublicNumbers(exponent, modulus).public_key()
def gerar_developer_access_code(secret_key: str, xml_public_key: str) -> str:
data_hoje = datetime.utcnow().strftime('%d/%m/%Y')
mensagem = f"{secret_key}|{data_hoje}".encode('utf-8')
public_key = xml_to_rsa_key(xml_public_key)
criptografado = public_key.encrypt(mensagem, padding.PKCS1v15())
return base64.b64encode(criptografado).decode('utf-8')
4. Credenciais Operacionais da Agência (No Request Body)
Cada chamada à API é independente (stateless) e deve identificar a agência/usuário responsável pela transação dentro do JSON:
{
"Login": "AGENCIA_OPERACIONAL_LOGIN",
"Senha": "SENHA_OPERACIONAL",
"ClienteId": null,
"transacaoId": "guid-opcional-para-rastreabilidade"
}
Login(Obrigatório, String): Identificador da agência/usuário cadastrado no Travellink.Senha(Obrigatório, String): Senha da agência.ClienteId(Opcional, Int): ID de subcliente/empresa corporativa (para agências corporativas com múltiplos clientes).transacaoId(Opcional, String): Identificador único gerado pelo seu sistema para rastreamento de logs e auditoria ponta a ponta.