Árvore Docs Guia

Autenticação e segurança

Os endpoints destinados a parceiros têm as requisições validadas através do uso de um token de segurança. A montagem deste token acontece no lado do cliente.

O token deve ser utilizado em toda requisição, dado que ele leva em consideração a rota, o horário atual e o payload da requisição.

Obtendo suas credenciais

Para montar o token, obtenha suas credenciais com o time da Árvore. As credenciais para realizar a autenticação na API são compostas de uma access_key e uma access_key_id.

Geração do token

O token é uma junção de cinco valores, separados por vírgula, codificados via HMAC com o algoritmo SHA1, e posteriormente codificado via Base64.

Os valores que compõem o token são:

  1. Verbo HTTP utilizado na requisição. Ex: POST.
  2. Tipo de conteúdo utilizado na requisição. Ex: application/json.
  3. Hash do corpo da requisição, codificado via Base64. Ex: 7JnmJ3nblefY67zpbKm9rg==.
  4. Rota da requisição. Ex: /api/v2/partners/readers.
  5. Horário atual, no formato strftime com os códigos "%a, %d %b %Y %H:%M:%S GMT". Ex: Wed, 16 Sep 2024 14:14:58 GMT.

Obtendo o hash do corpo da requisição

Caso a requisição não tenha um corpo, o content será uma string vazia. Se não, o content será o hash do corpo da requisição, codificado via Base64:

  1. Obtenha o JSON que será o body da requisição. Ex: {"key": "value"}.
  2. Em seguida, transforme o body utilizando o algoritmo MD5. Ex: 850d491f70778cfa331cede127b37013.
  3. Por fim, codifique o hash obtido no passo anterior via Base64. Ex: 7JnmJ3nblefY67zpbKm9rg==.

Montagem do token

  1. Junte os valores acima na ordem em que foram declarados por vírgula. Ex: {http_verb},{content_type},{body_hash},{request_uri},{current_time}. O valor resultante dessa junção será daqui em diante chamado de canonical_string.
  2. Codifique a canonical_string via HMAC com o algoritmo SHA1 usando a access_key como chave.
  3. Codifique o valor acima via Base64.
  4. O valor resultante será daqui em diante chamado de signature.
  5. Por fim, use a signature e o access_key_id para montar um token da seguinte forma: "APIAuth {access_key_id}:{signature}".
  6. O valor gerado nesses passos deve aparecer nos headers da requisição sob a chave Authorization. Ex: Authorization: APIAuth {access_key_id}:{signature}.

Realizando uma requisição

Por fim, a requisição deve ser feita para o endpoint desejado utilizando o header acima calculado, como por exemplo no comando curl abaixo:

curl -i -X POST \
  -H 'Content-Type: application/json' \
  -H 'Content-MD5: 7JnmJ3nblefY67zpbKm9rg==' \
  -H 'DATE: Wed, 16 Sep 2024 14:14:58 GMT' \
  -H 'Authorization: APIAuth <signature>' \
  -d '{}' https://livros.arvore.com.br/api/v2/partners/readers

Repositório com exemplos

Para auxiliar com a autenticação e uso da API, a Árvore mantém um repositório com exemplos em diferentes linguagens. Acesse através do seguinte link:

Exemplo de geração do API Auth em Elixir

Abaixo, um exemplo de como gerar o token de autenticação utilizando Elixir:

access_key = "some_access_key"
access_key_id = "some_access_key_id"

# 1. HTTP Verb
http_verb = "POST"

# 2. Content Type
content_type = "application/json"

# 3. Body Hash
body = "{\"some_key\":\"some value\"}"
md5_content = :crypto.hash(:md5, body)
content =  Base.encode64(md5_content) # 7JnmJ3nblefY67zpbKm9rg==

# 4. Request URI
path = "/api/v2/partners/readers"

# 5. Current Time
date = Timex.format!(DateTime.utc_now(), "%a, %d %b %Y %H:%M:%S GMT", :strftime) # Wed, 16 Sep 2024 14:14:58 GMT

# 6. Canonical String generation
canonical_string = "#{http_verb},#{content_type},#{content},#{path},#{date}"

# 7. Signature generation
sha1_signature = :crypto.mac(:hmac, :sha, access_key, canonical_string)
signature = Base.encode64(sha1_signature)

# 8. Token generation
auth_token = "APIAuth #{access_key_id}:#{signature}"

Exemplo de geração do token em JavaScript

const crypto = require('crypto')
const moment = require('moment')
const https = require('https')

// Setup do access_key
let access_key = 'some access key'
let access_key_id = 'some access key id'

let http_verb = 'GET' // Passo 1
let content_type = 'application/json'; // Passo 2
let body = `{ "some_key": "some value" }`
let content = crypto.createHash('md5').update(body).digest('base64') // Passo 3
let path = '/api/v2/partners/readers/<reference_id>' // Passo 4
let date = moment().utc().format("ddd, DD MMM YYYY HH:mm:ss") + " GMT"; // Passo 5
let canonical_string = [http_verb, content_type, content, path, date].join() // Passo 6
let signature = crypto.createHmac('sha1', access_key).update(canonical_string).digest('base64') // Passo 7
let auth_token = "APIAuth " + access_key_id + ":" + signature; // Passo 8

const options = {
    host: 'livros.arvore.com.br',
    path: path,
    method: 'GET',
    // Estes request headers devem ser passados em todas as
    // chamadas: GET, POST, PUT ou DELETE
    headers: {
        'Content-Type': content_type,
        'Content-MD5': content,
        'DATE': date,
        'Authorization': auth_token
    }
};

const request = https.request(options, (res) => {
    if (res.statusCode !== 200) {
        console.error(`Did not get an OK from the server. Code: ${res.statusCode}`);
        console.error(`Response headers: ${res.headers}`)
        res.resume();
        return;
    };

    let data = '';

    res.on('data', (chunk) => {
        data += chunk;
    });

    res.on('close', () => {
        console.log('Retrieved all data');
        console.log(JSON.parse(data));
    });
});

request.end();

request.on('error', (err) => {
    console.error(`Encountered an error trying to make a request: ${err.message}`);
});