# Autenticação

> Como assinar cada requisição com HMAC-SHA256 no esquema SB1-HMAC-SHA256, com valores de validação e código em Node.js e Python.

URL canônica: https://apis.cortecloud.com.br/docs/comecando/autenticacao/

Toda requisição à API é assinada com HMAC-SHA256 no esquema `SB1-HMAC-SHA256`. Não há login, sessão, cookie nem token para renovar: a mesma secret key assina todas as chamadas até ser trocada.

## Credenciais {#credenciais}

A Serrabits fornece à sua integração duas credenciais por ambiente. Para obtê-las, escreva para suporte@serrabits.com.br.

| Credencial | Uso |
| --- | --- |
| **api key** | Identifica a sua integração. Vai no cabeçalho `Authorization` de toda requisição. |
| **secret key** | Gera a assinatura de cada requisição. Nunca é enviada: só a assinatura vai na requisição. |

A secret key deve ficar apenas no seu backend. Não a coloque em código de frontend, app mobile ou qualquer lugar acessível pelo navegador do usuário: quem tiver a secret key consegue fazer chamadas em nome da sua integração. Se ela vazar, escreva para suporte@serrabits.com.br para revogá-la e receber uma nova.

## Cabeçalhos {#cabecalhos}

Toda requisição leva dois cabeçalhos:

```text
Authorization: SB1-HMAC-SHA256 api-key="<api key>", signed-headers="host", signature="<assinatura>"
x-company-internal-code: <código da central>
```

- `Authorization` identifica a sua integração e traz a assinatura da requisição, calculada como descrito abaixo.
- `x-company-internal-code` indica a central sobre a qual a requisição atua (ver [código da central](https://apis.cortecloud.com.br/docs/comecando/conceitos.md#codigo-da-central)). Esse cabeçalho não entra na assinatura.

Requisições com corpo também enviam `Content-Type: application/json`.

A assinatura cobre método, caminho, query string, host e corpo. Alterar qualquer um deles depois de assinar invalida a requisição, e uma requisição capturada não pode ser reaproveitada com outro caminho, método ou corpo.

## Como calcular a assinatura {#como-calcular-a-assinatura}

1. Monte o texto a ser assinado, com uma informação por linha, separadas por `\n` (quebra de linha real, não os caracteres `\` e `n`):
   1. método HTTP em maiúsculas (`GET`, `POST`, `PUT`, `PATCH`);
   2. caminho da URL, sem host nem query string (`/materials/boards`); caminho vazio vale `/`;
   3. parâmetros da query string ordenados por nome (e, em nomes repetidos, por valor), cada um como `nome=valor` codificado como no `encodeURIComponent` do JavaScript, unidos por `&`; sem query string, linha vazia;
   4. `host:<host>` seguido de uma linha em branco, onde `<host>` é exatamente o valor que a sua biblioteca HTTP envia no cabeçalho `host` (o hostname, mais `:porta` só quando a porta não é a padrão do protocolo);
   5. a palavra `host`, que é a lista de cabeçalhos assinados;
   6. SHA-256 em hexadecimal dos bytes do corpo; sem corpo, o SHA-256 da string vazia (`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`).
2. Calcule o HMAC-SHA256 desse texto com a secret key como chave.
3. Use o resultado em hexadecimal minúsculo como `<assinatura>`.

A linha em branco depois de `host:<host>` faz parte do texto: a linha do host termina com a sua própria quebra de linha, somada ao `\n` que separa as partes.

### Exemplo: GET {#exemplo-get}

Texto assinado para `GET /materials/boards?limit=10` no host `api.example.com`, sem corpo:

```text
GET
/materials/boards
limit=10
host:api.example.com

host
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

### Exemplo: POST {#exemplo-post}

Texto assinado para `POST /services/123/production` no host `api.example.com`, com o corpo `{"sellerEmail":"vendedor@example.com"}`. O hash é calculado sobre os bytes exatos do corpo enviado: reformatar o JSON depois de assinar (espaços, quebras de linha, ordem das chaves) invalida a assinatura.

```text
POST
/services/123/production

host:api.example.com

host
d4f22123a7a0685bd98bb9f7fc9b3db455e5b4e0c4420f0e0ddedd4826a3e0cc
```

Sem query string, a terceira linha fica vazia.

### Valide a sua implementação {#valide-a-sua-implementacao}

Com a secret key de teste `sb_sk_exemplo`, os dois textos acima produzem estas assinaturas:

| Exemplo | Assinatura |
| --- | --- |
| GET | `e2a240cea084ff4950cc78e1d48d6da563aae9f7935a8dfaa28c3672598ef344` |
| POST | `8ac32656f8233168640afaff2f7c6213e42dd0fb9cdbd3f81b2ae666cab94e2b` |

Se a sua implementação chega nos mesmos valores, ela está montando o texto do jeito que a API espera.

## Código de referência {#codigo-de-referencia}

### Node.js {#nodejs}

```js
const crypto = require('crypto');

function canonicalQueryString(url) {
  const entries = [...url.searchParams.entries()].sort(([ak, av], [bk, bv]) =>
    ak === bk ? av.localeCompare(bv) : ak.localeCompare(bk),
  );
  return entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
}

function buildCanonicalRequest(method, url, body = '') {
  const payloadHash = crypto.createHash('sha256').update(body, 'utf-8').digest('hex');
  return [
    method.toUpperCase(),
    url.pathname || '/',
    canonicalQueryString(url),
    `host:${url.host}\n`,
    'host',
    payloadHash,
  ].join('\n');
}

const url = new URL('https://api.example.com/materials/boards?limit=10');
const canonicalRequest = buildCanonicalRequest('GET', url);
const signature = crypto.createHmac('sha256', secretKey).update(canonicalRequest).digest('hex');

const authorization = `SB1-HMAC-SHA256 api-key="${apiKey}", signed-headers="host", signature="${signature}"`;
```

### Python {#python}

```python
# urllib.parse.quote com safe="!*'()" reproduz o mesmo conjunto
# de caracteres que o encodeURIComponent do JavaScript deixa sem escapar.
import hashlib
import hmac
import urllib.parse


def encode(value):
    return urllib.parse.quote(str(value), safe="!*'()")


def canonical_query_string(query_pairs):
    return "&".join(
        f"{encode(k)}={encode(v)}"
        for k, v in sorted(query_pairs, key=lambda kv: (kv[0], kv[1]))
    )


def build_canonical_request(method, path, query_pairs, host, body=b""):
    payload_hash = hashlib.sha256(body).hexdigest()
    return "\n".join([
        method.upper(),
        path or "/",
        canonical_query_string(query_pairs),
        f"host:{host}\n",
        "host",
        payload_hash,
    ])


canonical_request = build_canonical_request("GET", "/materials/boards", [("limit", "10")], "api.example.com")
signature = hmac.new(secret_key.encode(), canonical_request.encode(), hashlib.sha256).hexdigest()

authorization = f'SB1-HMAC-SHA256 api-key="{api_key}", signed-headers="host", signature="{signature}"'
```

Não defina o cabeçalho `host` manualmente na chamada HTTP: a biblioteca já o envia a partir da URL. Garanta só que o valor usado para assinar (`url.host` e `host` nos exemplos) seja exatamente o que ela envia; na maioria das bibliotecas, é o mesmo.

Para requisições com corpo, assine e envie os mesmos bytes. Serialize o JSON uma vez, guarde a string e use-a tanto no hash quanto no corpo da requisição.

## Quando a autenticação falha {#quando-a-autenticacao-falha}

A API responde `401` quando:

- `Authorization` ou `x-company-internal-code` falta ou está malformado;
- a api key é desconhecida ou foi revogada;
- a central indicada em `x-company-internal-code` não está vinculada à api key ou não está ativa no Cortecloud;
- `signed-headers` nomeia um cabeçalho que a requisição não enviou;
- a assinatura não confere com a requisição recebida.

Para diagnosticar uma assinatura que não confere, compare o texto que você assinou com o formato acima, linha a linha: as causas mais comuns são a falta da linha em branco depois do host, a query string fora de ordem e o corpo reformatado entre o hash e o envio.
