@cboxdk/id-js 0.17.0 → 0.19.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/README.md +315 -3
- package/dist/{chunk-WYTGLCMF.js → chunk-2E25IPBA.js} +4 -3
- package/dist/chunk-2E25IPBA.js.map +1 -0
- package/dist/chunk-QQW63PGU.js +25 -0
- package/dist/chunk-QQW63PGU.js.map +1 -0
- package/dist/{chunk-ASOQN47M.js → chunk-SEUD4TE4.js} +3 -23
- package/dist/chunk-SEUD4TE4.js.map +1 -0
- package/dist/{chunk-G3CD7X4U.js → chunk-YGLMLIKL.js} +197 -9
- package/dist/chunk-YGLMLIKL.js.map +1 -0
- package/dist/{client-CdwdigzG.d.ts → client-BT-jOm0p.d.cts} +154 -2
- package/dist/{client-CEXddiCR.d.cts → client-xlEJalu8.d.ts} +154 -2
- package/dist/errors-D61xljZE.d.cts +87 -0
- package/dist/errors-D61xljZE.d.ts +87 -0
- package/dist/index.cjs +267 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -92
- package/dist/index.d.ts +84 -92
- package/dist/index.js +66 -13
- package/dist/index.js.map +1 -1
- package/dist/management.cjs +5092 -0
- package/dist/management.cjs.map +1 -0
- package/dist/management.d.cts +13454 -0
- package/dist/management.d.ts +13454 -0
- package/dist/management.js +5054 -0
- package/dist/management.js.map +1 -0
- package/dist/nextjs.cjs +136 -5
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +3 -2
- package/dist/nextjs.d.ts +3 -2
- package/dist/nextjs.js +4 -3
- package/dist/nextjs.js.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -2
- package/dist/{types-BYZldock.d.cts → types-CC1bz68x.d.cts} +30 -1
- package/dist/{types-BYZldock.d.ts → types-CC1bz68x.d.ts} +30 -1
- package/package.json +9 -2
- package/dist/chunk-ASOQN47M.js.map +0 -1
- package/dist/chunk-G3CD7X4U.js.map +0 -1
- package/dist/chunk-WYTGLCMF.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as CboxIdError } from './errors-D61xljZE.cjs';
|
|
2
|
+
import { a as CboxIdConfig, A as AuthorizationRequestOptions, b as AuthorizationRequest, C as CboxUser, D as DeviceAuthorization, R as RefreshedTokens, T as TokenTypeHint } from './types-CC1bz68x.cjs';
|
|
2
3
|
|
|
3
4
|
/** A stored vault secret's metadata (never its plaintext). */
|
|
4
5
|
interface VaultSecretRef {
|
|
@@ -17,6 +18,98 @@ interface VaultLease {
|
|
|
17
18
|
secret: string;
|
|
18
19
|
expiresAt: string;
|
|
19
20
|
}
|
|
21
|
+
/** A provider a pipe can connect to. */
|
|
22
|
+
type PipeProvider = 'github' | 'google' | 'microsoft' | 'slack' | 'salesforce' | 'hubspot' | 'linear' | 'notion';
|
|
23
|
+
/**
|
|
24
|
+
* A fresh access token for one person's connected account at a provider — what
|
|
25
|
+
* {@link VaultClient.leasePipeToken} returns. Use it, drop it, lease again next time.
|
|
26
|
+
*/
|
|
27
|
+
interface PipeToken {
|
|
28
|
+
/** The provider's access token. Send it as a bearer token to the provider's API. */
|
|
29
|
+
accessToken: string;
|
|
30
|
+
tokenType: 'Bearer';
|
|
31
|
+
provider: string;
|
|
32
|
+
userId: string;
|
|
33
|
+
connectionId: string;
|
|
34
|
+
/** What the person consented to. */
|
|
35
|
+
scopes: string[];
|
|
36
|
+
/** When the PROVIDER stops accepting it; null for tokens that do not expire. */
|
|
37
|
+
expiresAt: string | null;
|
|
38
|
+
/** When to drop it and lease again. */
|
|
39
|
+
leaseExpiresAt: string;
|
|
40
|
+
/** What some providers need to be called at all — Salesforce's `instance_url`, Slack's `team.id`. */
|
|
41
|
+
metadata: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/** Where a person connects a provider, and where they come back to afterwards. */
|
|
44
|
+
interface PipeConnectOptions {
|
|
45
|
+
/** Your app's client id — the connect page shows its name and allows its origins. */
|
|
46
|
+
clientId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Where to land afterwards, with `?provider=…&status=connected|cancelled|failed`. Must be
|
|
49
|
+
* on one of the app's registered redirect origins, or it is ignored.
|
|
50
|
+
*/
|
|
51
|
+
returnTo?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Add `client_id` and `return_to` to a connect URL — the one a lease error carries, or
|
|
55
|
+
* one built with {@link pipeConnectUrl}.
|
|
56
|
+
*/
|
|
57
|
+
declare function withConnectReturn(connectUrl: string, options?: PipeConnectOptions): string;
|
|
58
|
+
/**
|
|
59
|
+
* The hosted page where the signed-in person connects their account at `provider`:
|
|
60
|
+
* `{issuer}/account/connected-services/{provider}/connect?client_id=…&return_to=…`.
|
|
61
|
+
*/
|
|
62
|
+
declare function pipeConnectUrl(issuer: string, provider: PipeProvider | string, options?: PipeConnectOptions): string;
|
|
63
|
+
/**
|
|
64
|
+
* A pipe token lease was refused. Catch the subclasses for the cases you can act on:
|
|
65
|
+
*
|
|
66
|
+
* - {@link PipeNotConnectedError} (404) and {@link PipeReauthorizationRequiredError}
|
|
67
|
+
* (409) — send the person to `connectUrl`.
|
|
68
|
+
* - {@link PipeTemporarilyUnavailableError} (503) — retry after `retryAfter` seconds.
|
|
69
|
+
* - {@link PipeLeaseDeniedError} (403) — a configuration problem: the app is not granted
|
|
70
|
+
* the pipe, it is disabled, or the person is outside the app's organization.
|
|
71
|
+
*
|
|
72
|
+
* Anything else (401, 422, 429) arrives as this base class with `status` and `error`.
|
|
73
|
+
*/
|
|
74
|
+
declare class PipeLeaseError extends CboxIdError {
|
|
75
|
+
/** The server's error code, e.g. `not_connected`. */
|
|
76
|
+
readonly error: string | null;
|
|
77
|
+
readonly status: number;
|
|
78
|
+
/** Where to send the person to (re)connect — set on 404 and 409. */
|
|
79
|
+
readonly connectUrl: string | null;
|
|
80
|
+
/** Seconds to wait off `Retry-After`, when the server sent one. */
|
|
81
|
+
readonly retryAfter: number | null;
|
|
82
|
+
constructor(message: string,
|
|
83
|
+
/** The server's error code, e.g. `not_connected`. */
|
|
84
|
+
error: string | null, status: number,
|
|
85
|
+
/** Where to send the person to (re)connect — set on 404 and 409. */
|
|
86
|
+
connectUrl?: string | null,
|
|
87
|
+
/** Seconds to wait off `Retry-After`, when the server sent one. */
|
|
88
|
+
retryAfter?: number | null);
|
|
89
|
+
/** `connectUrl` with your `client_id` and `return_to` on it, or null when there is none. */
|
|
90
|
+
connectUrlWith(options: PipeConnectOptions): string | null;
|
|
91
|
+
}
|
|
92
|
+
/** The person has not connected this provider (404 `not_connected`). Send them to `connectUrl`. */
|
|
93
|
+
declare class PipeNotConnectedError extends PipeLeaseError {
|
|
94
|
+
readonly connectUrl: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The provider stopped accepting the connection — revoked, or the refresh token expired
|
|
98
|
+
* (409 `reauthorization_required`). Send the person to `connectUrl` to connect again.
|
|
99
|
+
*/
|
|
100
|
+
declare class PipeReauthorizationRequiredError extends PipeLeaseError {
|
|
101
|
+
readonly connectUrl: string;
|
|
102
|
+
}
|
|
103
|
+
/** The provider could not refresh the token just now (503). Retry after `retryAfter`. */
|
|
104
|
+
declare class PipeTemporarilyUnavailableError extends PipeLeaseError {
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The lease was denied (403 `lease_denied`): the app is not granted the pipe, the pipe is
|
|
108
|
+
* disabled or missing, the person is not in the app's organization, or `userId` names
|
|
109
|
+
* someone other than the token's person. One answer for every reason, by design.
|
|
110
|
+
*/
|
|
111
|
+
declare class PipeLeaseDeniedError extends PipeLeaseError {
|
|
112
|
+
}
|
|
20
113
|
interface StoreSecretInput {
|
|
21
114
|
name: string;
|
|
22
115
|
provider: string;
|
|
@@ -54,8 +147,32 @@ declare class VaultClient {
|
|
|
54
147
|
* is identified by the token's client; a lease with no live grant is refused.
|
|
55
148
|
*/
|
|
56
149
|
lease(secretId: string, purpose: string): Promise<VaultLease>;
|
|
150
|
+
/**
|
|
151
|
+
* Lease a fresh access token for a person's connected account at `provider` (Pipes,
|
|
152
|
+
* scope `vault.lease`). The token is refreshed first when it is about to expire, so
|
|
153
|
+
* you never handle a refresh token.
|
|
154
|
+
*
|
|
155
|
+
* With a client-credentials token, name the person in `userId`. With a token issued to
|
|
156
|
+
* your app FOR a person, leave it out — the lease is theirs.
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* try {
|
|
160
|
+
* const { accessToken } = await vault.leasePipeToken('github', { userId, purpose: 'list-repos' })
|
|
161
|
+
* } catch (e) {
|
|
162
|
+
* if (e instanceof PipeNotConnectedError || e instanceof PipeReauthorizationRequiredError) {
|
|
163
|
+
* return redirect(e.connectUrlWith({ clientId, returnTo }))
|
|
164
|
+
* }
|
|
165
|
+
* throw e
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
leasePipeToken(provider: PipeProvider | string, options: {
|
|
170
|
+
purpose: string;
|
|
171
|
+
userId?: string;
|
|
172
|
+
}): Promise<PipeToken>;
|
|
57
173
|
private json;
|
|
58
174
|
private request;
|
|
175
|
+
private send;
|
|
59
176
|
}
|
|
60
177
|
|
|
61
178
|
/**
|
|
@@ -78,6 +195,34 @@ interface VerifyWebhookOptions {
|
|
|
78
195
|
}
|
|
79
196
|
/** Returns `true` only when the signature is present, fresh and valid. Never throws. */
|
|
80
197
|
declare function verifyWebhook(options: VerifyWebhookOptions): Promise<boolean>;
|
|
198
|
+
/** Headers as a `Headers` object or a plain record (Node's `IncomingHttpHeaders`, a framework's map). */
|
|
199
|
+
type WebhookHeaders = Headers | Record<string, string | readonly string[] | undefined>;
|
|
200
|
+
interface VerifyStandardWebhookOptions {
|
|
201
|
+
/** The RAW request body — the exact bytes received, not a re-encoded copy. */
|
|
202
|
+
payload: string;
|
|
203
|
+
/** The request headers; `webhook-id`, `webhook-timestamp` and `webhook-signature` are read. */
|
|
204
|
+
headers: WebhookHeaders;
|
|
205
|
+
/**
|
|
206
|
+
* The endpoint's secret: a `whsec_…` secret, or the 64-hex secret of an endpoint that
|
|
207
|
+
* moved from the `cbox` scheme (Cbox ID uses it as `whsec_` + base64 of the hex string,
|
|
208
|
+
* which keys the HMAC with the same bytes).
|
|
209
|
+
*/
|
|
210
|
+
secret: string;
|
|
211
|
+
/** Freshness window in seconds. Defaults to 300 (5 minutes). */
|
|
212
|
+
toleranceSeconds?: number;
|
|
213
|
+
/** Injectable clock (unix seconds), for tests. Defaults to `Date.now()`. */
|
|
214
|
+
now?: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Verify a delivery from an endpoint on the `standard_webhooks` signature scheme
|
|
218
|
+
* ([Standard Webhooks](https://www.standardwebhooks.com/)): `webhook-signature` carries one or
|
|
219
|
+
* more space-separated `v1,<base64 HMAC-SHA256 of "{webhook-id}.{webhook-timestamp}.{body}">`,
|
|
220
|
+
* and the timestamp must be within the freshness window. Use {@link verifyWebhook} for the
|
|
221
|
+
* default `cbox` scheme (`X-Cbox-Signature`).
|
|
222
|
+
*
|
|
223
|
+
* Returns `true` only when a signature is present, fresh and valid. Never throws.
|
|
224
|
+
*/
|
|
225
|
+
declare function verifyStandardWebhook(options: VerifyStandardWebhookOptions): Promise<boolean>;
|
|
81
226
|
|
|
82
227
|
/** Query parameters as they arrive on your callback route. */
|
|
83
228
|
interface CallbackParams {
|
|
@@ -231,6 +376,13 @@ declare class CboxIdClient {
|
|
|
231
376
|
* registered. `organization` picks which of the person's organizations the keys act in;
|
|
232
377
|
* omitted, the page uses the one they are in.
|
|
233
378
|
*/
|
|
379
|
+
/**
|
|
380
|
+
* The hosted page where the signed-in person connects their account at `provider`
|
|
381
|
+
* (Pipes) — `/account/connected-services/{provider}/connect`, preselected to this app.
|
|
382
|
+
* They come back to `returnTo` with `?provider=…&status=connected|cancelled|failed`,
|
|
383
|
+
* honoured only on an origin the app registered.
|
|
384
|
+
*/
|
|
385
|
+
pipeConnectUrl(provider: PipeProvider | string, returnTo?: string): string;
|
|
234
386
|
apiKeysUrl(options?: {
|
|
235
387
|
clientId?: string;
|
|
236
388
|
returnTo?: string;
|
|
@@ -310,4 +462,4 @@ declare class CboxIdClient {
|
|
|
310
462
|
private fetchWithTimeout;
|
|
311
463
|
}
|
|
312
464
|
|
|
313
|
-
export { CboxIdClient as C, type StoreSecretInput as S, VaultClient as V, type CallbackParams as a, type StoredAuthState as
|
|
465
|
+
export { CboxIdClient as C, type PipeConnectOptions as P, type StoreSecretInput as S, VaultClient as V, type WebhookHeaders as W, type CallbackParams as a, PipeLeaseDeniedError as b, PipeLeaseError as c, PipeNotConnectedError as d, type PipeProvider as e, PipeReauthorizationRequiredError as f, PipeTemporarilyUnavailableError as g, type PipeToken as h, type StoredAuthState as i, type VaultLease as j, type VaultSecretRef as k, type VerifyStandardWebhookOptions as l, type VerifyWebhookOptions as m, verifyWebhook as n, pipeConnectUrl as p, verifyStandardWebhook as v, withConnectReturn as w };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as CboxIdError } from './errors-D61xljZE.js';
|
|
2
|
+
import { a as CboxIdConfig, A as AuthorizationRequestOptions, b as AuthorizationRequest, C as CboxUser, D as DeviceAuthorization, R as RefreshedTokens, T as TokenTypeHint } from './types-CC1bz68x.js';
|
|
2
3
|
|
|
3
4
|
/** A stored vault secret's metadata (never its plaintext). */
|
|
4
5
|
interface VaultSecretRef {
|
|
@@ -17,6 +18,98 @@ interface VaultLease {
|
|
|
17
18
|
secret: string;
|
|
18
19
|
expiresAt: string;
|
|
19
20
|
}
|
|
21
|
+
/** A provider a pipe can connect to. */
|
|
22
|
+
type PipeProvider = 'github' | 'google' | 'microsoft' | 'slack' | 'salesforce' | 'hubspot' | 'linear' | 'notion';
|
|
23
|
+
/**
|
|
24
|
+
* A fresh access token for one person's connected account at a provider — what
|
|
25
|
+
* {@link VaultClient.leasePipeToken} returns. Use it, drop it, lease again next time.
|
|
26
|
+
*/
|
|
27
|
+
interface PipeToken {
|
|
28
|
+
/** The provider's access token. Send it as a bearer token to the provider's API. */
|
|
29
|
+
accessToken: string;
|
|
30
|
+
tokenType: 'Bearer';
|
|
31
|
+
provider: string;
|
|
32
|
+
userId: string;
|
|
33
|
+
connectionId: string;
|
|
34
|
+
/** What the person consented to. */
|
|
35
|
+
scopes: string[];
|
|
36
|
+
/** When the PROVIDER stops accepting it; null for tokens that do not expire. */
|
|
37
|
+
expiresAt: string | null;
|
|
38
|
+
/** When to drop it and lease again. */
|
|
39
|
+
leaseExpiresAt: string;
|
|
40
|
+
/** What some providers need to be called at all — Salesforce's `instance_url`, Slack's `team.id`. */
|
|
41
|
+
metadata: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/** Where a person connects a provider, and where they come back to afterwards. */
|
|
44
|
+
interface PipeConnectOptions {
|
|
45
|
+
/** Your app's client id — the connect page shows its name and allows its origins. */
|
|
46
|
+
clientId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Where to land afterwards, with `?provider=…&status=connected|cancelled|failed`. Must be
|
|
49
|
+
* on one of the app's registered redirect origins, or it is ignored.
|
|
50
|
+
*/
|
|
51
|
+
returnTo?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Add `client_id` and `return_to` to a connect URL — the one a lease error carries, or
|
|
55
|
+
* one built with {@link pipeConnectUrl}.
|
|
56
|
+
*/
|
|
57
|
+
declare function withConnectReturn(connectUrl: string, options?: PipeConnectOptions): string;
|
|
58
|
+
/**
|
|
59
|
+
* The hosted page where the signed-in person connects their account at `provider`:
|
|
60
|
+
* `{issuer}/account/connected-services/{provider}/connect?client_id=…&return_to=…`.
|
|
61
|
+
*/
|
|
62
|
+
declare function pipeConnectUrl(issuer: string, provider: PipeProvider | string, options?: PipeConnectOptions): string;
|
|
63
|
+
/**
|
|
64
|
+
* A pipe token lease was refused. Catch the subclasses for the cases you can act on:
|
|
65
|
+
*
|
|
66
|
+
* - {@link PipeNotConnectedError} (404) and {@link PipeReauthorizationRequiredError}
|
|
67
|
+
* (409) — send the person to `connectUrl`.
|
|
68
|
+
* - {@link PipeTemporarilyUnavailableError} (503) — retry after `retryAfter` seconds.
|
|
69
|
+
* - {@link PipeLeaseDeniedError} (403) — a configuration problem: the app is not granted
|
|
70
|
+
* the pipe, it is disabled, or the person is outside the app's organization.
|
|
71
|
+
*
|
|
72
|
+
* Anything else (401, 422, 429) arrives as this base class with `status` and `error`.
|
|
73
|
+
*/
|
|
74
|
+
declare class PipeLeaseError extends CboxIdError {
|
|
75
|
+
/** The server's error code, e.g. `not_connected`. */
|
|
76
|
+
readonly error: string | null;
|
|
77
|
+
readonly status: number;
|
|
78
|
+
/** Where to send the person to (re)connect — set on 404 and 409. */
|
|
79
|
+
readonly connectUrl: string | null;
|
|
80
|
+
/** Seconds to wait off `Retry-After`, when the server sent one. */
|
|
81
|
+
readonly retryAfter: number | null;
|
|
82
|
+
constructor(message: string,
|
|
83
|
+
/** The server's error code, e.g. `not_connected`. */
|
|
84
|
+
error: string | null, status: number,
|
|
85
|
+
/** Where to send the person to (re)connect — set on 404 and 409. */
|
|
86
|
+
connectUrl?: string | null,
|
|
87
|
+
/** Seconds to wait off `Retry-After`, when the server sent one. */
|
|
88
|
+
retryAfter?: number | null);
|
|
89
|
+
/** `connectUrl` with your `client_id` and `return_to` on it, or null when there is none. */
|
|
90
|
+
connectUrlWith(options: PipeConnectOptions): string | null;
|
|
91
|
+
}
|
|
92
|
+
/** The person has not connected this provider (404 `not_connected`). Send them to `connectUrl`. */
|
|
93
|
+
declare class PipeNotConnectedError extends PipeLeaseError {
|
|
94
|
+
readonly connectUrl: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The provider stopped accepting the connection — revoked, or the refresh token expired
|
|
98
|
+
* (409 `reauthorization_required`). Send the person to `connectUrl` to connect again.
|
|
99
|
+
*/
|
|
100
|
+
declare class PipeReauthorizationRequiredError extends PipeLeaseError {
|
|
101
|
+
readonly connectUrl: string;
|
|
102
|
+
}
|
|
103
|
+
/** The provider could not refresh the token just now (503). Retry after `retryAfter`. */
|
|
104
|
+
declare class PipeTemporarilyUnavailableError extends PipeLeaseError {
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The lease was denied (403 `lease_denied`): the app is not granted the pipe, the pipe is
|
|
108
|
+
* disabled or missing, the person is not in the app's organization, or `userId` names
|
|
109
|
+
* someone other than the token's person. One answer for every reason, by design.
|
|
110
|
+
*/
|
|
111
|
+
declare class PipeLeaseDeniedError extends PipeLeaseError {
|
|
112
|
+
}
|
|
20
113
|
interface StoreSecretInput {
|
|
21
114
|
name: string;
|
|
22
115
|
provider: string;
|
|
@@ -54,8 +147,32 @@ declare class VaultClient {
|
|
|
54
147
|
* is identified by the token's client; a lease with no live grant is refused.
|
|
55
148
|
*/
|
|
56
149
|
lease(secretId: string, purpose: string): Promise<VaultLease>;
|
|
150
|
+
/**
|
|
151
|
+
* Lease a fresh access token for a person's connected account at `provider` (Pipes,
|
|
152
|
+
* scope `vault.lease`). The token is refreshed first when it is about to expire, so
|
|
153
|
+
* you never handle a refresh token.
|
|
154
|
+
*
|
|
155
|
+
* With a client-credentials token, name the person in `userId`. With a token issued to
|
|
156
|
+
* your app FOR a person, leave it out — the lease is theirs.
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* try {
|
|
160
|
+
* const { accessToken } = await vault.leasePipeToken('github', { userId, purpose: 'list-repos' })
|
|
161
|
+
* } catch (e) {
|
|
162
|
+
* if (e instanceof PipeNotConnectedError || e instanceof PipeReauthorizationRequiredError) {
|
|
163
|
+
* return redirect(e.connectUrlWith({ clientId, returnTo }))
|
|
164
|
+
* }
|
|
165
|
+
* throw e
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
leasePipeToken(provider: PipeProvider | string, options: {
|
|
170
|
+
purpose: string;
|
|
171
|
+
userId?: string;
|
|
172
|
+
}): Promise<PipeToken>;
|
|
57
173
|
private json;
|
|
58
174
|
private request;
|
|
175
|
+
private send;
|
|
59
176
|
}
|
|
60
177
|
|
|
61
178
|
/**
|
|
@@ -78,6 +195,34 @@ interface VerifyWebhookOptions {
|
|
|
78
195
|
}
|
|
79
196
|
/** Returns `true` only when the signature is present, fresh and valid. Never throws. */
|
|
80
197
|
declare function verifyWebhook(options: VerifyWebhookOptions): Promise<boolean>;
|
|
198
|
+
/** Headers as a `Headers` object or a plain record (Node's `IncomingHttpHeaders`, a framework's map). */
|
|
199
|
+
type WebhookHeaders = Headers | Record<string, string | readonly string[] | undefined>;
|
|
200
|
+
interface VerifyStandardWebhookOptions {
|
|
201
|
+
/** The RAW request body — the exact bytes received, not a re-encoded copy. */
|
|
202
|
+
payload: string;
|
|
203
|
+
/** The request headers; `webhook-id`, `webhook-timestamp` and `webhook-signature` are read. */
|
|
204
|
+
headers: WebhookHeaders;
|
|
205
|
+
/**
|
|
206
|
+
* The endpoint's secret: a `whsec_…` secret, or the 64-hex secret of an endpoint that
|
|
207
|
+
* moved from the `cbox` scheme (Cbox ID uses it as `whsec_` + base64 of the hex string,
|
|
208
|
+
* which keys the HMAC with the same bytes).
|
|
209
|
+
*/
|
|
210
|
+
secret: string;
|
|
211
|
+
/** Freshness window in seconds. Defaults to 300 (5 minutes). */
|
|
212
|
+
toleranceSeconds?: number;
|
|
213
|
+
/** Injectable clock (unix seconds), for tests. Defaults to `Date.now()`. */
|
|
214
|
+
now?: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Verify a delivery from an endpoint on the `standard_webhooks` signature scheme
|
|
218
|
+
* ([Standard Webhooks](https://www.standardwebhooks.com/)): `webhook-signature` carries one or
|
|
219
|
+
* more space-separated `v1,<base64 HMAC-SHA256 of "{webhook-id}.{webhook-timestamp}.{body}">`,
|
|
220
|
+
* and the timestamp must be within the freshness window. Use {@link verifyWebhook} for the
|
|
221
|
+
* default `cbox` scheme (`X-Cbox-Signature`).
|
|
222
|
+
*
|
|
223
|
+
* Returns `true` only when a signature is present, fresh and valid. Never throws.
|
|
224
|
+
*/
|
|
225
|
+
declare function verifyStandardWebhook(options: VerifyStandardWebhookOptions): Promise<boolean>;
|
|
81
226
|
|
|
82
227
|
/** Query parameters as they arrive on your callback route. */
|
|
83
228
|
interface CallbackParams {
|
|
@@ -231,6 +376,13 @@ declare class CboxIdClient {
|
|
|
231
376
|
* registered. `organization` picks which of the person's organizations the keys act in;
|
|
232
377
|
* omitted, the page uses the one they are in.
|
|
233
378
|
*/
|
|
379
|
+
/**
|
|
380
|
+
* The hosted page where the signed-in person connects their account at `provider`
|
|
381
|
+
* (Pipes) — `/account/connected-services/{provider}/connect`, preselected to this app.
|
|
382
|
+
* They come back to `returnTo` with `?provider=…&status=connected|cancelled|failed`,
|
|
383
|
+
* honoured only on an origin the app registered.
|
|
384
|
+
*/
|
|
385
|
+
pipeConnectUrl(provider: PipeProvider | string, returnTo?: string): string;
|
|
234
386
|
apiKeysUrl(options?: {
|
|
235
387
|
clientId?: string;
|
|
236
388
|
returnTo?: string;
|
|
@@ -310,4 +462,4 @@ declare class CboxIdClient {
|
|
|
310
462
|
private fetchWithTimeout;
|
|
311
463
|
}
|
|
312
464
|
|
|
313
|
-
export { CboxIdClient as C, type StoreSecretInput as S, VaultClient as V, type CallbackParams as a, type StoredAuthState as
|
|
465
|
+
export { CboxIdClient as C, type PipeConnectOptions as P, type StoreSecretInput as S, VaultClient as V, type WebhookHeaders as W, type CallbackParams as a, PipeLeaseDeniedError as b, PipeLeaseError as c, PipeNotConnectedError as d, type PipeProvider as e, PipeReauthorizationRequiredError as f, PipeTemporarilyUnavailableError as g, type PipeToken as h, type StoredAuthState as i, type VaultLease as j, type VaultSecretRef as k, type VerifyStandardWebhookOptions as l, type VerifyWebhookOptions as m, verifyWebhook as n, pipeConnectUrl as p, verifyStandardWebhook as v, withConnectReturn as w };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for every error this SDK throws, so callers can `catch (e) { if (e
|
|
3
|
+
* instanceof CboxIdError) ... }` in one branch.
|
|
4
|
+
*/
|
|
5
|
+
declare class CboxIdError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** The client is misconfigured (a required option is missing or empty). */
|
|
9
|
+
declare class ConfigurationError extends CboxIdError {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The login `state` did not match what was stored — the callback is forged or
|
|
13
|
+
* stale. Treat as a fresh start, not an error to surface to the user.
|
|
14
|
+
*/
|
|
15
|
+
declare class InvalidStateError extends CboxIdError {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Login could not be completed: the provider returned an error, the callback was
|
|
19
|
+
* missing a code, or a token / id_token failed verification.
|
|
20
|
+
*
|
|
21
|
+
* `error` carries the RFC 6749 §5.2 code the authorization server sent, when it sent one.
|
|
22
|
+
* It used to be discarded at every back-channel boundary, leaving a single message string
|
|
23
|
+
* for outcomes that need opposite responses: `invalid_grant` on a refresh means the
|
|
24
|
+
* session is over and the person has to sign in again, while `temporarily_unavailable`
|
|
25
|
+
* means try the same token in a moment. A caller reduced to matching on prose either
|
|
26
|
+
* retries what can never succeed, or signs out somebody who did not need to be.
|
|
27
|
+
*/
|
|
28
|
+
declare class AuthenticationError extends CboxIdError {
|
|
29
|
+
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
30
|
+
readonly error?: string | undefined;
|
|
31
|
+
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
32
|
+
readonly errorDescription?: string | undefined;
|
|
33
|
+
/** HTTP status, for the cases where the body says nothing useful. */
|
|
34
|
+
readonly status?: number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
37
|
+
*
|
|
38
|
+
* The distinction it carries is the one a caller most needs and could least infer:
|
|
39
|
+
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
40
|
+
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
41
|
+
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
42
|
+
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
43
|
+
*/
|
|
44
|
+
readonly retryAfter?: number | undefined;
|
|
45
|
+
constructor(message: string,
|
|
46
|
+
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
47
|
+
error?: string | undefined,
|
|
48
|
+
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
49
|
+
errorDescription?: string | undefined,
|
|
50
|
+
/** HTTP status, for the cases where the body says nothing useful. */
|
|
51
|
+
status?: number | undefined,
|
|
52
|
+
/**
|
|
53
|
+
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
54
|
+
*
|
|
55
|
+
* The distinction it carries is the one a caller most needs and could least infer:
|
|
56
|
+
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
57
|
+
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
58
|
+
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
59
|
+
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
60
|
+
*/
|
|
61
|
+
retryAfter?: number | undefined);
|
|
62
|
+
/** Whether waiting `retryAfter` seconds and repeating the same request is worth it. */
|
|
63
|
+
get isRateLimited(): boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The Frontend API refused, or could not be reached.
|
|
67
|
+
*
|
|
68
|
+
* Carries a `code` because the four ways this goes wrong need four different responses
|
|
69
|
+
* from the caller, and telling them apart from a message string is the kind of thing
|
|
70
|
+
* that works until somebody rewords the message:
|
|
71
|
+
*
|
|
72
|
+
* - `origin_not_allowed` — the overwhelmingly likely cause of a refusal, and a
|
|
73
|
+
* configuration problem the developer fixes once in the console.
|
|
74
|
+
* - `rate_limited` — back off; `retryAfter` says for how long, when the server said.
|
|
75
|
+
* - `unavailable` — the network, or a 5xx. Transient by assumption; already retried.
|
|
76
|
+
* - `malformed` — a 200 whose body is not the document we expect, which means something
|
|
77
|
+
* between the page and us is rewriting responses (a proxy, an extension, a captive
|
|
78
|
+
* portal). Worth distinguishing, because retrying it never helps.
|
|
79
|
+
*/
|
|
80
|
+
declare class FrontendApiError extends CboxIdError {
|
|
81
|
+
readonly code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed';
|
|
82
|
+
readonly status?: number | undefined;
|
|
83
|
+
readonly retryAfter?: number | undefined;
|
|
84
|
+
constructor(message: string, code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed', status?: number | undefined, retryAfter?: number | undefined);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { AuthenticationError as A, CboxIdError as C, FrontendApiError as F, InvalidStateError as I, ConfigurationError as a };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for every error this SDK throws, so callers can `catch (e) { if (e
|
|
3
|
+
* instanceof CboxIdError) ... }` in one branch.
|
|
4
|
+
*/
|
|
5
|
+
declare class CboxIdError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** The client is misconfigured (a required option is missing or empty). */
|
|
9
|
+
declare class ConfigurationError extends CboxIdError {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The login `state` did not match what was stored — the callback is forged or
|
|
13
|
+
* stale. Treat as a fresh start, not an error to surface to the user.
|
|
14
|
+
*/
|
|
15
|
+
declare class InvalidStateError extends CboxIdError {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Login could not be completed: the provider returned an error, the callback was
|
|
19
|
+
* missing a code, or a token / id_token failed verification.
|
|
20
|
+
*
|
|
21
|
+
* `error` carries the RFC 6749 §5.2 code the authorization server sent, when it sent one.
|
|
22
|
+
* It used to be discarded at every back-channel boundary, leaving a single message string
|
|
23
|
+
* for outcomes that need opposite responses: `invalid_grant` on a refresh means the
|
|
24
|
+
* session is over and the person has to sign in again, while `temporarily_unavailable`
|
|
25
|
+
* means try the same token in a moment. A caller reduced to matching on prose either
|
|
26
|
+
* retries what can never succeed, or signs out somebody who did not need to be.
|
|
27
|
+
*/
|
|
28
|
+
declare class AuthenticationError extends CboxIdError {
|
|
29
|
+
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
30
|
+
readonly error?: string | undefined;
|
|
31
|
+
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
32
|
+
readonly errorDescription?: string | undefined;
|
|
33
|
+
/** HTTP status, for the cases where the body says nothing useful. */
|
|
34
|
+
readonly status?: number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
37
|
+
*
|
|
38
|
+
* The distinction it carries is the one a caller most needs and could least infer:
|
|
39
|
+
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
40
|
+
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
41
|
+
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
42
|
+
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
43
|
+
*/
|
|
44
|
+
readonly retryAfter?: number | undefined;
|
|
45
|
+
constructor(message: string,
|
|
46
|
+
/** RFC 6749 §5.2 error code, when the server sent a parseable one. */
|
|
47
|
+
error?: string | undefined,
|
|
48
|
+
/** The server's `error_description`, verbatim. Not end-user copy. */
|
|
49
|
+
errorDescription?: string | undefined,
|
|
50
|
+
/** HTTP status, for the cases where the body says nothing useful. */
|
|
51
|
+
status?: number | undefined,
|
|
52
|
+
/**
|
|
53
|
+
* Seconds to wait, off the `Retry-After` header — set only on a 429.
|
|
54
|
+
*
|
|
55
|
+
* The distinction it carries is the one a caller most needs and could least infer:
|
|
56
|
+
* a 429 is the ONLY back-channel failure where the same request will succeed
|
|
57
|
+
* unchanged if you simply wait. Every other one needs a different request, or a new
|
|
58
|
+
* sign-in. Without this the SDK reported "token refresh failed" and a caller with a
|
|
59
|
+
* retry loop hammered a limiter that was already telling it exactly how long to stop.
|
|
60
|
+
*/
|
|
61
|
+
retryAfter?: number | undefined);
|
|
62
|
+
/** Whether waiting `retryAfter` seconds and repeating the same request is worth it. */
|
|
63
|
+
get isRateLimited(): boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The Frontend API refused, or could not be reached.
|
|
67
|
+
*
|
|
68
|
+
* Carries a `code` because the four ways this goes wrong need four different responses
|
|
69
|
+
* from the caller, and telling them apart from a message string is the kind of thing
|
|
70
|
+
* that works until somebody rewords the message:
|
|
71
|
+
*
|
|
72
|
+
* - `origin_not_allowed` — the overwhelmingly likely cause of a refusal, and a
|
|
73
|
+
* configuration problem the developer fixes once in the console.
|
|
74
|
+
* - `rate_limited` — back off; `retryAfter` says for how long, when the server said.
|
|
75
|
+
* - `unavailable` — the network, or a 5xx. Transient by assumption; already retried.
|
|
76
|
+
* - `malformed` — a 200 whose body is not the document we expect, which means something
|
|
77
|
+
* between the page and us is rewriting responses (a proxy, an extension, a captive
|
|
78
|
+
* portal). Worth distinguishing, because retrying it never helps.
|
|
79
|
+
*/
|
|
80
|
+
declare class FrontendApiError extends CboxIdError {
|
|
81
|
+
readonly code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed';
|
|
82
|
+
readonly status?: number | undefined;
|
|
83
|
+
readonly retryAfter?: number | undefined;
|
|
84
|
+
constructor(message: string, code: 'origin_not_allowed' | 'rate_limited' | 'unavailable' | 'malformed', status?: number | undefined, retryAfter?: number | undefined);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { AuthenticationError as A, CboxIdError as C, FrontendApiError as F, InvalidStateError as I, ConfigurationError as a };
|