> For the complete documentation index, see [llms.txt](https://portal-integracao.intelbras.com.br/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://portal-integracao.intelbras.com.br/defense-ia-and-lite/dicas-de-implementacao/implementacao-de-um-token-service.md).

# Implementação de um Token Service

Implementação em Go de um Token Service para o Defense IA: login/keepalive, armazenamento em memória e endpoints "/token" e "/health" com API key e boas práticas de segurança.

### Token Service (Defense IA)

{% embed url="<https://github.com/Integracao-Intelbras-Cftv/Defense-Token-Service.git>" %}

{% hint style="danger" %}
Antes de ir para produção, exponha **/token** e **/health** **apenas via HTTPS** e **restrinja o acesso** (ex.: **mTLS**, **API Key mais completa** e/ou **VPN**).\
Se possível, **não exponha /token**: mantenha-o na **rede privada** e forneça o token **apenas em memória**, com **expiração/rotação**.\
Alternativa mais segura: use um **Secret Manager** (Vault/Cloud) para **entregar e rotacionar** o token sem endpoint público.
{% endhint %}

Este serviço em Go autentica no Defense IA, mantem a sessao ativa e disponibiliza o token atual por um endpoint HTTP local.

Objetivo pratico:

* evitar que outras integrações precisem implementar login/keepalive direto no Defense;
* centralizar token e dados de sessão em um único processo;
* expor token de forma simples e protegida por API key.

### Visao Rapida da Arquitetura

```mermaid
---
config:
  layout: dagre
  look: neo
  theme: neo-dark
---
flowchart RL
 subgraph Auth["Autenticação inicial"]
        A["App"]
        D["Defense IA"]
        T[("Token + Timers")]
  end
 subgraph Loop["Loop de vida (app rodando)"]
    direction TB
        K["PUT   /accounts/keepalive Header: X-Subject-Token+Body: ..."]
        U["POST  /accounts/updateToken Header: X-Subject-Token + signature"]
        S["Swap automático do token global"]
  end
    A -- 2) Second Login (credenciais/encrypt) --> D
    A L_A_D_2@-- 1) First Login (Recebe a chave pública) --> D
    D -- 2) Resposta: token, userId, tokenRate, secretKey --> A
    D L_D_A_2@-- 1) Resposta: params p/ criptografia --> A
    A -- Guarda token + inicializa timers --> T
    T -- "&lt;= 30s" --> K
    K -- 200 Success --> T
    T -- "&lt;= 30min" --> U
    U -- 200 (novo token) --> S
    S --> T
    K L_K_R_0@-. 401 .-> R["401 em qualquer chamada Refaz autenticação (1 + 2) Reinicia timers"]
    U -. 401 .-> R
    R --> A

    style A stroke:#00C853
    style D stroke:#00C853
    style T stroke:#00C853
    style K stroke:#00C853
    style U stroke:#00C853
    style S stroke:#00C853
    style R stroke:#00C853
    style Auth stroke:#00C853,fill:transparent
    style Loop stroke:#00C853,fill:transparent
    linkStyle 0 stroke:#00C853,fill:none
    linkStyle 1 stroke:#00C853,fill:none
    linkStyle 2 stroke:#00C853,fill:none
    linkStyle 3 stroke:#00C853,fill:none
    linkStyle 4 stroke:#00C853,fill:none
    linkStyle 5 stroke:#00C853,fill:none
    linkStyle 6 stroke:#00C853,fill:none
    linkStyle 7 stroke:#00C853,fill:none
    linkStyle 8 stroke:#00C853,fill:none
    linkStyle 9 stroke:#00C853,fill:none
    linkStyle 10 stroke:#00C853,fill:none
    linkStyle 11 stroke:#00C853,fill:none
    linkStyle 12 stroke:#00C853,fill:none

    L_A_D_2@{ animation: fast } 
    L_D_A_2@{ animation: fast } 
    L_K_R_0@{ animation: fast }
```

Componentes principais:

* `cmd/token-service/token-service.go`: ponto de entrada da aplicação.
* `internal/app/token-service.go`: loop de manutenção do token (login + refresh).
* `internal/app/create-token.go`: cria token inicial e salva no store.
* `internal/app/refresh-token.go`: executa keepalive no Defense.
* `internal/app/http-server.go`: endpoints locais `/health` e `/token`.
* `internal/pkg/defense/*`: cliente HTTP e logica de autenticação Defense.
* `internal/pkg/store/store.go`: armazenamento em memoria com lock (`RWMutex`).

### Fluxo de Ponta a Ponta

#### Inicialização da aplicação

No `main`, o serviço carrega configuração, sobe servidor HTTP e inicia o loop do token em goroutine:

```go
kv := store.NewMemoryStore()
app.StartHTTPServer(config.HTTP_ADDR, config.HTTP_API_KEY, kv, lgr)

tokenService := &app.TokenService{
  Config:  config,
  Defense: defense,
  Logger:  lgr,
  Store:   kv,
}
tokenService.SetInfo()
go tokenService.Go()
```

Leitura simplificada:

* o `store` é compartilhado entre manutenção e API HTTP;
* `SetInfo()` prepara dados criptográficos (chaves);
* `Go()` entra no ciclo continuo de login/refresh.

#### Criação de token (login)

Em `create-token.go`, o serviço chama `Auth` e persiste informações essenciais:

```go
res, err := ts.Defense.Auth(user, pass, "")
...
err = ts.Store.SetKey("defenseToken", res.Token)
err = ts.Store.SetKey("defenseCredential", res.Credential)
err = ts.Store.SetKey("defenseTokenUpdatedAt", updatedAt)
```

O que isso garante:

* token atual disponível para consulta HTTP;
* `updatedAt` para rastrear idade da sessão;
* credencial associada ao login também fica disponível internamente.

#### Loop de manutenção do token

No `TokenService.Go()` existe o ciclo principal:

```go
for {
  code, err := ts.createToken(ts.Config.DEFENSE_USER, ts.Config.DEFENSE_PASS)
  ...
  for {
    time.Sleep(DELAY_TO_REFRESH * time.Second)
    token, err := ts.Store.GetKey("defenseToken")
    refreshed, err := ts.refreshToken(token)
    if !refreshed { break }
  }
}
```

Comportamento atual:

* cria token;
* executa keepalive a cada `20s` (`DELAY_TO_REFRESH`);
* se keepalive falhar, sai do loop interno e volta ao login;
* se receber código de credencial inválida (`2001`), fica aguardando alteração de senha no `.env`.

#### Keepalive no Defense (refresh)

O refresh atual usa endpoint de keepalive:

```go
const REFRESH_ENDPOINT = "/brms/api/v1.0/accounts/keepalive"
code, _, err := defenseApi.Put(REFRESH_ENDPOINT, nil)
return code == 200, nil
```

Importante:

* o token vai no header `X-Subject-Token`;
* retorno `200` é tratado como sessão renovada.

#### Endpoint local para consumidores

Em `http-server.go`, `/token` devolve token e timestamp:

```go
token, err := kv.GetKey("defenseToken")
updatedAt, _ := kv.GetKey("defenseTokenUpdatedAt")
json.NewEncoder(w).Encode(tokenResponse{
  Token:     token,
  UpdatedAt: updatedAt,
})
```

Esse endpoint é protegido por API key:

```go
key := strings.TrimSpace(r.Header.Get("X-API-Key"))
if !matchAPIKey(key, apiKey) {
  http.Error(w, "unauthorized", http.StatusUnauthorized)
  return
}
```

Também aceita `Authorization: Bearer <api-key>`.

#### Thread-safe: por quê não há concorrência de dados?

`MemoryStore` usa `sync.RWMutex`:

```go
func (ms *MemoryStore) SetKey(key, value string) error {
  ms.mu.Lock()
  defer ms.mu.Unlock()
  ms.data[key] = value
  return nil
}

func (ms *MemoryStore) GetKey(key string) (string, error) {
  ms.mu.RLock()
  defer ms.mu.RUnlock()
  value, ok := ms.data[key]
  ...
}
```

Na pratica:

* escrita do token acontece de forma ativa e em memória;
* leitura via HTTP não conflita com atualização do loop de manutenção.

#### [Como a assinatura de login é gerada](/defense-ia-and-lite/readme/autenticacao-via-api-passo-a-passo.md)

A assinatura do login é montada em etapas de hash MD5 em `auth.go`:

```go
hash := auth.hash(password)
hash = auth.hash(username + hash)
hash = auth.hash(hash)
hash = auth.hash(username + ":" + realm + ":" + hash)
hash = auth.hash(hash + ":" + randomKey)
```

Esse fluxo usa dados retornados pelo primeiro passo de `authorize` (`realm` e `randomKey`) para autenticar no segundo passo.

### Endpoints do Defense usados

* `POST /brms/api/v1.0/accounts/authorize`
  * usado no handshake de autenticação e login final.
* `PUT /brms/api/v1.0/accounts/keepalive`
  * usado para manter sessão viva.

Endpoint de update:

* `POST /brms/api/v1.0/accounts/updateToken`
  * requer `X-Subject-Token` e body com `signature`.

### Regras de tempo

* keepalive por volta de `30s`;
* update token por volta de `30min` (ou `tokenRate`).

Implementação atual:

* keepalive fixo em `20s` (`DELAY_TO_REFRESH`);
* não executa `updateToken` periódico;
* quando refresh falha, faz re-login completo.

### Variáveis de ambiente

Obrigatórias:

* `DEFENSE_HOST`
* `DEFENSE_PORT`
* `DEFENSE_USERNAME`
* `DEFENSE_PASSWORD`
* `HTTP_API_KEY`

Opcionais:

* `DEFENSE_SCHEME` (padrão `https`)
* `DEFENSE_INSECURE_SKIP_VERIFY` (`true`/`false`)
* `HTTP_ADDR` (padrão `:8080`)
* `ENVIRONMENT` (padrão operacional: `DEVELOPMENT`)

Exemplo (`cmd/token-service/.env.example`):

```env
DEFENSE_HOST="127.0.0.1"
DEFENSE_PORT="80"
DEFENSE_SCHEME="https"
DEFENSE_INSECURE_SKIP_VERIFY="true"
DEFENSE_USERNAME="system"
DEFENSE_PASSWORD=""
HTTP_ADDR=":8080"
HTTP_API_KEY="change-me"
ENVIRONMENT="DEVELOPMENT"
```

### Como executar

```bash
go run ./cmd/token-service
```

Logs esperados:

* servidor HTTP ouvindo (`HTTP server listening on ...`);
* token criado com sucesso;
* logs periódicos de refresh.

### Como consumir o token (usuário final)

#### Healthcheck

```bash
curl http://localhost:8080/health
```

Resposta esperada:

```
ok
```

#### Obter token

```bash
curl -H "X-API-Key: change-me" http://localhost:8080/token
```

Resposta esperada:

```json
{
  "token": "SEU_TOKEN_AQUI",
  "updatedAt": "2026-02-12T12:00:00Z"
}
```

### Chaves internas usadas no store

* `defenseToken`
* `defenseCredential`
* `defenseTokenUpdatedAt`
* `defenseConnection`
* `secretKey`
* `secretVector`

### Erros comuns e diagnóstico rápido

* `unauthorized` ao chamar `/token`
  * causa: API key ausente ou incorreta.
  * ação: validar `HTTP_API_KEY` e header enviado.
* `token not available`
  * causa: token ainda não foi criado.
  * ação: verificar logs de login no startup.
* falhas repetidas em refresh
  * causa: token expirado, conectividade ou credencial invalida.
  * ação: revisar `DEFENSE_HOST/PORT`, senha e certificados.
* mensagem de senha não alterada (`Password haven't Changed`)
  * causa: retorno `2001` no login.
  * ação: atualizar `DEFENSE_PASSWORD` no `.env`.

### Atenção:

* adicionar agendamento dedicado de update (`~30min` ou `tokenRate`);
* aplicar politica de retry unico em `401` para clientes consumidores.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://portal-integracao.intelbras.com.br/defense-ia-and-lite/dicas-de-implementacao/implementacao-de-um-token-service.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
