> 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/defense-ia-english/implementation-tips/token-service-implementation.md).

# Token service implementation

Go implementation of a Defense IA token service. Covers login, keepalive, in-memory storage, and the "/token" and "/health" endpoints with API-key protection and security practices.

### Token Service (Defense IA)

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

{% hint style="danger" %}
Before production, expose **/token** and **/health** **only through HTTPS**. **Restrict access** with mTLS, a stronger API-key scheme, or VPN.\
When possible, **do not expose /token**. Keep it on a **private network** and provide the token **only in memory**, with expiration and rotation.\
For greater security, use a **Secret Manager** such as Vault or a cloud service. It delivers and rotates tokens without a public endpoint.
{% endhint %}

This Go service authenticates with Defense IA, keeps the session active, and exposes the current token through a local HTTP endpoint.

Practical goals:

* Prevent other integrations from implementing Defense login and keepalive.
* Centralize token and session data in one process.
* Expose the token simply, with API-key protection.

### Architecture at a glance

```mermaid
---
config:
  layout: dagre
  look: neo
  theme: neo-dark
---
flowchart RL
 subgraph Auth["Initial authentication"]
        A["App"]
        D["Defense IA"]
        T[("Token + Timers")]
  end
 subgraph Loop["Lifecycle loop (app running)"]
    direction TB
        K["PUT   /accounts/keepalive Header: X-Subject-Token+Body: ..."]
        U["POST  /accounts/updateToken Header: X-Subject-Token + signature"]
        S["Automatic global token swap"]
  end
    A -- 2) Second Login (credentials/encrypt) --> D
    A L_A_D_2@-- 1) First Login (Receives public key) --> D
    D -- 2) Response: token, userId, tokenRate, secretKey --> A
    D L_D_A_2@-- 1) Response: encryption parameters --> A
    A -- Stores token and starts 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 on any call Re-authenticate (1 + 2) Restart 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 }
```

Main components:

* `cmd/token-service/token-service.go`: Application entry point.
* `internal/app/token-service.go`: Token maintenance loop for login and refresh.
* `internal/app/create-token.go`: Creates and stores the initial token.
* `internal/app/refresh-token.go`: Runs Defense keepalive.
* `internal/app/http-server.go`: Local `/health` and `/token` endpoints.
* `internal/pkg/defense/*`: HTTP client and Defense authentication logic.
* `internal/pkg/store/store.go`: In-memory storage with an `RWMutex` lock.

### End-to-end flow

#### Application startup

In `main`, the service loads configuration, starts the HTTP server, and starts the token loop in a 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()
```

Summary:

* The `store` is shared by the maintenance loop and HTTP API.
* `SetInfo()` prepares cryptographic data and keys.
* `Go()` starts the continuous login and refresh cycle.

#### Token creation (login)

In `create-token.go`, the service calls `Auth` and persists essential information:

```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)
```

This ensures:

* The current token is available through HTTP.
* `updatedAt` tracks session age.
* The login credential is also available internally.

#### Token maintenance loop

`TokenService.Go()` contains the main loop:

```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 }
  }
}
```

Current behavior:

* Creates a token.
* Runs keepalive every `20s` through `DELAY_TO_REFRESH`.
* Returns to login when keepalive fails.
* Waits for a password update in `.env` after invalid-credential code `2001`.

#### Defense keepalive (refresh)

The current refresh uses the keepalive endpoint:

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

Important:

* The token is sent in the `X-Subject-Token` header.
* A `200` response means the session was renewed.

#### Local consumer endpoint

In `http-server.go`, `/token` returns the token and timestamp:

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

This endpoint uses API-key protection:

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

It also accepts `Authorization: Bearer <api-key>`.

#### Thread safety

`MemoryStore` uses `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]
  ...
}
```

In practice:

* Token writes occur in memory.
* HTTP reads do not conflict with maintenance-loop updates.

#### How the login signature is generated

`auth.go` builds the login signature through MD5 hash steps:

```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)
```

This flow uses `realm` and `randomKey` from the first `authorize` step to authenticate the second step.

### Defense endpoints

* `POST /brms/api/v1.0/accounts/authorize`
  * Used for the authentication handshake and final login.
* `PUT /brms/api/v1.0/accounts/keepalive`
  * Keeps the session active.

Update endpoint:

* `POST /brms/api/v1.0/accounts/updateToken`
  * Requires `X-Subject-Token` and a body with `signature`.

### Timing rules

* Run keepalive around every `30s`.
* Update the token around every `30min`, or at `tokenRate`.

Current implementation:

* Fixed `20s` keepalive through `DELAY_TO_REFRESH`.
* No periodic `updateToken` execution.
* Performs a full login after refresh failure.

### Environment variables

Required:

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

Optional:

* `DEFENSE_SCHEME` (default: `https`)
* `DEFENSE_INSECURE_SKIP_VERIFY` (`true`/`false`)
* `HTTP_ADDR` (default: `:8080`)
* `ENVIRONMENT` (operational default: `DEVELOPMENT`)

Example (`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"
```

### Run the service

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

Expected logs:

* HTTP server listening (`HTTP server listening on ...`).
* Successful token creation.
* Periodic refresh logs.

### Consume the token

#### Health check

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

Expected response:

```
ok
```

#### Get the token

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

Expected response:

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

### Internal store keys

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

### Common errors and quick diagnosis

* `unauthorized` ao chamar `/token`
  * Cause: Missing or incorrect API key.
  * Action: Validate `HTTP_API_KEY` and the sent header.
* `token not available`
  * Cause: The token was not created yet.
  * Action: Check login logs during startup.
* Repeated refresh failures
  * Cause: Expired token, connectivity, or invalid credentials.
  * Action: Review `DEFENSE_HOST/PORT`, password, and certificates.
* Password-not-changed message (`Password haven't Changed`)
  * Cause: Login returns `2001`.
  * Action: Update `DEFENSE_PASSWORD` in `.env`.

### Next steps

* Add a dedicated update schedule for `~30min` or `tokenRate`.
* Apply a single-retry policy for consumer-client `401` responses.


---

# 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/defense-ia-english/implementation-tips/token-service-implementation.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.
