From 4cfa8fa6e4e32508f3b2d778f8ab2f8ea453601a Mon Sep 17 00:00:00 2001 From: Leandro Costa Date: Sat, 24 Jan 2026 11:04:38 -0300 Subject: [PATCH] feat: update version to 1.0.38 and add README documentation --- README.md | 326 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 +- package.json | 2 +- 3 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7fda51 --- /dev/null +++ b/README.md @@ -0,0 +1,326 @@ +# lhisp-oauth-client + +Bilingual README (Português / English) for the **lhisp-oauth-client** library. + +- [Português (PT-BR)](#português-pt-br) +- [English](#english) + +--- + +## Português (PT-BR) + +Cliente HTTP simples para consumir uma API protegida por **OAuth2 (Client Credentials)**, cuidando automaticamente de: + +- Obter o `access_token` no endpoint de autenticação +- Cache do token até expirar (com margem de ~10s) +- Enviar o token nas requisições subsequentes para a API +- Suporte opcional a **certificado cliente (PFX)** via `https.Agent` + +> Observação: refresh token **ainda não está implementado** (há um `TODO` no código). Quando o token expira, um novo token é solicitado. + +### Instalação + +```bash +npm i lhisp-oauth-client +# ou +yarn add lhisp-oauth-client +``` + +### Uso rápido + +```ts +import { LhispOauthClient } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.exemplo.com", + authUrl: "https://auth.exemplo.com/oauth/token", + clientId: "SEU_CLIENT_ID", + clientSecret: "SEU_CLIENT_SECRET", +}); + +type StatusResponse = { status: string }; + +async function main() { + const resp = await client.get({ + path: "/status", + params: { verbose: true }, + }); + + console.log(resp.status); +} + +main(); +``` + +### API (principais métodos) + +- `getAccessToken(): Promise` + - Obtém e cacheia o token. +- `getAuthToken(): string` + - Retorna o header final de autorização (por padrão `" "`). +- `executarRequest(params): Promise` + - Método base que injeta token e executa request via Axios. +- Atalhos HTTP: + - `get`, `post`, `put`, `patch`, `delete` + +### Parâmetros do construtor + +`new LhispOauthClient(params)` aceita: + +- `apiUrl` (string, obrigatório): URL base da API (ex.: `https://api.exemplo.com`) +- `authUrl` (string, obrigatório): endpoint OAuth2 para obter token +- `clientId` (string, obrigatório) +- `clientSecret` (string, obrigatório) + +Opcionalmente: + +- `certificado` (string | Buffer): certificado cliente PFX + - Se for string, o código assume **base64** e faz `Buffer.from(certificado, "base64")` +- `senhaCertificado` (string): senha do PFX +- `authScope` (string): adiciona `scope` no request de token +- `grantType` (string): padrão `client_credentials` +- `authContentType` (`ContentType`): padrão `application/x-www-form-urlencoded` +- `authData` (Record): campos extras enviados ao endpoint de token +- `headers` (object): headers aplicados nas requisições para a API +- `authHeaders` (object): headers extras somente na obtenção do token +- `authHeaderName` (string): nome do header usado para credenciais Basic (padrão `Authorization`) +- `tokenHeaderName` (string): nome do header onde o token é enviado (padrão `Authorization`) +- `sendAuthCredentialsOnRequestBody` (boolean): envia `client_id` e `client_secret` também no body +- `formatAccessToken` ((token?) => string): personaliza o valor enviado no header do token +- `timeout` (number): timeout das requisições (ms). Padrão `60000` +- `logger` (Logger): logger compatível (com `.child()` + métodos `info/warn/error/debug`) + +### Content-Type + +Você pode escolher o `Content-Type` das requisições para API via `contentType`: + +```ts +import { ContentType } from "lhisp-oauth-client"; + +await client.post({ + path: "/clientes", + contentType: ContentType.APPLICATION_JSON, + data: { name: "Maria" }, +}); +``` + +> Nota: o `contentType` de **autenticação** (request do token) é controlado por `authContentType`. + +### Exemplo: credenciais no body (alguns servidores exigem) + +```ts +import { LhispOauthClient, ContentType } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.exemplo.com", + authUrl: "https://auth.exemplo.com/oauth/token", + clientId: "SEU_CLIENT_ID", + clientSecret: "SEU_CLIENT_SECRET", + authContentType: ContentType.APPLICATION_JSON, + sendAuthCredentialsOnRequestBody: true, +}); + +await client.get({ path: "/status" }); +``` + +### Exemplo: header de token customizado + +```ts +import { LhispOauthClient } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.exemplo.com", + authUrl: "https://auth.exemplo.com/oauth/token", + clientId: "SEU_CLIENT_ID", + clientSecret: "SEU_CLIENT_SECRET", + tokenHeaderName: "x-token", +}); + +await client.get({ path: "/status" }); +``` + +### Exemplo: usando certificado (PFX) + +```ts +import fs from "node:fs"; +import { LhispOauthClient } from "lhisp-oauth-client"; + +const pfxBuffer = fs.readFileSync("./certificado.pfx"); + +const client = new LhispOauthClient({ + apiUrl: "https://api.exemplo.com", + authUrl: "https://auth.exemplo.com/oauth/token", + clientId: "SEU_CLIENT_ID", + clientSecret: "SEU_CLIENT_SECRET", + certificado: pfxBuffer, + senhaCertificado: "SENHA_DO_PFX", +}); + +await client.get({ path: "/status" }); +``` + +### Observações de segurança + +- O `https.Agent` é criado com `rejectUnauthorized: false` (com ou sem certificado). Isso **desabilita validação do certificado** do servidor TLS. + - Em produção, isso pode reduzir segurança. Se você precisar de validação TLS, será necessário ajustar a implementação. + +--- + +## English + +A lightweight HTTP client to consume an API protected by **OAuth2 (Client Credentials)**. It automatically: + +- Fetches an `access_token` from the auth endpoint +- Caches the token until it expires (with a ~10s safety margin) +- Sends the token on subsequent API requests +- Optionally supports **client certificate (PFX)** via `https.Agent` + +> Note: refresh token is **not implemented yet** (there is a `TODO` in the code). When the token expires, a new token is requested. + +### Install + +```bash +npm i lhisp-oauth-client +# or +yarn add lhisp-oauth-client +``` + +### Quick start + +```ts +import { LhispOauthClient } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.example.com", + authUrl: "https://auth.example.com/oauth/token", + clientId: "YOUR_CLIENT_ID", + clientSecret: "YOUR_CLIENT_SECRET", +}); + +type StatusResponse = { status: string }; + +async function main() { + const resp = await client.get({ + path: "/status", + params: { verbose: true }, + }); + + console.log(resp.status); +} + +main(); +``` + +### API (main methods) + +- `getAccessToken(): Promise` + - Fetches and caches the token. +- `getAuthToken(): string` + - Returns the final auth header value (by default `" "`). +- `executarRequest(params): Promise` + - Base method that injects the token and performs the request via Axios. +- HTTP helpers: + - `get`, `post`, `put`, `patch`, `delete` + +### Constructor parameters + +`new LhispOauthClient(params)` accepts: + +Required: + +- `apiUrl` (string): API base URL (e.g. `https://api.example.com`) +- `authUrl` (string): OAuth2 token endpoint +- `clientId` (string) +- `clientSecret` (string) + +Optional: + +- `certificado` (string | Buffer): PFX client certificate + - If string, it is treated as **base64** and converted using `Buffer.from(certificado, "base64")` +- `senhaCertificado` (string): PFX password +- `authScope` (string): adds `scope` to the token request +- `grantType` (string): default `client_credentials` +- `authContentType` (`ContentType`): default `application/x-www-form-urlencoded` +- `authData` (Record): extra fields sent to the token endpoint +- `headers` (object): headers applied to API calls +- `authHeaders` (object): extra headers only for token retrieval +- `authHeaderName` (string): Basic credentials header name (default `Authorization`) +- `tokenHeaderName` (string): token header name (default `Authorization`) +- `sendAuthCredentialsOnRequestBody` (boolean): also sends `client_id` and `client_secret` in the request body +- `formatAccessToken` ((token?) => string): customize token header value +- `timeout` (number): request timeout in ms (default `60000`) +- `logger` (Logger): compatible logger (must support `.child()` and `info/warn/error/debug`) + +### Content-Type + +You can set the API request `Content-Type` using `contentType`: + +```ts +import { ContentType } from "lhisp-oauth-client"; + +await client.post({ + path: "/customers", + contentType: ContentType.APPLICATION_JSON, + data: { name: "Alice" }, +}); +``` + +> Note: the **auth/token** request content type is controlled by `authContentType`. + +### Example: credentials in request body + +```ts +import { LhispOauthClient, ContentType } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.example.com", + authUrl: "https://auth.example.com/oauth/token", + clientId: "YOUR_CLIENT_ID", + clientSecret: "YOUR_CLIENT_SECRET", + authContentType: ContentType.APPLICATION_JSON, + sendAuthCredentialsOnRequestBody: true, +}); + +await client.get({ path: "/status" }); +``` + +### Example: custom token header + +```ts +import { LhispOauthClient } from "lhisp-oauth-client"; + +const client = new LhispOauthClient({ + apiUrl: "https://api.example.com", + authUrl: "https://auth.example.com/oauth/token", + clientId: "YOUR_CLIENT_ID", + clientSecret: "YOUR_CLIENT_SECRET", + tokenHeaderName: "x-token", +}); + +await client.get({ path: "/status" }); +``` + +### Example: using a PFX certificate + +```ts +import fs from "node:fs"; +import { LhispOauthClient } from "lhisp-oauth-client"; + +const pfxBuffer = fs.readFileSync("./certificate.pfx"); + +const client = new LhispOauthClient({ + apiUrl: "https://api.example.com", + authUrl: "https://auth.example.com/oauth/token", + clientId: "YOUR_CLIENT_ID", + clientSecret: "YOUR_CLIENT_SECRET", + certificado: pfxBuffer, + senhaCertificado: "PFX_PASSWORD", +}); + +await client.get({ path: "/status" }); +``` + +### Security notes + +- The internal `https.Agent` is created with `rejectUnauthorized: false` (with or without PFX). This **disables TLS server certificate validation**. + - In production, this may reduce security. If you need strict TLS validation, the implementation must be adjusted. diff --git a/package-lock.json b/package-lock.json index ccd6bff..10813df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lhisp-oauth-client", - "version": "1.0.37", + "version": "1.0.38", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lhisp-oauth-client", - "version": "1.0.37", + "version": "1.0.38", "license": "MIT", "dependencies": { "axios": "^1.10.0" diff --git a/package.json b/package.json index f2252c2..564fa28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lhisp-oauth-client", - "version": "1.0.37", + "version": "1.0.38", "main": "src/index", "types": "src/index.d.ts", "repository": {