@assinafy/sdk 2.1.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,170 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.3.0] - 2026-09-20
11
+
12
+ ### Added
13
+
14
+ - **OAuth 2.1 support — `client.oauth`.** Applications that act inside *other
15
+ people's* workspaces can now run the whole authorization-code flow through
16
+ the SDK instead of assembling it by hand:
17
+ - `createAuthorizationUrl(options)` mints a fresh PKCE verifier (S256), a
18
+ `state` and, for `openid`, a `nonce`, then builds the consent URL. It
19
+ returns everything the callback and token exchange need, so nothing has to
20
+ be re-derived later.
21
+ - `readAuthorizationCallback(params, expected)` validates the redirect before
22
+ anything on it is trusted: `state` in constant time, then `iss` (RFC 9207).
23
+ A declined consent surfaces as an `OAuthError`, not a silent success. It
24
+ accepts an Express `req.query`, a `URLSearchParams`, a `URL`, a full
25
+ callback URL or a bare query string.
26
+ - `exchangeCode(options)`, `refreshToken(options)` and `revokeToken(options)`
27
+ call `POST /oauth/token` and `POST /oauth/revoke` on a credential-free
28
+ transport, so an integrator's own `X-Api-Key` is never sent to a route that
29
+ authenticates the application.
30
+ - `getUserInfo(accessToken?)` reads the OpenID Connect claims.
31
+ - `getProtectedResourceMetadata()` (RFC 9728, served at the API host root)
32
+ and `getAuthorizationServerMetadata(issuer?)` (RFC 8414) discover the
33
+ endpoints instead of hardcoding them, and the second one rejects a document
34
+ whose `issuer` disagrees with where it was fetched from.
35
+ - **`OAuthError`** — the OAuth endpoints answer with a flat
36
+ `{ error, error_description }` body rather than the `{ status, message, data }`
37
+ envelope, so the RFC 6749 code is exposed as `error` and the explanation as
38
+ `errorDescription`. It extends `ApiError`, so existing `catch` blocks keep
39
+ matching.
40
+ - **`ApiError.challenge`** — the parsed `WWW-Authenticate` header, when the API
41
+ sends one. On a `403` it names the scope the token is missing
42
+ (`insufficient_scope`), which is the only way to tell "reconnect asking for
43
+ more" apart from a permission an OAuth token can never hold. The parser is
44
+ also exported as `parseWwwAuthenticate`.
45
+ - Both READMEs document the complete OAuth flow: registering the application,
46
+ the scope catalogue, workspace binding, refresh-token rotation, revocation,
47
+ and what each failure means.
48
+
49
+ ### Changed
50
+
51
+ - **Signer verification and notification channels are now validated as a
52
+ pair.** The API couples them — the verification code travels on the
53
+ notification channel — so `Email` verification only allows `Email`
54
+ notification, `Whatsapp` only `Whatsapp`, and `DigitalCertificate` either
55
+ one. Exactly one notification method is allowed per signer. The SDK rejected
56
+ unknown channel names but let an impossible pairing (and an empty or
57
+ two-element `notification_methods`) through to a `400`. The rule now lives in
58
+ `validateAssignmentSignerOptions()` and therefore applies to assignment
59
+ creation, cost estimation and template-driven document creation alike; the
60
+ duplicate one-method check in the template path was removed.
61
+ - A payload the API would have rejected now fails locally with a
62
+ `ValidationError` naming the pairing.
63
+ - `AssignmentVerificationMethod` and `AssignmentNotificationMethod` document
64
+ the channel matrix, its costs and its prerequisites. ICP-Brasil **A1** and
65
+ **A3** are recorded explicitly as certificate media — a file in software or a
66
+ token/smartcard, chosen by the signer in the browser — both modelled by the
67
+ single `DigitalCertificate` value, with no separate field to send.
68
+ - `bun run audit:api` additionally fetches the two OAuth discovery documents
69
+ and checks the issuer, the authorization and token endpoints, PKCE `S256` and
70
+ the RFC 9207 `iss` parameter against the published contract. They are the
71
+ only part of the OAuth surface that does not live in the OpenAPI document.
72
+ - `docs/API_COVERAGE.md` maps the four new OAuth operations, bringing the
73
+ ledger to 93 of 93.
74
+ - The Portuguese README is now the complete reference rather than a summary
75
+ pointing at the English one: installation through the end-to-end signature
76
+ flow, every resource, errors, environments and development.
77
+
78
+ ## [2.2.0] - 2026-08-27
79
+
80
+ ### Security
81
+
82
+ - **`baseUrl` now requires `https` for any remote host.** Every request on the
83
+ authenticated transport carries the caller's `X-Api-Key` or bearer token, so
84
+ a plaintext `http://` base URL put a long-lived credential on the wire in
85
+ cleartext. `http` remains accepted when the host is loopback (`localhost`,
86
+ the `127.0.0.0/8` block, or `::1`) so local mock servers and the packed
87
+ consumer smoke test keep working; anything else is rejected with a
88
+ `ValidationError` at construction time. `SECURITY.md` already required HTTPS
89
+ endpoints — the client now enforces it rather than trusting configuration.
90
+ - Callers pointing at a remote `http://` host must switch to `https://`.
91
+
92
+ ### Fixed
93
+
94
+ - **`signers.findByEmail()` requested 100 rows per page and received 50.** The
95
+ API clamps `per-page` to 50 and answers `200`, so the lookup scanned half the
96
+ window its documentation claimed. It now pins the real maximum, and the
97
+ method and `signers.list()` documentation state the clamp instead of naming
98
+ 100 as the API maximum. `documents.list()` carried the same incorrect claim.
99
+ - **Unstructured error bodies were reported as the generic "API request
100
+ failed".** A gateway or proxy in front of the API answers with `text/plain`
101
+ or HTML rather than the Assinafy JSON envelope. `ApiError.fromResponse()`
102
+ only read `message`/`error` off an object, so exactly the failures with no
103
+ structured body to explain them lost their only explanation. A non-JSON body
104
+ is now used as the message, with whitespace collapsed and the text capped at
105
+ 500 characters; the untouched body remains on `ApiError.responseData`.
106
+
107
+ ### Added
108
+
109
+ - **`MAX_LIST_PAGE_SIZE`** — the largest page any list endpoint returns (50).
110
+ Exported so callers can size pagination loops against the value the server
111
+ actually honours rather than the one they request.
112
+
113
+ ### Changed
114
+
115
+ - The email and E.164 phone-number patterns had five and two copies across the
116
+ resource files. They are now `isEmail()`, `assertEmail()`, and
117
+ `isE164PhoneNumber()` in `src/utils.ts`, so the accepted shape of a contact
118
+ value is defined in one place. Validation behaviour and error messages are
119
+ unchanged.
120
+ - `decodeBinaryErrorBody()` returns a non-JSON binary body as a string instead
121
+ of wrapping it in `{ message }`, so both unstructured-body paths converge on
122
+ the single rule in `ApiError.fromResponse()`.
123
+ - GitLab CI reads the Bun version from `package.json`'s `packageManager` field
124
+ instead of repeating the literal, matching how GitHub Actions' `setup-bun`
125
+ resolves it. Both CIs now move together when that field is bumped.
126
+ - README gained a contents map linking the setup, end-to-end workflow, and
127
+ per-resource sections; list examples use the `per-page` spelling the API
128
+ reads and document the page-size clamp.
129
+
130
+ ## [2.1.2] - 2026-08-26
131
+
132
+ ### Added
133
+
134
+ - `SDK_USER_AGENT` — every transport, including public and signer-access-code
135
+ requests, now identifies itself as `Assinafy-Typescript-SDK/v<VERSION>`.
136
+ - `scripts/node-consumer-smoke.mjs`, exercised by CI and the release workflow:
137
+ it installs the packed tarball into an isolated consumer and drives the real
138
+ Node HTTP, auth, and multipart paths through both CJS and ESM entrypoints.
139
+ - A manual **Sandbox integration** workflow with `read-only` and
140
+ `disposable-full` modes, gated on a protected environment.
141
+
142
+ ### Security
143
+
144
+ - Requests on the authenticated transport are pinned to the configured API
145
+ origin. A cross-origin URL is rejected before dispatch, and `Authorization`
146
+ and `X-Api-Key` are marked redirect-sensitive so the redirect stack strips
147
+ them.
148
+ - Cross-origin redirects are blocked unless they are a `GET`/`HEAD` to an
149
+ `https` target whose source URL carries no sensitive query parameter.
150
+ - Public, login, and signer-access-code flows run on a transport whose
151
+ credential headers are removed at construction, so a configured API key
152
+ cannot reach a public endpoint.
153
+ - `AxiosError` is no longer attached as an SDK error `cause`. It retains the
154
+ full request config, including credentials and body, which error reporters
155
+ commonly serialize. Transport failures now carry a sanitized cause with
156
+ credential-shaped values redacted from the message.
157
+ - Caller-supplied loggers receive an allowlist of numeric operational counters
158
+ only. Logger exceptions and rejected promises are swallowed so telemetry
159
+ cannot change request semantics.
160
+
161
+ ### Fixed
162
+
163
+ - `AssinafyClient.create()` no longer let its options bag override the
164
+ `apiKey`/`accountId` passed positionally, and it rejects missing or malformed
165
+ credentials instead of constructing an unusable client.
166
+ - Constructor options are validated up front: a non-object bag, a blank
167
+ credential, or an invalid `timeout`/`maxRetries` throws `ValidationError`.
168
+ - `uploadAndRequestSignatures()` validates every signer before uploading, so an
169
+ invalid later signer no longer leaves an orphaned document behind, and it no
170
+ longer waits for `metadata_ready` before creating a virtual assignment.
171
+ - HTTP 429 replay is limited to `GET`, `HEAD`, `OPTIONS`, and `DELETE`, with
172
+ `GET /sign` excluded because that read records a signer view.
173
+
10
174
  ## [2.1.1] - 2026-08-06
11
175
 
12
176
  Maintenance only. The published `dist/` is byte-identical to 2.1.0; no runtime,
@@ -457,7 +621,10 @@ fields (`cpf`, `whatsapp_phone_number`) with the PHP SDK and n8n node.
457
621
  - High-level `uploadAndRequestSignatures` helper on `AssinafyClient`.
458
622
  - `PaginatedResult<T>` with parsed `X-Pagination-*` header meta.
459
623
 
460
- [Unreleased]: https://github.com/assinafy/typescript-sdk/compare/v2.1.1...HEAD
624
+ [Unreleased]: https://github.com/assinafy/typescript-sdk/compare/v2.3.0...HEAD
625
+ [2.3.0]: https://github.com/assinafy/typescript-sdk/compare/v2.2.0...v2.3.0
626
+ [2.2.0]: https://github.com/assinafy/typescript-sdk/compare/v2.1.2...v2.2.0
627
+ [2.1.2]: https://github.com/assinafy/typescript-sdk/compare/v2.1.1...v2.1.2
461
628
  [2.1.1]: https://github.com/assinafy/typescript-sdk/compare/v2.1.0...v2.1.1
462
629
  [2.1.0]: https://github.com/assinafy/typescript-sdk/compare/v2.0.0...v2.1.0
463
630
  [2.0.0]: https://github.com/assinafy/typescript-sdk/releases/tag/v2.0.0