# Autenticação

A API Dinie usa **OAuth2 Client Credentials** para autenticação. Você troca suas credenciais de API por um access token de curta duração e inclui esse token em todas as requisições.

![Fluxo de autenticação](/assets/authentication-flow.6c3c206755e11f84fe4d92a01c3ac0c0e30371c13201e70183db34b69429b342.53e9d79f.svg)

## Visão Geral

1. Seu servidor envia as credenciais para `POST /auth/token`
2. A Dinie retorna um access token JWT de curta duração
3. Inclua o token como header Bearer em todas as requisições subsequentes


## Obtendo um Token

Envie uma requisição `POST` com seu `client_id` e `client_secret` via HTTP Basic Authentication:

```typescript Node.js
import Dinie from "dinie";

// O SDK troca as credenciais e faz cache do token automaticamente.
const dinie = new Dinie({
  clientId: process.env.DINIE_CLIENT_ID,
  clientSecret: process.env.DINIE_CLIENT_SECRET,
  environment: "sandbox",
});
```

```ruby Ruby
require "dinie"

# O SDK troca as credenciais e faz cache do token automaticamente.
dinie = Dinie::Client.new(
  client_id: ENV["DINIE_CLIENT_ID"],
  client_secret: ENV["DINIE_CLIENT_SECRET"],
  environment: "sandbox"
)
```

```python Python
from dinie import Dinie

# O SDK troca as credenciais e faz cache do token automaticamente.
dinie = Dinie(
    client_id=os.environ["DINIE_CLIENT_ID"],
    client_secret=os.environ["DINIE_CLIENT_SECRET"],
    environment="sandbox",
)
```

```bash cURL
curl -X POST https://api-dev.dinietech.io/api/v3/auth/token \
  -u "dinie_ci_your_client_id:dinie_cs_live_your_client_secret" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials"
```

### Resposta

```json
{
  "access_token": "dinie_at_...",
  "token_type": "bearer",
  "expires_in": 3600
}
```

| Campo | Descrição |
|  --- | --- |
| `access_token` | Token JWT com prefixo `dinie_at_`. Use como Bearer token. |
| `token_type` | Sempre `bearer`. |
| `expires_in` | Tempo de vida do token em segundos (3600 = 1 hora). |


## Usando Bearer Tokens

Inclua o token no header `Authorization` de cada requisição à API:

```bash
Authorization: Bearer dinie_at_...
```

> **Warning:** Nunca inclua credenciais ou tokens em URLs ou parâmetros de query. Sempre use o header `Authorization`.


## Expiração e Renovação de Token

Os tokens expiram após **1 hora** (3600 segundos). Quando um token expira, a API retorna um erro `401`:

```json
{
  "type": "https://docs.dinie.com/errors/authentication-failed",
  "title": "Authentication Failed",
  "status": 401,
  "detail": "Bearer token has expired.",
  "code": "token_expired"
}
```

Para lidar com a expiração:

1. **Renovação proativa** -- acompanhe o `expires_in` e solicite um novo token antes de expirar (ex.: a 80% do TTL)
2. **Renovação reativa** -- capture respostas `401` com código `token_expired`, solicite um novo token e reenvie a requisição


> **Tip:** Os SDKs gerenciam a renovação de token automaticamente. Eles solicitam um novo token antes que o atual expire e reenviam requisições que falharam de forma transparente.


## Autenticação Automática dos SDKs

Quando você inicializa o SDK com suas credenciais, ele gerencia todo o ciclo de vida do token:

- Troca as credenciais por um token na primeira chamada à API
- Armazena o token em cache na memória
- Renova o token antes da expiração
- Reenvia requisições que falharam devido a tokens expirados


Você nunca precisa chamar o endpoint de token diretamente ao usar um SDK.

## Erros de Autenticação

| Status | Código | Descrição |
|  --- | --- | --- |
| 400 | `missing_grant_type` | Parâmetro `grant_type` ausente |
| 400 | `unsupported_grant_type` | `grant_type` não é `client_credentials` |
| 400 | `missing_authorization` | Header `Authorization` ausente |
| 400 | `invalid_content_type` | Header `Content-Type` não é `application/x-www-form-urlencoded` |
| 401 | `invalid_client` | `client_id` não existe |
| 401 | `invalid_client_secret` | `client_secret` não confere |
| 401 | `credential_revoked` | Credencial foi revogada |
| 401 | `credential_expired` | Credencial passou da data de expiração |


## Boas Práticas de Segurança

1. **Armazene secrets com segurança** -- use variáveis de ambiente ou um gerenciador de secrets. Nunca faça commit de credenciais no controle de versão.
2. **Use apenas chamadas server-side** -- nunca exponha seu `client_secret` em código frontend, aplicativos mobile ou JavaScript client-side.
3. **Rotacione credenciais regularmente** -- crie uma nova credencial, atualize sua integração e depois revogue a antiga. Veja a seção [Gerenciamento de Credenciais](#gerenciamento-de-credenciais) abaixo.
4. **Separe credenciais por ambiente** -- use credenciais diferentes para sandbox e produção.
5. **Monitore o uso** -- verifique o campo `last_used_at` nas suas credenciais para detectar uso não autorizado.


# Gerenciamento de Credenciais

Parceiros podem ter **múltiplas credenciais de API ativas** simultaneamente. Isso permite rotação de credenciais sem downtime -- crie uma nova credencial, atualize sua integração e depois revogue a antiga.

## Criando Novas Credenciais

Crie uma nova API key com um nome legível e uma data de expiração opcional.

```typescript Node.js
const credential = await dinie.auth.credentials.create({
  name: "Production Key",
  expires_at: 1804118400,
});

// Armazene credential.client_secret com segurança -- ele é exibido apenas uma vez.
console.log(credential.client_id);
console.log(credential.client_secret);
```

```ruby Ruby
credential = dinie.auth.credentials.create(
  name: "Production Key",
  expires_at: 1804118400
)

# Armazene credential.client_secret com segurança -- ele é exibido apenas uma vez.
puts credential.client_id
puts credential.client_secret
```

```python Python
credential = dinie.auth.credentials.create(
    name="Production Key",
    expires_at=1804118400,
)

# Armazene credential.client_secret com segurança -- ele é exibido apenas uma vez.
print(credential.client_id)
print(credential.client_secret)
```

```bash cURL
curl -X POST https://api-dev.dinietech.io/api/v3/auth/credentials \
  -H "Authorization: Bearer dinie_at_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: create-prod-key-2026" \
  -d '{
    "name": "Production Key",
    "expires_at": 1804118400
  }'
```

### Resposta

```json
{
  "id": "dinie_ci_550e8400e29b41d4a716446655440000",
  "client_id": "dinie_ci_550e8400e29b41d4a716446655440000",
  "client_secret": "dinie_cs_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "name": "Production Key",
  "status": "active",
  "expires_at": 1804118400,
  "created_at": 1709546400,
  "updated_at": 1709546400,
  "last_used_at": null
}
```

> **Warning:** O `client_secret` é **retornado apenas uma vez** no momento da criação. Armazene-o imediatamente no seu gerenciador de secrets. Ele não pode ser recuperado novamente.


| Campo | Obrigatório | Descrição |
|  --- | --- | --- |
| `name` | Sim | Label legível para esta chave (ex.: "Production Key", "Staging Key") |
| `expires_at` | Não | Data de expiração opcional (Unix epoch, segundos). `null` significa que a credencial nunca expira. |


## Listando Credenciais

Recupere todas as credenciais da sua conta. Os secrets nunca são incluídos nas respostas de listagem.

```typescript Node.js
const credentials = await dinie.auth.credentials.list();

for (const cred of credentials.data) {
  console.log(cred.name, cred.status, cred.last_used_at);
}
```

```ruby Ruby
credentials = dinie.auth.credentials.list

credentials.data.each do |cred|
  puts "#{cred.name} #{cred.status} #{cred.last_used_at}"
end
```

```python Python
credentials = dinie.auth.credentials.list()

for cred in credentials.data:
    print(cred.name, cred.status, cred.last_used_at)
```

```bash cURL
curl https://api-dev.dinietech.io/api/v3/auth/credentials \
  -H "Authorization: Bearer dinie_at_..."
```

### Resposta da listagem

```json
{
  "data": [
    {
      "id": "dinie_ci_550e8400e29b41d4a716446655440000",
      "client_id": "dinie_ci_550e8400e29b41d4a716446655440000",
      "name": "Production Key",
      "status": "active",
      "expires_at": 1804118400,
      "created_at": 1709287200,
      "updated_at": 1709287200,
      "last_used_at": 1709544600
    }
  ],
  "has_more": false
}
```

## Revogando Credenciais

Revogue uma credencial pelo `client_id`. A revogação tem efeito imediato -- nenhum novo token pode ser emitido com essa credencial. Tokens já emitidos continuam funcionando até expirarem (até 1 hora).

```typescript Node.js
await dinie.auth.credentials.del("dinie_ci_550e8400e29b41d4a716446655440000");
```

```ruby Ruby
dinie.auth.credentials.delete("dinie_ci_550e8400e29b41d4a716446655440000")
```

```python Python
dinie.auth.credentials.delete("dinie_ci_550e8400e29b41d4a716446655440000")
```

```bash cURL
curl -X DELETE https://api-dev.dinietech.io/api/v3/auth/credentials/dinie_ci_550e8400e29b41d4a716446655440000 \
  -H "Authorization: Bearer dinie_at_..."
```

A resposta é `204 No Content` com corpo vazio.

> **Info:** Você não pode revogar sua **última credencial ativa**. Um parceiro deve sempre ter pelo menos uma credencial ativa. Tentar revogar a última retorna um `409 Conflict` com código `last_active_credential`.


## Boas Práticas de Rotação

Siga este processo para rotação de credenciais sem downtime:

1. **Crie uma nova credencial** com `POST /auth/credentials`
2. **Atualize sua integração** para usar o novo `client_id` e `client_secret`
3. **Verifique se a nova credencial funciona** solicitando um token com `POST /auth/token`
4. **Revogue a credencial antiga** com `DELETE /auth/credentials/{old_client_id}`


Durante os passos 1--4, **todas as credenciais ativas funcionam simultaneamente**. Não há downtime na autenticação.

> **Tip:** Defina um `expires_at` nas credenciais como rede de segurança. Mesmo que você esqueça de revogar uma credencial antiga, ela vai parar de funcionar após a data de expiração.


### Frequência Recomendada de Rotação

| Ambiente | Frequência de Rotação |
|  --- | --- |
| Produção | A cada 90 dias |
| Staging | A cada 30 dias ou sob demanda |


Monitore o campo `last_used_at` para detectar credenciais que não estão mais em uso e devem ser revogadas.