@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 +168 -1
- package/README.en.md +1373 -0
- package/README.md +763 -522
- package/SECURITY.md +17 -2
- package/dist/index.d.mts +794 -12
- package/dist/index.d.ts +794 -12
- package/dist/index.js +900 -59
- package/dist/index.mjs +894 -57
- package/docs/API_COVERAGE.md +28 -4
- package/docs/COMPATIBILITY.md +86 -0
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -22,9 +22,41 @@ type SignatureImageType = 'signature' | 'initial' | AnyString;
|
|
|
22
22
|
type AnyString = string & {};
|
|
23
23
|
/** Assignment methods supported by the API. */
|
|
24
24
|
type AssignmentMethod = 'virtual' | 'collect';
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* How a signer proves who they are before signing.
|
|
27
|
+
*
|
|
28
|
+
* | Value | How the signer is verified | Requirements | Cost per signer |
|
|
29
|
+
* | --- | --- | --- | --- |
|
|
30
|
+
* | `Email` | One-time code sent by e-mail (the default). | An `email`. | 0 credits |
|
|
31
|
+
* | `Whatsapp` | One-time code sent over WhatsApp. | A `whatsapp_phone_number`; paid plan. | 0.45 credits |
|
|
32
|
+
* | `DigitalCertificate` | The signer signs with their own ICP-Brasil certificate — **A1** (software, a file on the machine) or **A3** (hardware, a token or smartcard) — through the Web PKI browser extension, producing a qualified PAdES signature. | Digital Certificate feature (Standard/Pro); `government_id` (CPF, or CNPJ naming the signer as legal representative); the signer must be alone in their signing step. | 2 credits + notification |
|
|
33
|
+
*
|
|
34
|
+
* A1 and A3 are certificate *media*, chosen by the signer in their browser at
|
|
35
|
+
* signing time. The API models both as the single `DigitalCertificate` value —
|
|
36
|
+
* there is no separate `A1`/`A3` field to send, and the resulting PAdES
|
|
37
|
+
* signature is qualified either way.
|
|
38
|
+
*
|
|
39
|
+
* Verification is coupled to {@link AssignmentNotificationMethod}: the
|
|
40
|
+
* verification code travels on the notification channel, so `Email` pairs only
|
|
41
|
+
* with `Email` and `Whatsapp` only with `Whatsapp`. `DigitalCertificate`
|
|
42
|
+
* carries no code and may be announced over either channel. The SDK rejects an
|
|
43
|
+
* invalid pairing before the request; the API answers `400`.
|
|
44
|
+
*
|
|
45
|
+
* Send neither field to get `Email`/`Email`; send one and the other is
|
|
46
|
+
* inferred from it.
|
|
47
|
+
*/
|
|
26
48
|
type AssignmentVerificationMethod = 'Email' | 'Whatsapp' | 'DigitalCertificate';
|
|
27
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* How the signing invitation reaches the signer. Exactly one per signer.
|
|
51
|
+
*
|
|
52
|
+
* | Value | Requirements | Cost per signer |
|
|
53
|
+
* | --- | --- | --- |
|
|
54
|
+
* | `Email` | An `email`. | 0 credits |
|
|
55
|
+
* | `Whatsapp` | A `whatsapp_phone_number`; paid plan. | 0.45 credits |
|
|
56
|
+
*
|
|
57
|
+
* Notifications are charged when the assignment is created and again on every
|
|
58
|
+
* resend. See {@link AssignmentVerificationMethod} for the pairing rules.
|
|
59
|
+
*/
|
|
28
60
|
type AssignmentNotificationMethod = 'Email' | 'Whatsapp';
|
|
29
61
|
/** Minimal logger contract (compatible with console, pino, winston, etc.). */
|
|
30
62
|
interface Logger {
|
|
@@ -1110,6 +1142,129 @@ interface IUpdateTagPayload {
|
|
|
1110
1142
|
/** Pass `null` to clear the color; omit to leave unchanged. */
|
|
1111
1143
|
color?: string | null;
|
|
1112
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Permission an OAuth application can request.
|
|
1147
|
+
*
|
|
1148
|
+
* Known literals stay suggested in editors while any server-added scope still
|
|
1149
|
+
* type-checks (see {@link AnyString}). The authoritative list is
|
|
1150
|
+
* `scopes_supported` in
|
|
1151
|
+
* {@link IOAuthAuthorizationServerMetadata}.
|
|
1152
|
+
*/
|
|
1153
|
+
type OAuthScope = 'documents:read' | 'documents:write' | 'templates:read' | 'templates:write' | 'account:read' | 'openid' | 'profile' | 'email' | 'offline_access' | AnyString;
|
|
1154
|
+
/**
|
|
1155
|
+
* RFC 9728 protected-resource metadata served at
|
|
1156
|
+
* `https://api.assinafy.com.br/.well-known/oauth-protected-resource`.
|
|
1157
|
+
*
|
|
1158
|
+
* Served bare, without the `{ status, message, data }` envelope, as RFC 8615
|
|
1159
|
+
* requires. `scopes_supported` omits `offline_access`, which is a request-time
|
|
1160
|
+
* signal to the authorization server rather than a permission this API checks.
|
|
1161
|
+
*/
|
|
1162
|
+
interface IOAuthProtectedResourceMetadata {
|
|
1163
|
+
/** Canonical identifier of this API, e.g. `https://api.assinafy.com.br`. */
|
|
1164
|
+
resource: string;
|
|
1165
|
+
/** Issuers allowed to mint tokens for it, e.g. `['https://auth.assinafy.com.br']`. */
|
|
1166
|
+
authorization_servers: string[];
|
|
1167
|
+
/** Scopes this API accepts. */
|
|
1168
|
+
scopes_supported: string[];
|
|
1169
|
+
/** How a token may be presented; Assinafy accepts `header` only. */
|
|
1170
|
+
bearer_methods_supported: string[];
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* RFC 8414 authorization-server metadata served at
|
|
1174
|
+
* `{issuer}/.well-known/oauth-authorization-server`.
|
|
1175
|
+
*
|
|
1176
|
+
* The browser-facing `authorization_endpoint` lives on the authorization server
|
|
1177
|
+
* while `token_endpoint` and friends live on this API, so read the URLs from
|
|
1178
|
+
* here rather than deriving them from one host.
|
|
1179
|
+
*/
|
|
1180
|
+
interface IOAuthAuthorizationServerMetadata {
|
|
1181
|
+
/** Issuer identifier; must equal the URL the document was fetched from. */
|
|
1182
|
+
issuer: string;
|
|
1183
|
+
/** Browser-facing consent URL. */
|
|
1184
|
+
authorization_endpoint: string;
|
|
1185
|
+
/** Token endpoint, on the API host. */
|
|
1186
|
+
token_endpoint: string;
|
|
1187
|
+
revocation_endpoint?: string;
|
|
1188
|
+
userinfo_endpoint?: string;
|
|
1189
|
+
jwks_uri?: string;
|
|
1190
|
+
scopes_supported?: string[];
|
|
1191
|
+
response_types_supported?: string[];
|
|
1192
|
+
grant_types_supported?: string[];
|
|
1193
|
+
/** Assinafy supports `S256` only; plain PKCE is rejected. */
|
|
1194
|
+
code_challenge_methods_supported?: string[];
|
|
1195
|
+
token_endpoint_auth_methods_supported?: string[];
|
|
1196
|
+
/** RFC 9207. `true` means the callback carries `iss` and clients must check it. */
|
|
1197
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
1198
|
+
client_id_metadata_document_supported?: boolean;
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Everything one authorization attempt needs, returned by
|
|
1202
|
+
* {@link OAuthResource.createAuthorizationUrl}.
|
|
1203
|
+
*
|
|
1204
|
+
* Store every field except `url` in the user's session: the callback handler
|
|
1205
|
+
* needs `state` and `issuer` to prove the response is yours, the token exchange
|
|
1206
|
+
* needs `codeVerifier`, and `nonce` validates the `id_token`.
|
|
1207
|
+
*/
|
|
1208
|
+
interface IOAuthAuthorizationRequest {
|
|
1209
|
+
/** Absolute URL to send the browser to with a full page navigation. */
|
|
1210
|
+
url: string;
|
|
1211
|
+
/** Single-use CSRF value echoed back on the redirect URI. */
|
|
1212
|
+
state: string;
|
|
1213
|
+
/** RFC 7636 code verifier; never leaves your server after this. */
|
|
1214
|
+
codeVerifier: string;
|
|
1215
|
+
/** Issuer expected in the callback's `iss` parameter. */
|
|
1216
|
+
issuer: string;
|
|
1217
|
+
/** Present when `openid` was requested; compare it to the `id_token` claim. */
|
|
1218
|
+
nonce?: string;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Validated authorization response read off the redirect URI by
|
|
1222
|
+
* {@link OAuthResource.readAuthorizationCallback}.
|
|
1223
|
+
*/
|
|
1224
|
+
interface IOAuthAuthorizationCallback {
|
|
1225
|
+
/** Single-use authorization code. Expires 60 seconds after approval. */
|
|
1226
|
+
code: string;
|
|
1227
|
+
/** The `state` value, already checked against the stored one. */
|
|
1228
|
+
state: string;
|
|
1229
|
+
/** The `iss` value, already checked against the expected issuer. */
|
|
1230
|
+
issuer?: string;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* RFC 6749 §5.1 token response. Returned flat, without this API's
|
|
1234
|
+
* `{ status, message, data }` envelope.
|
|
1235
|
+
*/
|
|
1236
|
+
interface IOAuthTokenResponse {
|
|
1237
|
+
access_token: string;
|
|
1238
|
+
/** Always `Bearer`. */
|
|
1239
|
+
token_type: string;
|
|
1240
|
+
/** Access-token lifetime in seconds; Assinafy issues 3600. */
|
|
1241
|
+
expires_in: number;
|
|
1242
|
+
/**
|
|
1243
|
+
* Present only when `offline_access` was requested and granted. Every
|
|
1244
|
+
* refresh returns a new one and retires the old one — persist it before
|
|
1245
|
+
* using the access token.
|
|
1246
|
+
*/
|
|
1247
|
+
refresh_token?: string | null;
|
|
1248
|
+
/**
|
|
1249
|
+
* Scopes actually granted to the access token. Read this instead of
|
|
1250
|
+
* assuming the request was honoured in full; `offline_access` never appears
|
|
1251
|
+
* here because it is a request-time signal, not a permission.
|
|
1252
|
+
*/
|
|
1253
|
+
scope?: string;
|
|
1254
|
+
/** Signed OIDC identity token (RS256). Present only when `openid` was granted. */
|
|
1255
|
+
id_token?: string | null;
|
|
1256
|
+
}
|
|
1257
|
+
/** OpenID Connect claims returned by `GET /oauth/userinfo`. */
|
|
1258
|
+
interface IOAuthUserInfo {
|
|
1259
|
+
/** Stable user identifier. Always present. */
|
|
1260
|
+
sub: string;
|
|
1261
|
+
/** Requires the `profile` scope. */
|
|
1262
|
+
name?: string | null;
|
|
1263
|
+
/** Requires the `email` scope. */
|
|
1264
|
+
email?: string | null;
|
|
1265
|
+
/** Requires the `email` scope. */
|
|
1266
|
+
email_verified?: boolean | null;
|
|
1267
|
+
}
|
|
1113
1268
|
|
|
1114
1269
|
/** Maximum upload size accepted by the API (hard limit, 25 MB). */
|
|
1115
1270
|
declare const MAX_UPLOAD_BYTES: number;
|
|
@@ -1261,7 +1416,8 @@ declare class DocumentResource extends BaseResource {
|
|
|
1261
1416
|
* @param params - Filters and pagination: `status`; `method` (`virtual` or
|
|
1262
1417
|
* `collect`); `tags` (comma-separated IDs, all of which must match);
|
|
1263
1418
|
* `search` (document name, signer name, or signer email); `sort` (`name` or
|
|
1264
|
-
* `updated_at`); `page`; and `per-page` (
|
|
1419
|
+
* `updated_at`); `page`; and `per-page` (the server clamps this to 50
|
|
1420
|
+
* rather than rejecting a larger value).
|
|
1265
1421
|
* @param accountId - Override the client's default account ID.
|
|
1266
1422
|
* @returns Matching documents, with pagination in `meta`. Each item:
|
|
1267
1423
|
* ```jsonc
|
|
@@ -2099,7 +2255,8 @@ declare class SignerResource extends BaseResource {
|
|
|
2099
2255
|
* Pagination info (if any) is attached in `meta`.
|
|
2100
2256
|
*
|
|
2101
2257
|
* @param params - `page`, `per-page`, and `search` (matches `full_name` or
|
|
2102
|
-
* `email`). The
|
|
2258
|
+
* `email`). The server clamps `per-page` to {@link MAX_LIST_PAGE_SIZE}
|
|
2259
|
+
* (50); a larger value is not rejected, it is silently reduced.
|
|
2103
2260
|
* @param accountId - Override the client's default account ID.
|
|
2104
2261
|
* @returns The matching signers, with pagination in `meta`. Each item:
|
|
2105
2262
|
* ```jsonc
|
|
@@ -2181,10 +2338,10 @@ declare class SignerResource extends BaseResource {
|
|
|
2181
2338
|
* `search` is a substring match across signer fields, so the result is
|
|
2182
2339
|
* re-filtered here for an exact, case-insensitive email match.
|
|
2183
2340
|
*
|
|
2184
|
-
* Page size is pinned to the
|
|
2185
|
-
* An exact address realistically matches one
|
|
2186
|
-
* matched more than
|
|
2187
|
-
* exact-email filter to rule that out.
|
|
2341
|
+
* Page size is pinned to {@link MAX_LIST_PAGE_SIZE}, the largest page the
|
|
2342
|
+
* server actually returns. An exact address realistically matches one
|
|
2343
|
+
* signer, but a search term that matched more than that could in principle
|
|
2344
|
+
* miss one — the API exposes no exact-email filter to rule that out.
|
|
2188
2345
|
*
|
|
2189
2346
|
* A `404` from the underlying list is treated as "no match" and mapped to
|
|
2190
2347
|
* `null`; any other {@link ApiError} propagates.
|
|
@@ -3986,6 +4143,506 @@ declare class AuthenticationResource extends BaseResource {
|
|
|
3986
4143
|
private absoluteUrl;
|
|
3987
4144
|
}
|
|
3988
4145
|
|
|
4146
|
+
/** Token-endpoint client authentication methods Assinafy accepts. */
|
|
4147
|
+
type TokenEndpointAuthOptions = {
|
|
4148
|
+
/** The application's `client_id` from Settings → OAuth applications. */
|
|
4149
|
+
clientId: string;
|
|
4150
|
+
/**
|
|
4151
|
+
* The application's `client_secret`. Confidential applications only —
|
|
4152
|
+
* public ones authenticate with PKCE and are never issued a secret. Never
|
|
4153
|
+
* ship it in browser, mobile, or repository code.
|
|
4154
|
+
*/
|
|
4155
|
+
clientSecret?: string;
|
|
4156
|
+
};
|
|
4157
|
+
/**
|
|
4158
|
+
* OAuth 2.1 + OpenID Connect endpoints for applications acting inside *other
|
|
4159
|
+
* people's* workspaces.
|
|
4160
|
+
*
|
|
4161
|
+
* Use this resource only when your product is connected by its users. To
|
|
4162
|
+
* automate your own workspace, keep using an API key and ignore everything
|
|
4163
|
+
* here.
|
|
4164
|
+
*
|
|
4165
|
+
* Two hosts are involved on purpose: the consent page lives on the
|
|
4166
|
+
* authorization server (`https://auth.assinafy.com.br`) while the token,
|
|
4167
|
+
* revocation and userinfo endpoints live on this API. Both are published by
|
|
4168
|
+
* {@link OAuthResource.getAuthorizationServerMetadata}, so nothing needs
|
|
4169
|
+
* hardcoding.
|
|
4170
|
+
*
|
|
4171
|
+
* The full round trip:
|
|
4172
|
+
*
|
|
4173
|
+
* 1. {@link OAuthResource.createAuthorizationUrl} — mint PKCE + `state`, build
|
|
4174
|
+
* the consent URL, store the returned request in the user's session.
|
|
4175
|
+
* 2. Redirect the browser there; the user picks **one** workspace and approves.
|
|
4176
|
+
* 3. {@link OAuthResource.readAuthorizationCallback} — check `state` and `iss`
|
|
4177
|
+
* on your redirect URI, and surface a declined consent as an
|
|
4178
|
+
* {@link OAuthError}.
|
|
4179
|
+
* 4. {@link OAuthResource.exchangeCode} — swap the 60-second code for tokens.
|
|
4180
|
+
* 5. Build a per-connection client with that token and read the one workspace
|
|
4181
|
+
* it covers:
|
|
4182
|
+
* ```ts
|
|
4183
|
+
* const connected = new AssinafyClient({ token: tokens.access_token });
|
|
4184
|
+
* const { data } = await connected.workspaces.list();
|
|
4185
|
+
* const accountId = data[0]?.id;
|
|
4186
|
+
* ```
|
|
4187
|
+
* 6. {@link OAuthResource.refreshToken} before the hour is up (requires
|
|
4188
|
+
* `offline_access`), and {@link OAuthResource.revokeToken} when the user
|
|
4189
|
+
* disconnects.
|
|
4190
|
+
*
|
|
4191
|
+
* Two facts that cause most integration bugs: a token works for exactly one
|
|
4192
|
+
* workspace (any other answers `403`), and a connection expires 30 days after
|
|
4193
|
+
* approval no matter how often it is refreshed.
|
|
4194
|
+
*
|
|
4195
|
+
* @example
|
|
4196
|
+
* ```ts
|
|
4197
|
+
* const client = new AssinafyClient(); // no credentials needed
|
|
4198
|
+
*
|
|
4199
|
+
* // Step 1 — before redirecting the user
|
|
4200
|
+
* const request = await client.oauth.createAuthorizationUrl({
|
|
4201
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4202
|
+
* redirectUri: 'https://myapp.com/oauth/callback',
|
|
4203
|
+
* scopes: ['documents:read', 'documents:write', 'offline_access'],
|
|
4204
|
+
* });
|
|
4205
|
+
* session.oauth = request; // state + codeVerifier + issuer
|
|
4206
|
+
* response.redirect(request.url);
|
|
4207
|
+
*
|
|
4208
|
+
* // Step 3/4 — on https://myapp.com/oauth/callback
|
|
4209
|
+
* const { code } = client.oauth.readAuthorizationCallback(query, session.oauth);
|
|
4210
|
+
* const tokens = await client.oauth.exchangeCode({
|
|
4211
|
+
* code,
|
|
4212
|
+
* codeVerifier: session.oauth.codeVerifier,
|
|
4213
|
+
* redirectUri: 'https://myapp.com/oauth/callback',
|
|
4214
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4215
|
+
* clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
|
|
4216
|
+
* });
|
|
4217
|
+
* ```
|
|
4218
|
+
*/
|
|
4219
|
+
declare class OAuthResource extends BaseResource {
|
|
4220
|
+
private readonly publicHttp;
|
|
4221
|
+
constructor(http: AxiosInstance, defaultAccountId?: string, logger?: Logger, publicHttp?: AxiosInstance);
|
|
4222
|
+
/**
|
|
4223
|
+
* Read this API's protected-resource metadata
|
|
4224
|
+
* (`GET /.well-known/oauth-protected-resource`).
|
|
4225
|
+
*
|
|
4226
|
+
* Served at the API host root — not under `/v1` — and bare, without the
|
|
4227
|
+
* `{ status, message, data }` envelope, as RFC 8615 requires. Use it to
|
|
4228
|
+
* discover which authorization server may issue tokens for this API and
|
|
4229
|
+
* which scopes it accepts.
|
|
4230
|
+
*
|
|
4231
|
+
* Request body: none. Authentication: none.
|
|
4232
|
+
*
|
|
4233
|
+
* @returns The metadata document:
|
|
4234
|
+
* ```jsonc
|
|
4235
|
+
* {
|
|
4236
|
+
* "resource": "https://api.assinafy.com.br",
|
|
4237
|
+
* "authorization_servers": ["https://auth.assinafy.com.br"],
|
|
4238
|
+
* "scopes_supported": [
|
|
4239
|
+
* "documents:read", "documents:write",
|
|
4240
|
+
* "templates:read", "templates:write",
|
|
4241
|
+
* "account:read", "openid", "profile", "email"
|
|
4242
|
+
* ],
|
|
4243
|
+
* "bearer_methods_supported": ["header"]
|
|
4244
|
+
* }
|
|
4245
|
+
* ```
|
|
4246
|
+
* `offline_access` is deliberately absent: it is a request-time signal to
|
|
4247
|
+
* the authorization server, not a permission this API enforces.
|
|
4248
|
+
* @throws {ApiError} If the host does not publish the document.
|
|
4249
|
+
*
|
|
4250
|
+
* @example
|
|
4251
|
+
* ```ts
|
|
4252
|
+
* const metadata = await client.oauth.getProtectedResourceMetadata();
|
|
4253
|
+
* console.log(metadata.authorization_servers[0]);
|
|
4254
|
+
* ```
|
|
4255
|
+
*/
|
|
4256
|
+
getProtectedResourceMetadata(): Promise<IOAuthProtectedResourceMetadata>;
|
|
4257
|
+
/**
|
|
4258
|
+
* Read the authorization server's metadata
|
|
4259
|
+
* (`GET {issuer}/.well-known/oauth-authorization-server`, RFC 8414).
|
|
4260
|
+
*
|
|
4261
|
+
* Every endpoint URL an OAuth client needs comes from here, so nothing has
|
|
4262
|
+
* to be hardcoded. The document is served by the authorization server, a
|
|
4263
|
+
* different host from this API.
|
|
4264
|
+
*
|
|
4265
|
+
* @param issuer - Issuer to read. Defaults to the first entry of
|
|
4266
|
+
* {@link OAuthResource.getProtectedResourceMetadata}, which costs one extra
|
|
4267
|
+
* request — pass the issuer to skip it.
|
|
4268
|
+
* @returns The metadata document:
|
|
4269
|
+
* ```jsonc
|
|
4270
|
+
* {
|
|
4271
|
+
* "issuer": "https://auth.assinafy.com.br",
|
|
4272
|
+
* "authorization_endpoint": "https://auth.assinafy.com.br/oauth/authorize",
|
|
4273
|
+
* "token_endpoint": "https://api.assinafy.com.br/v1/oauth/token",
|
|
4274
|
+
* "revocation_endpoint": "https://api.assinafy.com.br/v1/oauth/revoke",
|
|
4275
|
+
* "userinfo_endpoint": "https://api.assinafy.com.br/v1/oauth/userinfo",
|
|
4276
|
+
* "jwks_uri": "https://auth.assinafy.com.br/.well-known/jwks.json",
|
|
4277
|
+
* "scopes_supported": ["documents:read", "documents:write", "templates:read",
|
|
4278
|
+
* "templates:write", "account:read", "openid",
|
|
4279
|
+
* "profile", "email", "offline_access"],
|
|
4280
|
+
* "response_types_supported": ["code"],
|
|
4281
|
+
* "grant_types_supported": ["authorization_code", "refresh_token"],
|
|
4282
|
+
* "code_challenge_methods_supported": ["S256"],
|
|
4283
|
+
* "token_endpoint_auth_methods_supported": ["client_secret_post", "none"],
|
|
4284
|
+
* "authorization_response_iss_parameter_supported": true,
|
|
4285
|
+
* "client_id_metadata_document_supported": true
|
|
4286
|
+
* }
|
|
4287
|
+
* ```
|
|
4288
|
+
* @throws {ValidationError} If `issuer` is not an absolute `https://` URL,
|
|
4289
|
+
* or the document's own `issuer` disagrees with where it was fetched from
|
|
4290
|
+
* (RFC 8414 §3.3 — a mismatch means the document is not authoritative).
|
|
4291
|
+
* @throws {ApiError} If the authorization server rejects the request.
|
|
4292
|
+
*
|
|
4293
|
+
* @example
|
|
4294
|
+
* ```ts
|
|
4295
|
+
* const as = await client.oauth.getAuthorizationServerMetadata();
|
|
4296
|
+
* console.log(as.authorization_endpoint);
|
|
4297
|
+
* ```
|
|
4298
|
+
*/
|
|
4299
|
+
getAuthorizationServerMetadata(issuer?: string): Promise<IOAuthAuthorizationServerMetadata>;
|
|
4300
|
+
/**
|
|
4301
|
+
* Mint a PKCE pair and a `state`, then build the consent URL to send the
|
|
4302
|
+
* user's browser to (`GET {authorization_endpoint}`).
|
|
4303
|
+
*
|
|
4304
|
+
* Call this once per connection attempt and keep the whole returned object
|
|
4305
|
+
* in the user's session: reusing a verifier or a `state` across attempts
|
|
4306
|
+
* defeats both PKCE and CSRF protection. Navigate the browser to `url` with
|
|
4307
|
+
* a full page load — an `fetch`/XHR cannot show a consent screen.
|
|
4308
|
+
*
|
|
4309
|
+
* PKCE is mandatory for confidential applications too, and Assinafy accepts
|
|
4310
|
+
* only the `S256` challenge method.
|
|
4311
|
+
*
|
|
4312
|
+
* @param options - Authorization-request options.
|
|
4313
|
+
* @param options.clientId - The application's `client_id`.
|
|
4314
|
+
* @param options.redirectUri - One of the application's registered redirect
|
|
4315
|
+
* URIs, matched character for character (`…/callback` and `…/callback/` are
|
|
4316
|
+
* different). Must be `https://` and carry no fragment.
|
|
4317
|
+
* @param options.scopes - Permissions to request, e.g.
|
|
4318
|
+
* `['documents:read', 'documents:write', 'offline_access']`. Ask for the
|
|
4319
|
+
* minimum: the user approves all of them or none. Add `offline_access` to
|
|
4320
|
+
* receive a refresh token and `openid` to receive an `id_token`.
|
|
4321
|
+
* @param options.authorizationEndpoint - Skip discovery by supplying the
|
|
4322
|
+
* endpoint yourself. Defaults to the discovered
|
|
4323
|
+
* `authorization_endpoint`.
|
|
4324
|
+
* @param options.issuer - Issuer to discover from, and the value the
|
|
4325
|
+
* callback's `iss` must equal. Defaults to the discovered issuer.
|
|
4326
|
+
* @param options.resource - RFC 8707 resource indicator. Defaults to this
|
|
4327
|
+
* API's origin; pass `null` to omit it. It must match the value sent to the
|
|
4328
|
+
* token endpoint, or the exchange fails with `invalid_target`.
|
|
4329
|
+
* @param options.state - Supply your own CSRF value instead of a generated
|
|
4330
|
+
* one. Must be unique per attempt.
|
|
4331
|
+
* @param options.codeVerifier - Supply your own RFC 7636 verifier (43–128
|
|
4332
|
+
* characters from `A-Z a-z 0-9 - . _ ~`) instead of a generated one.
|
|
4333
|
+
* @param options.nonce - OIDC nonce echoed in the `id_token`. Generated
|
|
4334
|
+
* automatically when `openid` is requested; pass a string to set it or
|
|
4335
|
+
* `null` to omit it.
|
|
4336
|
+
* @param options.prompt - Forwarded as the OIDC `prompt` parameter, e.g.
|
|
4337
|
+
* `'consent'` to force the approval screen again.
|
|
4338
|
+
* @returns The request to store and redirect with:
|
|
4339
|
+
* ```jsonc
|
|
4340
|
+
* {
|
|
4341
|
+
* "url": "https://auth.assinafy.com.br/oauth/authorize?response_type=code&client_id=…&redirect_uri=https%3A%2F%2Fmyapp.com%2Foauth%2Fcallback&scope=documents%3Aread+offline_access&state=8Xv…&code_challenge=E9M…&code_challenge_method=S256&resource=https%3A%2F%2Fapi.assinafy.com.br",
|
|
4342
|
+
* "state": "8Xv2rQ7mJt0aLpKcWn4dZg",
|
|
4343
|
+
* "codeVerifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
|
4344
|
+
* "issuer": "https://auth.assinafy.com.br",
|
|
4345
|
+
* "nonce": "n-0S6_WzA2Mj"
|
|
4346
|
+
* }
|
|
4347
|
+
* ```
|
|
4348
|
+
* @throws {ValidationError} If `clientId` is empty, `redirectUri` is not an
|
|
4349
|
+
* absolute `https://` URL without a fragment, `scopes` is empty or contains
|
|
4350
|
+
* a value with whitespace, or a supplied `codeVerifier`/`state` is invalid.
|
|
4351
|
+
* @throws {ApiError} If discovery is needed and fails.
|
|
4352
|
+
*
|
|
4353
|
+
* @example
|
|
4354
|
+
* ```ts
|
|
4355
|
+
* const request = await client.oauth.createAuthorizationUrl({
|
|
4356
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4357
|
+
* redirectUri: 'https://myapp.com/oauth/callback',
|
|
4358
|
+
* scopes: ['documents:read', 'documents:write', 'offline_access'],
|
|
4359
|
+
* });
|
|
4360
|
+
* session.oauth = request;
|
|
4361
|
+
* response.redirect(request.url);
|
|
4362
|
+
* ```
|
|
4363
|
+
*/
|
|
4364
|
+
createAuthorizationUrl(options: {
|
|
4365
|
+
clientId: string;
|
|
4366
|
+
redirectUri: string;
|
|
4367
|
+
scopes: OAuthScope[];
|
|
4368
|
+
authorizationEndpoint?: string;
|
|
4369
|
+
issuer?: string;
|
|
4370
|
+
resource?: string | null;
|
|
4371
|
+
state?: string;
|
|
4372
|
+
codeVerifier?: string;
|
|
4373
|
+
nonce?: string | null;
|
|
4374
|
+
prompt?: string;
|
|
4375
|
+
}): Promise<IOAuthAuthorizationRequest>;
|
|
4376
|
+
/**
|
|
4377
|
+
* Validate the authorization response that lands on your redirect URI and
|
|
4378
|
+
* return the code to exchange.
|
|
4379
|
+
*
|
|
4380
|
+
* Checks, in order and before anything else is trusted: `state` equals the
|
|
4381
|
+
* value from {@link OAuthResource.createAuthorizationUrl} (constant-time),
|
|
4382
|
+
* `iss` is present and equals the expected issuer, and only then whether
|
|
4383
|
+
* the server reported an error. A declined consent arrives as
|
|
4384
|
+
* `?error=access_denied`, not as a failed HTTP request.
|
|
4385
|
+
*
|
|
4386
|
+
* The `iss` check is strict because the authorization server advertises
|
|
4387
|
+
* RFC 9207 support and always sends the parameter: a missing `iss` is
|
|
4388
|
+
* treated exactly like a wrong one. Omit `expected.issuer` only if
|
|
4389
|
+
* something between the browser and your handler strips query parameters.
|
|
4390
|
+
*
|
|
4391
|
+
* This performs no network I/O.
|
|
4392
|
+
*
|
|
4393
|
+
* @param params - The callback's query parameters. Accepts an Express-style
|
|
4394
|
+
* `req.query` record, a `URLSearchParams`, a `URL`, a full callback URL
|
|
4395
|
+
* string, or a bare `a=b&c=d` query string.
|
|
4396
|
+
* @param expected - The stored {@link IOAuthAuthorizationRequest} (or any
|
|
4397
|
+
* object carrying its `state` and `issuer`).
|
|
4398
|
+
* @returns The validated response:
|
|
4399
|
+
* ```jsonc
|
|
4400
|
+
* {
|
|
4401
|
+
* "code": "def50200a1b2c3…",
|
|
4402
|
+
* "state": "8Xv2rQ7mJt0aLpKcWn4dZg",
|
|
4403
|
+
* "issuer": "https://auth.assinafy.com.br"
|
|
4404
|
+
* }
|
|
4405
|
+
* ```
|
|
4406
|
+
* @throws {ValidationError} If `state` is missing or does not match, `iss`
|
|
4407
|
+
* is absent or disagrees with the expected issuer, or a successful response
|
|
4408
|
+
* carries no `code`. In every case the response is not yours — stop, do not
|
|
4409
|
+
* exchange.
|
|
4410
|
+
* @throws {OAuthError} If the server returned `error` (e.g.
|
|
4411
|
+
* `access_denied`, `invalid_scope`, `invalid_request`,
|
|
4412
|
+
* `unsupported_response_type`, `invalid_target`).
|
|
4413
|
+
*
|
|
4414
|
+
* @example
|
|
4415
|
+
* ```ts
|
|
4416
|
+
* app.get('/oauth/callback', async (req, res) => {
|
|
4417
|
+
* const stored = req.session.oauth;
|
|
4418
|
+
* const { code } = client.oauth.readAuthorizationCallback(req.query, stored);
|
|
4419
|
+
* const tokens = await client.oauth.exchangeCode({
|
|
4420
|
+
* code,
|
|
4421
|
+
* codeVerifier: stored.codeVerifier,
|
|
4422
|
+
* redirectUri: 'https://myapp.com/oauth/callback',
|
|
4423
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4424
|
+
* clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
|
|
4425
|
+
* });
|
|
4426
|
+
* });
|
|
4427
|
+
* ```
|
|
4428
|
+
*/
|
|
4429
|
+
readAuthorizationCallback(params: string | URL | URLSearchParams | Record<string, unknown>, expected: {
|
|
4430
|
+
state: string;
|
|
4431
|
+
issuer?: string;
|
|
4432
|
+
}): IOAuthAuthorizationCallback;
|
|
4433
|
+
/**
|
|
4434
|
+
* Exchange an authorization code for tokens
|
|
4435
|
+
* (`POST /oauth/token`, `grant_type=authorization_code`).
|
|
4436
|
+
*
|
|
4437
|
+
* Run this on your server: the code is single-use and expires **60 seconds**
|
|
4438
|
+
* after approval, and a confidential application's secret must never reach
|
|
4439
|
+
* a browser. Every value must match the authorization request exactly, or
|
|
4440
|
+
* the API answers `invalid_grant`.
|
|
4441
|
+
*
|
|
4442
|
+
* @param options - Exchange options.
|
|
4443
|
+
* @param options.code - The code from
|
|
4444
|
+
* {@link OAuthResource.readAuthorizationCallback}.
|
|
4445
|
+
* @param options.codeVerifier - The verifier stored alongside the request.
|
|
4446
|
+
* @param options.redirectUri - The same redirect URI that was authorized.
|
|
4447
|
+
* @param options.clientId - The application's `client_id`.
|
|
4448
|
+
* @param options.clientSecret - The `client_secret`, for confidential
|
|
4449
|
+
* applications only. Public applications omit it and rely on PKCE.
|
|
4450
|
+
* @param options.resource - The same RFC 8707 resource indicator sent to
|
|
4451
|
+
* the authorization endpoint. Defaults to this API's origin; pass `null` to
|
|
4452
|
+
* omit it. A value disagreeing with the authorized one fails with
|
|
4453
|
+
* `invalid_target`.
|
|
4454
|
+
* @returns The token set — a flat object, **not** the API's usual envelope:
|
|
4455
|
+
* ```jsonc
|
|
4456
|
+
* {
|
|
4457
|
+
* "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…",
|
|
4458
|
+
* "token_type": "Bearer",
|
|
4459
|
+
* "expires_in": 3600,
|
|
4460
|
+
* "scope": "documents:read documents:write",
|
|
4461
|
+
* "refresh_token": "def5020088c2…", // only with offline_access
|
|
4462
|
+
* "id_token": "eyJraWQiOiJEQlR0S0…" // only with openid
|
|
4463
|
+
* }
|
|
4464
|
+
* ```
|
|
4465
|
+
* Read `scope` rather than assuming every requested permission was granted.
|
|
4466
|
+
* @throws {ValidationError} If an argument is missing or malformed, or a
|
|
4467
|
+
* `2xx` response carries no `access_token`.
|
|
4468
|
+
* @throws {OAuthError} `invalid_grant` for a spent, expired, replayed or
|
|
4469
|
+
* mismatched code; `invalid_client` for a bad `client_id`/`client_secret`;
|
|
4470
|
+
* `invalid_target` for a `resource` mismatch.
|
|
4471
|
+
*
|
|
4472
|
+
* @example
|
|
4473
|
+
* ```ts
|
|
4474
|
+
* const tokens = await client.oauth.exchangeCode({
|
|
4475
|
+
* code,
|
|
4476
|
+
* codeVerifier: session.oauth.codeVerifier,
|
|
4477
|
+
* redirectUri: 'https://myapp.com/oauth/callback',
|
|
4478
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4479
|
+
* clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
|
|
4480
|
+
* });
|
|
4481
|
+
* ```
|
|
4482
|
+
*/
|
|
4483
|
+
exchangeCode(options: TokenEndpointAuthOptions & {
|
|
4484
|
+
code: string;
|
|
4485
|
+
codeVerifier: string;
|
|
4486
|
+
redirectUri: string;
|
|
4487
|
+
resource?: string | null;
|
|
4488
|
+
}): Promise<IOAuthTokenResponse>;
|
|
4489
|
+
/**
|
|
4490
|
+
* Renew an access token (`POST /oauth/token`, `grant_type=refresh_token`).
|
|
4491
|
+
*
|
|
4492
|
+
* Access tokens last one hour; refresh tokens are available only when
|
|
4493
|
+
* `offline_access` was requested and granted.
|
|
4494
|
+
*
|
|
4495
|
+
* **Refresh tokens rotate.** Every call returns a new one and retires the
|
|
4496
|
+
* one you sent, and a replayed refresh token cannot be told apart from a
|
|
4497
|
+
* stolen one — so the server ends the entire connection and the user must
|
|
4498
|
+
* reconnect. Therefore: persist `refresh_token` from the response before
|
|
4499
|
+
* doing anything else with it, treat a timeout as "it may have succeeded"
|
|
4500
|
+
* and re-read your stored token instead of retrying blindly, and never run
|
|
4501
|
+
* two refreshes concurrently for one connection.
|
|
4502
|
+
*
|
|
4503
|
+
* Refreshing does not extend the connection's 30-day life.
|
|
4504
|
+
*
|
|
4505
|
+
* @param options - Refresh options.
|
|
4506
|
+
* @param options.refreshToken - The current refresh token.
|
|
4507
|
+
* @param options.clientId - The application's `client_id`.
|
|
4508
|
+
* @param options.clientSecret - The `client_secret`, for confidential
|
|
4509
|
+
* applications only.
|
|
4510
|
+
* @param options.resource - RFC 8707 resource indicator. Defaults to this
|
|
4511
|
+
* API's origin; pass `null` to omit it.
|
|
4512
|
+
* @returns A fresh token set, identical in shape to
|
|
4513
|
+
* {@link OAuthResource.exchangeCode}:
|
|
4514
|
+
* ```jsonc
|
|
4515
|
+
* {
|
|
4516
|
+
* "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…",
|
|
4517
|
+
* "token_type": "Bearer",
|
|
4518
|
+
* "expires_in": 3600,
|
|
4519
|
+
* "scope": "documents:read documents:write",
|
|
4520
|
+
* "refresh_token": "def50200f1e2…" // NEW — persist it immediately
|
|
4521
|
+
* }
|
|
4522
|
+
* ```
|
|
4523
|
+
* @throws {ValidationError} If an argument is missing, or a `2xx` response
|
|
4524
|
+
* carries no `access_token`.
|
|
4525
|
+
* @throws {OAuthError} `invalid_grant` when the refresh token was already
|
|
4526
|
+
* used, expired, or the user reconnected with different permissions — ask
|
|
4527
|
+
* the user to reconnect. `invalid_client` for bad client credentials.
|
|
4528
|
+
*
|
|
4529
|
+
* @example
|
|
4530
|
+
* ```ts
|
|
4531
|
+
* const tokens = await client.oauth.refreshToken({
|
|
4532
|
+
* refreshToken: connection.refreshToken,
|
|
4533
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4534
|
+
* clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
|
|
4535
|
+
* });
|
|
4536
|
+
* await connection.save({ refreshToken: tokens.refresh_token });
|
|
4537
|
+
* ```
|
|
4538
|
+
*/
|
|
4539
|
+
refreshToken(options: TokenEndpointAuthOptions & {
|
|
4540
|
+
refreshToken: string;
|
|
4541
|
+
resource?: string | null;
|
|
4542
|
+
}): Promise<IOAuthTokenResponse>;
|
|
4543
|
+
/**
|
|
4544
|
+
* Revoke an access or refresh token (`POST /oauth/revoke`, RFC 7009).
|
|
4545
|
+
*
|
|
4546
|
+
* Call this when a user disconnects your app, instead of only deleting your
|
|
4547
|
+
* copy of the token. Revoking a refresh token ends the whole connection.
|
|
4548
|
+
*
|
|
4549
|
+
* Every token outcome answers `200` — unknown, malformed and
|
|
4550
|
+
* already-revoked included — so the endpoint cannot be used to probe
|
|
4551
|
+
* whether a token exists. Only failed client authentication returns `401`.
|
|
4552
|
+
*
|
|
4553
|
+
* @param options - Revocation options.
|
|
4554
|
+
* @param options.token - The access or refresh token to revoke.
|
|
4555
|
+
* @param options.clientId - The application's `client_id`.
|
|
4556
|
+
* @param options.clientSecret - The `client_secret`, for confidential
|
|
4557
|
+
* applications only.
|
|
4558
|
+
* @param options.tokenTypeHint - Optional `access_token` or
|
|
4559
|
+
* `refresh_token` hint that lets the server skip a lookup.
|
|
4560
|
+
* @returns Nothing; resolves once the API acknowledges the request.
|
|
4561
|
+
* Request body:
|
|
4562
|
+
* ```jsonc
|
|
4563
|
+
* {
|
|
4564
|
+
* "token": "def50200f1e2…",
|
|
4565
|
+
* "token_type_hint": "refresh_token",
|
|
4566
|
+
* "client_id": "cli_1a2b3c",
|
|
4567
|
+
* "client_secret": "…"
|
|
4568
|
+
* }
|
|
4569
|
+
* ```
|
|
4570
|
+
* @throws {ValidationError} If `token` or `clientId` is missing, or
|
|
4571
|
+
* `tokenTypeHint` is not one of the two documented values.
|
|
4572
|
+
* @throws {OAuthError} `invalid_client` when client authentication fails.
|
|
4573
|
+
*
|
|
4574
|
+
* @example
|
|
4575
|
+
* ```ts
|
|
4576
|
+
* await client.oauth.revokeToken({
|
|
4577
|
+
* token: connection.refreshToken,
|
|
4578
|
+
* tokenTypeHint: 'refresh_token',
|
|
4579
|
+
* clientId: process.env.ASSINAFY_CLIENT_ID!,
|
|
4580
|
+
* clientSecret: process.env.ASSINAFY_CLIENT_SECRET,
|
|
4581
|
+
* });
|
|
4582
|
+
* ```
|
|
4583
|
+
*/
|
|
4584
|
+
revokeToken(options: TokenEndpointAuthOptions & {
|
|
4585
|
+
token: string;
|
|
4586
|
+
tokenTypeHint?: 'access_token' | 'refresh_token';
|
|
4587
|
+
}): Promise<void>;
|
|
4588
|
+
/**
|
|
4589
|
+
* Read the OpenID Connect claims of the user who authorized a token
|
|
4590
|
+
* (`GET /oauth/userinfo`).
|
|
4591
|
+
*
|
|
4592
|
+
* Requires the `openid` scope; `name` additionally requires `profile` and
|
|
4593
|
+
* `email`/`email_verified` require `email`. Per OIDC Core §5.3.2 the
|
|
4594
|
+
* response is a flat claims object, not this API's usual envelope.
|
|
4595
|
+
*
|
|
4596
|
+
* @param accessToken - Token to introspect. Omit to use the credential the
|
|
4597
|
+
* client was constructed with (`token` or `apiKey`).
|
|
4598
|
+
* @returns The claims the granted scopes allow:
|
|
4599
|
+
* ```jsonc
|
|
4600
|
+
* {
|
|
4601
|
+
* "sub": "d6zqpbyog2v3xvxerwn8la94",
|
|
4602
|
+
* "name": "Maria Silva",
|
|
4603
|
+
* "email": "maria@example.com",
|
|
4604
|
+
* "email_verified": true
|
|
4605
|
+
* }
|
|
4606
|
+
* ```
|
|
4607
|
+
* `sub` is the stable user identifier; the rest are `null` when their scope
|
|
4608
|
+
* was not granted.
|
|
4609
|
+
* @throws {ValidationError} If `accessToken` is supplied but empty.
|
|
4610
|
+
* @throws {ApiError} `401` when the token is missing, expired or revoked;
|
|
4611
|
+
* `403` when the `openid` scope was not granted — its `WWW-Authenticate`
|
|
4612
|
+
* header names the scope to reconnect with.
|
|
4613
|
+
*
|
|
4614
|
+
* @example
|
|
4615
|
+
* ```ts
|
|
4616
|
+
* const who = await client.oauth.getUserInfo(tokens.access_token);
|
|
4617
|
+
* console.log(who.sub, who.email);
|
|
4618
|
+
* ```
|
|
4619
|
+
*/
|
|
4620
|
+
getUserInfo(accessToken?: string): Promise<IOAuthUserInfo>;
|
|
4621
|
+
/** POST the token endpoint and assert the response actually carries a token. */
|
|
4622
|
+
private requestToken;
|
|
4623
|
+
/** `client_secret_post` credentials, omitting the secret for public clients. */
|
|
4624
|
+
private clientAuth;
|
|
4625
|
+
/**
|
|
4626
|
+
* Resolve the optional RFC 8707 `resource` indicator.
|
|
4627
|
+
*
|
|
4628
|
+
* Defaults to the configured API origin, which is what this API publishes
|
|
4629
|
+
* as its `resource`. A loopback `http://` base URL — the shape used by mock
|
|
4630
|
+
* servers and the packed-consumer smoke test — has no valid resource
|
|
4631
|
+
* identifier, so the parameter is simply omitted rather than rejected; an
|
|
4632
|
+
* explicitly supplied value is still required to be `https`.
|
|
4633
|
+
*/
|
|
4634
|
+
private resourceParam;
|
|
4635
|
+
/** Discover which authorization server may issue tokens for this API. */
|
|
4636
|
+
private defaultIssuer;
|
|
4637
|
+
/**
|
|
4638
|
+
* Origin of the configured API host.
|
|
4639
|
+
*
|
|
4640
|
+
* The `.well-known` document and the RFC 8707 resource indicator both sit
|
|
4641
|
+
* at the host root, while `baseUrl` points at `/v1`.
|
|
4642
|
+
*/
|
|
4643
|
+
private apiOrigin;
|
|
4644
|
+
}
|
|
4645
|
+
|
|
3989
4646
|
/**
|
|
3990
4647
|
* Custom field definitions used by `collect` assignments.
|
|
3991
4648
|
*
|
|
@@ -5036,6 +5693,7 @@ declare class AssinafyClient {
|
|
|
5036
5693
|
readonly templates: TemplateResource;
|
|
5037
5694
|
readonly tags: TagResource;
|
|
5038
5695
|
readonly auth: AuthenticationResource;
|
|
5696
|
+
readonly oauth: OAuthResource;
|
|
5039
5697
|
readonly fields: FieldsResource;
|
|
5040
5698
|
readonly signerDocuments: SignerDocumentsResource;
|
|
5041
5699
|
readonly users: UserResource;
|
|
@@ -5253,6 +5911,40 @@ declare class AssinafyClient {
|
|
|
5253
5911
|
getAxiosInstance(): AxiosInstance;
|
|
5254
5912
|
}
|
|
5255
5913
|
|
|
5914
|
+
/**
|
|
5915
|
+
* One parsed `WWW-Authenticate` challenge.
|
|
5916
|
+
*
|
|
5917
|
+
* Assinafy answers an OAuth request that is missing a scope with
|
|
5918
|
+
* `403` and `WWW-Authenticate: Bearer error="insufficient_scope",
|
|
5919
|
+
* scope="documents:write", resource_metadata="…"`. `scope` names the permission
|
|
5920
|
+
* to request on the next authorization round-trip, so the challenge is the only
|
|
5921
|
+
* machine-readable way to tell "reconnect asking for more" apart from "this
|
|
5922
|
+
* token can never reach that surface".
|
|
5923
|
+
*/
|
|
5924
|
+
interface IAuthenticateChallenge {
|
|
5925
|
+
/** Authentication scheme, e.g. `Bearer`. */
|
|
5926
|
+
scheme: string;
|
|
5927
|
+
/** RFC 6750 error code, e.g. `insufficient_scope` or `invalid_token`. */
|
|
5928
|
+
error?: string;
|
|
5929
|
+
/** Human-readable explanation, when the server sends one. */
|
|
5930
|
+
error_description?: string;
|
|
5931
|
+
/** Space-separated scopes required by the rejected operation. */
|
|
5932
|
+
scope?: string;
|
|
5933
|
+
/** RFC 9728 URL of the protected-resource metadata document. */
|
|
5934
|
+
resource_metadata?: string;
|
|
5935
|
+
}
|
|
5936
|
+
/**
|
|
5937
|
+
* Parse a `WWW-Authenticate` header into its scheme and auth-param map.
|
|
5938
|
+
*
|
|
5939
|
+
* Only the first challenge is read: Assinafy sends exactly one, and a parser
|
|
5940
|
+
* that split on commas would corrupt quoted values containing them.
|
|
5941
|
+
*
|
|
5942
|
+
* @param value - Raw header value, or `undefined` when absent.
|
|
5943
|
+
* @returns The parsed challenge, or `undefined` when there is no header or it
|
|
5944
|
+
* carries no scheme.
|
|
5945
|
+
*/
|
|
5946
|
+
declare function parseWwwAuthenticate(value: string | undefined): IAuthenticateChallenge | undefined;
|
|
5947
|
+
|
|
5256
5948
|
/** Base class for all Assinafy SDK errors. */
|
|
5257
5949
|
declare class AssinafyError extends Error {
|
|
5258
5950
|
readonly context: Record<string, unknown>;
|
|
@@ -5274,6 +5966,29 @@ declare class AssinafyError extends Error {
|
|
|
5274
5966
|
declare class ApiError extends AssinafyError {
|
|
5275
5967
|
readonly statusCode: number;
|
|
5276
5968
|
readonly responseData: unknown;
|
|
5969
|
+
/**
|
|
5970
|
+
* Parsed `WWW-Authenticate` challenge, when the response carried one.
|
|
5971
|
+
*
|
|
5972
|
+
* A `403` whose challenge is `{ error: 'insufficient_scope', scope: '…' }`
|
|
5973
|
+
* means the OAuth token is valid but was never granted that permission:
|
|
5974
|
+
* send the user through the authorization flow again asking for the scope
|
|
5975
|
+
* named in `scope`. A `403` without a challenge has a different cause —
|
|
5976
|
+
* another workspace, the user's role, or a surface OAuth tokens never
|
|
5977
|
+
* reach — and reconnecting will not fix it.
|
|
5978
|
+
*
|
|
5979
|
+
* @example
|
|
5980
|
+
* ```ts
|
|
5981
|
+
* try {
|
|
5982
|
+
* await connected.documents.upload({ filePath: './contract.pdf' });
|
|
5983
|
+
* } catch (error) {
|
|
5984
|
+
* if (error instanceof ApiError && error.challenge?.error === 'insufficient_scope') {
|
|
5985
|
+
* return reconnect(error.challenge.scope); // 'documents:write'
|
|
5986
|
+
* }
|
|
5987
|
+
* throw error;
|
|
5988
|
+
* }
|
|
5989
|
+
* ```
|
|
5990
|
+
*/
|
|
5991
|
+
challenge?: IAuthenticateChallenge;
|
|
5277
5992
|
/**
|
|
5278
5993
|
* Create an error representing a non-success API response.
|
|
5279
5994
|
*
|
|
@@ -5287,9 +6002,14 @@ declare class ApiError extends AssinafyError {
|
|
|
5287
6002
|
* Convert a status/body pair into an {@link ApiError}.
|
|
5288
6003
|
*
|
|
5289
6004
|
* @param statusCode - Non-success HTTP response status.
|
|
5290
|
-
* @param responseData -
|
|
5291
|
-
* followed by string `error
|
|
5292
|
-
*
|
|
6005
|
+
* @param responseData - API body. For a JSON object, string `message` takes
|
|
6006
|
+
* priority, followed by string `error`. A non-JSON body (a proxy's
|
|
6007
|
+
* `text/plain` or HTML error page) is used verbatim rather than discarded —
|
|
6008
|
+
* otherwise the only failures reported as the generic fallback would be the
|
|
6009
|
+
* ones with no structured body to explain them. Anything else falls back to
|
|
6010
|
+
* the stable message.
|
|
6011
|
+
* @returns An `ApiError` retaining the original response body in
|
|
6012
|
+
* {@link ApiError.responseData}; `message` is truncated for legibility.
|
|
5293
6013
|
*
|
|
5294
6014
|
* @example
|
|
5295
6015
|
* ```ts
|
|
@@ -5299,6 +6019,59 @@ declare class ApiError extends AssinafyError {
|
|
|
5299
6019
|
*/
|
|
5300
6020
|
static fromResponse(statusCode: number, responseData: unknown): ApiError;
|
|
5301
6021
|
}
|
|
6022
|
+
/**
|
|
6023
|
+
* Thrown when an OAuth endpoint returns an RFC 6749 error object, or when an
|
|
6024
|
+
* authorization response comes back on the redirect URI carrying `?error=`.
|
|
6025
|
+
*
|
|
6026
|
+
* The OAuth endpoints answer with a flat `{ error, error_description }` body
|
|
6027
|
+
* instead of this API's `{ status, message, data }` envelope, because no
|
|
6028
|
+
* standard OAuth client would look for `error` inside a `data` key. This class
|
|
6029
|
+
* still extends {@link ApiError}, so existing `catch (err) { if (err instanceof
|
|
6030
|
+
* ApiError) … }` blocks keep matching.
|
|
6031
|
+
*
|
|
6032
|
+
* Branch on {@link OAuthError.error}, not on the message:
|
|
6033
|
+
*
|
|
6034
|
+
* | `error` | What to do |
|
|
6035
|
+
* | --- | --- |
|
|
6036
|
+
* | `invalid_grant` | The code or refresh token is spent, expired, or bound to other parameters. Send the user through the authorization flow again. |
|
|
6037
|
+
* | `invalid_client` | Wrong `client_id`/`client_secret`, or the application was disabled. Fix the configuration; retrying will not help. |
|
|
6038
|
+
* | `invalid_target` | The `resource` does not match the one that was authorized. |
|
|
6039
|
+
* | `unsupported_grant_type` | Only `authorization_code` and `refresh_token` exist. |
|
|
6040
|
+
* | `access_denied` | The user declined on the consent screen. |
|
|
6041
|
+
* | `invalid_scope` | A scope the application is not registered for. |
|
|
6042
|
+
* | `invalid_request` | Missing or malformed PKCE / request parameters. |
|
|
6043
|
+
*/
|
|
6044
|
+
declare class OAuthError extends ApiError {
|
|
6045
|
+
/** RFC 6749 error code, e.g. `invalid_grant`. */
|
|
6046
|
+
readonly error: string;
|
|
6047
|
+
/** The server's human-readable explanation, when it sent one. */
|
|
6048
|
+
readonly errorDescription: string | null;
|
|
6049
|
+
/**
|
|
6050
|
+
* Create an OAuth protocol error.
|
|
6051
|
+
*
|
|
6052
|
+
* @param error - RFC 6749 error code.
|
|
6053
|
+
* @param errorDescription - Server-provided explanation, or `null`.
|
|
6054
|
+
* @param statusCode - HTTP status that carried it. Authorization responses
|
|
6055
|
+
* arrive as redirect query parameters rather than an HTTP response, so
|
|
6056
|
+
* {@link OAuthResource.readAuthorizationCallback} reports them as `400`.
|
|
6057
|
+
* @param responseData - The raw error object.
|
|
6058
|
+
*
|
|
6059
|
+
* @example
|
|
6060
|
+
* ```ts
|
|
6061
|
+
* throw new OAuthError('invalid_grant', 'Authorization code expired.', 400);
|
|
6062
|
+
* ```
|
|
6063
|
+
*/
|
|
6064
|
+
constructor(error: string, errorDescription?: string | null, statusCode?: number, responseData?: unknown);
|
|
6065
|
+
/**
|
|
6066
|
+
* Upgrade an {@link ApiError} to an {@link OAuthError} when its body is an
|
|
6067
|
+
* RFC 6749 error object; otherwise return the value untouched.
|
|
6068
|
+
*
|
|
6069
|
+
* @param error - Any thrown value.
|
|
6070
|
+
* @returns An `OAuthError` when the body carries a non-empty string
|
|
6071
|
+
* `error`, else the original value.
|
|
6072
|
+
*/
|
|
6073
|
+
static upgrade(error: unknown): unknown;
|
|
6074
|
+
}
|
|
5302
6075
|
/** Thrown when SDK validation fails, including invalid input or workflow state. */
|
|
5303
6076
|
declare class ValidationError extends AssinafyError {
|
|
5304
6077
|
readonly errors: Record<string, unknown>;
|
|
@@ -5331,6 +6104,15 @@ declare class NetworkError extends AssinafyError {
|
|
|
5331
6104
|
constructor(message: string, options?: ErrorOptions);
|
|
5332
6105
|
}
|
|
5333
6106
|
|
|
6107
|
+
/**
|
|
6108
|
+
* Largest page the list endpoints actually return.
|
|
6109
|
+
*
|
|
6110
|
+
* The API silently clamps `per-page` to this value rather than rejecting a
|
|
6111
|
+
* larger one, so a caller asking for 100 receives 50 and no error. Methods that
|
|
6112
|
+
* need "as many rows as one request can give" pin this instead of guessing.
|
|
6113
|
+
*/
|
|
6114
|
+
declare const MAX_LIST_PAGE_SIZE = 50;
|
|
6115
|
+
|
|
5334
6116
|
declare const SDK_USER_AGENT: string;
|
|
5335
6117
|
|
|
5336
|
-
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload };
|
|
6118
|
+
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticateChallenge, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IOAuthAuthorizationCallback, type IOAuthAuthorizationRequest, type IOAuthAuthorizationServerMetadata, type IOAuthProtectedResourceMetadata, type IOAuthTokenResponse, type IOAuthUserInfo, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_LIST_PAGE_SIZE, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, OAuthError, OAuthResource, type OAuthScope, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload, parseWwwAuthenticate };
|