> 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/implementation-guide-people-management-client.md).

# Implementation guide: People management client

Python/FastAPI reference web client for Defense IA & Lite integration: BRMS authentication, OBMS people operations, facial image processing, photo display, and security best practices.

{% hint style="warning" %}
**Warning:** this project is a technical reference for development, validation, and testing. Before production use, review authentication, certificates, access control, auditing, logs, credential protection, and local backend hardening.
{% endhint %}

This client shows how a custom application can authenticate with Defense, use people APIs, send a Base64 facial photo, and display images returned by the system.

{% embed url="<https://github.com/Integracao-Intelbras-Cftv/Client-De-Pessoas.git>" %}

Practical goals:

* Simplify testing of person creation, listing, updating, and deletion in Defense.
* Encapsulate Defense communication in a local backend. This prevents frontend credential exposure.
* Separate API token and image credential use. This reduces common integration errors.
* Provide a simple foundation for validating payloads and flows before building an enterprise client.

## Architecture at a glance

Main components:

* main.py: FastAPI server, local routes, image URL normalization, and person CRUD operations.
* token\_service.py: Defense authentication, MD5 signature, and in-memory token and credential cache.
* service.py: HTTP client for OBMS calls, `X-Subject-Token` header creation, and 401 retry.
* image.py: photo processing, centered crop, resizing, JPEG compression, and Base64 conversion.
* defense.html and assets/: local web interface for visual client operation.
* requirements.txt: Python dependencies needed to run the project.

## End-to-end flow

```mermaid
flowchart LR
    subgraph USER[User layer]
        A[Web interface<br/>Browser]
    end

    subgraph LOCAL[Local client]
        B[Local FastAPI<br/>main.py]
        C[Token Service<br/>token_service.py]
        E[Service HTTP<br/>service.py]
        G[Image Service<br/>image.py]
        H[URL normalization<br/>image credential]
    end

    subgraph DEFENSE[Defense IA]
        D[BRMS<br/>Authentication]
        F[OBMS<br/>People API]
    end

    A --> B

    B --> C
    C --> D
    D --> C
    C --> B

    B --> E
    E --> F
    F --> E
    E --> B

    B --> G
    G --> B

    B --> H
    H --> A

    E -.->|401 Unauthorized<br/>invalidates token and signs in again| C

    classDef user fill:#E8F5E9,stroke:#2E7D32,stroke-width:1.5px,color:#1B1B1B;
    classDef local fill:#F1F8E9,stroke:#558B2F,stroke-width:1.5px,color:#1B1B1B;
    classDef defense fill:#FFFDE7,stroke:#F9A825,stroke-width:1.5px,color:#1B1B1B;

    class A user;
    class B,C,E,G,H local;
    class D,F defense;
```

### Application startup

When you run the project, FastAPI starts on local port 8000. It serves the HTML interface and local browser routes. Defense authentication runs on demand when a call needs a token.

| <p>python main.py<br># or<br>uvicorn main:app --host 0.0.0.0 --port 8000 --reload</p> |
| ------------------------------------------------------------------------------------- |

In summary:

* The browser does not communicate directly with Defense.
* The local backend receives interface actions and calls Defense.
* Token Service provides the current token to `service.py`.
* Responses are normalized before returning to the interface.

## Defense authentication

Authentication is implemented in `token_service.py`. It uses the two-step BRMS authorize endpoint.

{% content-ref url="/pages/No7JHvIzSDarfe8j9EfH" %}
[Implementação de um Token Service](/defense-ia-and-lite/dicas-de-implementacao/implementacao-de-um-token-service.md)
{% endcontent-ref %}

| POST /brms/api/v1.0/accounts/authorize |
| -------------------------------------- |

### First Login

The first call sends `userName`, `ipAddress`, and `clientType`. The expected response contains `realm`, `randomKey`, and `publicKey`/`publickey`. These build the second-step signature.

| <p>{<br>"userName": "system",<br>"ipAddress": "",<br>"clientType": "WINPC\_V2"<br>}</p> |
| --------------------------------------------------------------------------------------- |

### Second Login

The second call sends `mac`, `signature`, `userName`, `randomKey`, `publicKey`, `encryptType`, `ipAddress`, `clientType`, and `userType`. The expected response contains a token and credential.

| <p>{<br>"mac": "AA:BB:CC:DD:EE:FF",<br>"signature": "\<md5\_signature>",<br>"userName": "system",<br>"randomKey": "\<randomKey>",<br>"publicKey": "\<publicKey>",<br>"encryptType": "MD5",<br>"clientType": "WINPC\_V2",<br>"userType": "0"<br>}</p> |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

### How the login signature is generated

The signature follows the MD5 hash chain used by the Defense authentication flow:

| <p>temp1 = MD5(password)<br>temp2 = MD5(username + temp1)<br>temp3 = MD5(temp2)<br>temp4 = MD5(username + ":" + realm + ":" + temp3)<br>signature = MD5(temp4 + ":" + randomKey)</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

| **Field**  | **Correct use**                                                                                 |
| ---------- | ----------------------------------------------------------------------------------------------- |
| token      | Used in Defense API calls through the `X-Subject-Token` header.                                 |
| credential | Used to access images returned by Defense. It usually appears as a `token` image URL parameter. |

| **Warning:** do not treat token and credential as the same value. The token authenticates the API. The credential grants image access when Defense returns protected URLs. |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

## Token cache and automatic retry

The project keeps the token and credential in memory. `TOKEN_TTL` controls their logical validity. Before reusing the token, the code checks its remaining validity. It signs in again when needed.

| <p>TOKEN\_TTL = 1800 # 30 min<br><br>if \_token and time.time() < (\_token\_expiry - 60):<br>return \_token</p> |
| --------------------------------------------------------------------------------------------------------------- |

In `service.py`, all OBMS calls use the same HTTP wrapper. If Defense returns 401, the client invalidates the cache and retries the same call only once.

| <p>if response.status\_code == 401 and retry:<br>invalidate\_token()<br>return \_request(method, endpoint, data, params, retry=False)</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------- |

This behavior prevents infinite loops. It also reduces failures when a token expires between operations.

## Local client endpoints

| **Local route**       | **Method** | **Purpose**                                                        |
| --------------------- | ---------- | ------------------------------------------------------------------ |
| /                     | GET        | Serves the `defense.html` interface.                               |
| /assets/\*            | GET        | Serves interface static files.                                     |
| /pessoas              | GET        | Lists people from Defense.                                         |
| /pessoas              | POST       | Creates a person in Defense.                                       |
| /pessoas/{person\_id} | PUT        | Updates an existing person.                                        |
| /pessoas/{person\_id} | DELETE     | Removes a person through batch deletion.                           |
| /foto                 | GET        | Helper proxy for retrieving a Defense image in a test environment. |

## Defense endpoints used

| **Action**    | **Method**  | **Defense endpoint**                   |
| ------------- | ----------- | -------------------------------------- |
| First login   | POST        | /brms/api/v1.0/accounts/authorize      |
| Second login  | POST        | /brms/api/v1.0/accounts/authorize      |
| List people   | GET or POST | /obms/api/v1.1/acs/person/page         |
| Create person | POST        | /obms/api/v1.1/acs/person              |
| Update person | PUT         | /obms/api/v1.1/acs/person/{person\_id} |
| Remove person | POST        | /obms/api/v1.1/acs/person/delete/batch |

Note: some versions may return `405 Method Not Allowed` when listing through GET. The client already falls back to POST.

## People operations

### Listing people

The local `/pessoas` route builds a payload with pagination, `orgCode`, `containChild`, and empty filters. The Defense response is normalized to handle structures such as `data.pageData`, `data.list`, `rows`, or `records`.

| <p>{<br>"page": 1,<br>"pageSize": 100,<br>"orgCode": "001",<br>"containChild": "0",<br>"personName": ""<br>}</p> |
| ---------------------------------------------------------------------------------------------------------------- |

After listing, the backend iterates through each person. It updates `faceImageUrl` and `pictureUrl` with the correct credential.

### Creating a person

Creation uses POST at `/acs/person`. The `personId` is generated locally from the timestamp. Data is organized in blocks such as `baseInfo`, `extensionInfo`, `residentInfo`, `authenticationInfo`, `accessInfo`, `faceComparisonInfo`, and `entranceInfo`.

| <p>body = {<br>"baseInfo": {<br>"personId": "\<generated\_id>",<br>"firstName": "\<name>",<br>"orgCode": "001",<br>"facePictures": \["\<base64>"]<br>},<br>"extensionInfo": {<br>"idType": "0",<br>"nationalityId": "9999",<br>"companyName": "\<company>"<br>}<br>}</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

### Updating and removing

Updating uses PUT at `/acs/person/{person\_id}`. Removing uses POST at `/acs/person/delete/batch`, sending `personIds` and `mode`. This remains compatible with OBMS batch deletion.

| <p>{<br>"personIds": \["\<person\_id>"],<br>"mode": "1"<br>}</p> |
| ---------------------------------------------------------------- |

## Facial image flow

The `image.py` module processes the user's photo before sending it to Defense. It standardizes the facial image and reduces failures from image size, dimensions, or file weight.

| <p>Image upload<br>-> byte reading<br>-> RGB conversion<br>-> centered crop<br>-> resize to 500 x 500<br>-> JPEG compression up to 100 KB<br>-> Base64<br>-> send in the facePictures field</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| **Parameter**    | **Value used**        |
| ---------------- | --------------------- |
| Final dimensions | 500 x 500 pixels      |
| Output format    | JPEG                  |
| Target size      | Up to 100 KB          |
| Send field       | baseInfo.facePictures |

When the original image exceeds 100 KB, JPEG quality is progressively reduced. If it still exceeds the limit, the code uses a 250 x 250 version as a last resort.

## Displaying images returned by Defense

When listing people, Defense may return protected URLs in `faceImageUrl` and `pictureUrl`. The client uses `faceImageUrl` as the face crop and `pictureUrl` as the contextual image.

| <p>https\://\<IP>/v1/s/.../headPic.jpg?token=credentials<br><br># becomes<br><br>https\://\<IP>/v1/s/.../headPic.jpg?token=\<credential></p> |
| -------------------------------------------------------------------------------------------------------------------------------------------- |

The `/foto` route works as a helper proxy. It retrieves the image with the credential in the URL and the API token in the `X-Subject-Token` header. This helps during testing when the browser does not trust Defense's HTTPS certificate.

{% hint style="warning" %}
**Warning:** in production, resolve the certificate chain with a trusted CA or valid certificate. Restrict `verify=False` and the local image proxy to laboratory and controlled validation environments.
{% endhint %}

## Environment variables

Create a `.env` file in the project root. Do not commit real credentials.

| <p>HOST=\<DEFENSE\_IP><br>PORT=443<br>USERNAME=system<br>PASSWORD=user\_password<br>MAC=AA:BB:CC:DD:EE:FF<br>CLIENT\_TYPE=WINPC\_V2<br>USER\_TYPE=0<br>REQUEST\_TIMEOUT=300</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| **Variable**     | **Description**                                   |
| ---------------- | ------------------------------------------------- |
| HOST             | Defense server IP address or hostname.            |
| PORT             | Defense HTTPS port.                               |
| USERNAME         | User used for authentication.                     |
| PASSWORD         | User password.                                    |
| MAC              | MAC address included in the login payload.        |
| CLIENT\_TYPE     | Client type sent to Defense. Default: `WINPC_V2`. |
| USER\_TYPE       | User type sent to Defense. Default: `0`.          |
| REQUEST\_TIMEOUT | HTTP call timeout, in seconds.                    |

## How to run

Install dependencies and start the local server:

| <p>pip install -r requirements.txt<br>python main.py</p> |
| -------------------------------------------------------- |

Access the interface at:

| <http://localhost:8000> |
| ----------------------- |

Dependencies listed in `requirements.txt`:

* fastapi
* uvicorn\[standard]
* requests
* pillow
* python-multipart

## Best practices for evolving the client

### Authentication

* Clearly separate token and credential.
* Use the token only in the `X-Subject-Token` header.
* Use the credential only for images when Defense requires a URL token.
* Use the actual session duration returned by Defense when available. Do not rely only on a fixed TTL.
* After a 401 response, invalidate the cache and sign in once before failing.

### Security

* Never expose usernames, passwords, tokens, or credentials in the frontend.
* Do not commit `.env` files with real credentials.
* Avoid logs containing tokens, credentials, passwords, or sensitive payloads.
* Use HTTPS with a trusted certificate. Do not use `verify=False` in production.
* Protect local routes with authentication if the client is network-accessible.
* Add auditing for person creation, editing, and deletion.

### HTTP integration

* Centralize calls in one client, such as `service.py`.
* Configure an explicit timeout.
* Handle 401, 405, 422, 500, and network failures with clear messages.
* Keep the backend between the UI and Defense. Avoid direct frontend coupling with production APIs.

## Project limitations

* It does not implement user authentication in the interface.
* It has no role-based permission control.
* It has no persistent operation auditing.
* It does not locally encrypt `.env` secrets.
* It has no retry queue for Defense unavailability.
* It has no complete observability, metrics, or tracing.
* It does not handle certificates for production.

<mark style="color:$danger;">**Therefore, restrict its use to development, laboratory, validation, and controlled technical testing.**</mark>

## Common errors and quick diagnosis

| **Symptom**                                | **Likely cause**                                                                | **Recommended action**                                                                                |
| ------------------------------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| 405 Method Not Allowed when listing people | Firmware accepts `/acs/person/page` through POST instead of GET.                | Use the POST fallback already included in the client.                                                 |
| 401 Unauthorized                           | Expired token, incorrect credentials, or mismatched login parameters.           | Validate `USERNAME`, `PASSWORD`, `MAC`, `CLIENT_TYPE`, and `USER_TYPE`. Confirm the single retry ran. |
| Image does not appear in the interface     | URL lacks the correct credential or the browser does not trust the certificate. | Check `token=<credential>`, open the URL directly, and review the certificate.                        |
| ERR\_CERT\_AUTHORITY\_INVALID              | Defense HTTPS certificate is not trusted by the browser.                        | Install a trusted CA or valid certificate. Avoid bypasses in production.                              |
| 422 when creating or updating a person     | Required fields are missing or `multipart/form-data` is invalid.                | Validate the form, field names, and file upload.                                                      |
| Call timeout                               | Defense is unavailable, the network is slow, or `REQUEST_TIMEOUT` is too low.   | Validate connectivity, port, firewall, and server response time.                                      |

{% hint style="warning" %}
**Warning:** before presenting this client as a customer solution, turn the reference into a product. Add its own authentication, permissions, secure logs, auditing, certificate handling, robust validation, contract testing, and a deployment policy.
{% endhint %}


---

# 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/implementation-guide-people-management-client.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.
