@onebooks/node 0.0.0-stage → 0.1.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/dist/oauth.js ADDED
@@ -0,0 +1,140 @@
1
+ import { oauthErrorFromResponse } from './errors.js';
2
+ const DEFAULT_API_BASE = 'https://api.getonebooks.com';
3
+ const DEFAULT_AUTHORIZE_URL = 'https://app.getonebooks.com/oauth/authorize';
4
+ const TOKEN_EXCHANGE_GRANT = 'urn:ietf:params:oauth:grant-type:token-exchange';
5
+ const SESSION_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:jwt';
6
+ function getWebCrypto() {
7
+ const c = globalThis.crypto;
8
+ if (!c) {
9
+ throw new Error('@onebooks/node: Web Crypto API not available in this runtime (requires Node >= 20).');
10
+ }
11
+ return c;
12
+ }
13
+ function base64url(bytes) {
14
+ return Buffer.from(bytes).toString('base64url');
15
+ }
16
+ /** RFC 7636 PKCE pair: a 43-character verifier and its S256 challenge. */
17
+ export async function createPkcePair() {
18
+ const crypto = getWebCrypto();
19
+ const verifierBytes = new Uint8Array(32);
20
+ crypto.getRandomValues(verifierBytes);
21
+ const codeVerifier = base64url(verifierBytes);
22
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
23
+ const codeChallenge = base64url(new Uint8Array(digest));
24
+ return { codeVerifier, codeChallenge, codeChallengeMethod: 'S256' };
25
+ }
26
+ /** Builds the `https://app.getonebooks.com/oauth/authorize` URL (Step 1 of the code + PKCE flow). */
27
+ export function buildAuthorizeUrl(options) {
28
+ const url = new URL(options.authorizeUrl ?? DEFAULT_AUTHORIZE_URL);
29
+ url.searchParams.set('response_type', 'code');
30
+ url.searchParams.set('client_id', options.clientId);
31
+ url.searchParams.set('redirect_uri', options.redirectUri);
32
+ url.searchParams.set('scope', options.scopes.join(' '));
33
+ url.searchParams.set('state', options.state);
34
+ url.searchParams.set('code_challenge', options.codeChallenge);
35
+ url.searchParams.set('code_challenge_method', options.codeChallengeMethod ?? 'S256');
36
+ return url.toString();
37
+ }
38
+ function tokenEndpoint(apiBase, pathname) {
39
+ return `${(apiBase ?? DEFAULT_API_BASE).replace(/\/+$/, '')}${pathname}`;
40
+ }
41
+ function basicAuthHeader(clientId, clientSecret) {
42
+ return `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`;
43
+ }
44
+ /**
45
+ * A failed response's body: parsed JSON, or the raw text when it isn't JSON
46
+ * (a gateway's HTML error page, say) — never a SyntaxError in place of the
47
+ * `OneBooksApiError`.
48
+ */
49
+ async function readErrorBody(res) {
50
+ const text = await res.text().catch(() => '');
51
+ if (!text)
52
+ return undefined;
53
+ try {
54
+ return JSON.parse(text);
55
+ }
56
+ catch {
57
+ return text;
58
+ }
59
+ }
60
+ async function postForm(url, params, auth, fetchImpl) {
61
+ const body = new URLSearchParams();
62
+ for (const [key, value] of Object.entries(params)) {
63
+ if (value !== undefined)
64
+ body.set(key, value);
65
+ }
66
+ const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
67
+ if (auth.clientSecret) {
68
+ headers.Authorization = basicAuthHeader(auth.clientId, auth.clientSecret);
69
+ }
70
+ else {
71
+ body.set('client_id', auth.clientId);
72
+ }
73
+ const res = await fetchImpl(url, { method: 'POST', headers, body: body.toString() });
74
+ if (!res.ok) {
75
+ // RFC 6749 §5.2 `{ error, error_description? }` (or, from an older server,
76
+ // the code in `message`) — see oauthErrorFromResponse. Never retried here:
77
+ // a 429 carries `retryAfter` for the caller to honour.
78
+ throw oauthErrorFromResponse(res, await readErrorBody(res));
79
+ }
80
+ const text = await res.text();
81
+ return text ? JSON.parse(text) : {};
82
+ }
83
+ /** Step 3: exchange an authorization code for tokens (`POST /oauth/token`). */
84
+ export async function exchangeCode(options) {
85
+ const result = await postForm(tokenEndpoint(options.apiBase, '/oauth/token'), {
86
+ grant_type: 'authorization_code',
87
+ code: options.code,
88
+ redirect_uri: options.redirectUri,
89
+ code_verifier: options.codeVerifier,
90
+ }, options, options.fetch ?? fetch);
91
+ return result;
92
+ }
93
+ /** Refreshes an access token. Rotation is strict: persist BOTH new tokens before discarding the old ones. */
94
+ export async function refresh(options) {
95
+ const result = await postForm(tokenEndpoint(options.apiBase, '/oauth/token'), { grant_type: 'refresh_token', refresh_token: options.refreshToken }, options, options.fetch ?? fetch);
96
+ return result;
97
+ }
98
+ /**
99
+ * `POST /oauth/revoke` (RFC 7009) — resolves for an unknown or already-invalid
100
+ * token too; rejects only when the request itself fails (`invalid_client`, a
101
+ * rate limit, …).
102
+ */
103
+ export async function revoke(options) {
104
+ const fetchImpl = options.fetch ?? fetch;
105
+ const url = tokenEndpoint(options.apiBase, '/oauth/revoke');
106
+ const body = new URLSearchParams();
107
+ body.set('token', options.token);
108
+ if (options.tokenTypeHint)
109
+ body.set('token_type_hint', options.tokenTypeHint);
110
+ const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
111
+ if (options.clientSecret) {
112
+ headers.Authorization = basicAuthHeader(options.clientId, options.clientSecret);
113
+ }
114
+ else {
115
+ body.set('client_id', options.clientId);
116
+ }
117
+ const res = await fetchImpl(url, { method: 'POST', headers, body: body.toString() });
118
+ if (!res.ok) {
119
+ throw oauthErrorFromResponse(res, await readErrorBody(res));
120
+ }
121
+ }
122
+ /**
123
+ * RFC 8693 token exchange (Contract 3): trades an App Bridge session token
124
+ * (short-lived, ~60s) for a normal access/refresh token pair scoped to the
125
+ * installation. Confidential clients only.
126
+ */
127
+ export async function exchangeSessionToken(options) {
128
+ const result = await postForm(tokenEndpoint(options.apiBase, '/oauth/token'), {
129
+ grant_type: TOKEN_EXCHANGE_GRANT,
130
+ subject_token: options.sessionToken,
131
+ subject_token_type: SESSION_TOKEN_TYPE,
132
+ }, { clientId: options.clientId, clientSecret: options.clientSecret }, options.fetch ?? fetch);
133
+ return result;
134
+ }
135
+ /** `POST /oauth/introspect` — a debugging aid, not a production hot path (just call the API and handle 401). */
136
+ export async function introspect(options) {
137
+ const result = await postForm(tokenEndpoint(options.apiBase, '/oauth/introspect'), { token: options.token }, options, options.fetch ?? fetch);
138
+ return result;
139
+ }
140
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type JWTPayload } from 'jose';
2
+ export interface SessionTokenClaims extends JWTPayload {
3
+ iss: string;
4
+ aud: string;
5
+ sub: string;
6
+ /** Business id the embedded app is viewing. */
7
+ bid: string;
8
+ role: string;
9
+ locale: string;
10
+ jti: string;
11
+ iat: number;
12
+ nbf: number;
13
+ exp: number;
14
+ typ: 'onebooks.app_session';
15
+ }
16
+ export interface VerifySessionTokenOptions {
17
+ /** Your app's OAuth client_id — checked against the token's `aud`. */
18
+ clientId: string;
19
+ /** Default `https://api.getonebooks.com`; also determines the default JWKS URL and expected `iss`. */
20
+ apiBase?: string;
21
+ /** Default `<apiBase origin>/.well-known/jwks.json`. */
22
+ jwksUrl?: string;
23
+ /** Default 30. */
24
+ clockToleranceSec?: number;
25
+ }
26
+ /**
27
+ * Verifies an App Bridge session token (Contract 3): ES256 signature against
28
+ * `GET /.well-known/jwks.json`, `aud === clientId`, `iss === apiBase origin`,
29
+ * `exp`/`nbf` within `clockToleranceSec`, and the `typ` claim — a JWT that
30
+ * merely happens to be signed by the same keys but was minted for a
31
+ * different purpose (e.g. a future token type) will carry a different `typ`
32
+ * and must not be accepted here. Throws (jose's `JWTVerifyResult`-shaped
33
+ * errors, e.g. `JWTExpired`, `JWTClaimValidationFailed`) on any failure.
34
+ */
35
+ export declare function verifySessionToken(token: string, options: VerifySessionTokenOptions): Promise<SessionTokenClaims>;
36
+ //# sourceMappingURL=session-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-token.d.ts","sourceRoot":"","sources":["../src/session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAKtE,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,sBAAsB,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,kBAAkB,CAAC,CAkB7B"}
@@ -0,0 +1,41 @@
1
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
2
+ const DEFAULT_API_BASE = 'https://api.getonebooks.com';
3
+ const SESSION_TOKEN_TYP = 'onebooks.app_session';
4
+ // createRemoteJWKSet caches the fetched JWKS (and honors standard HTTP
5
+ // caching) internally, but constructing a new one per call would throw that
6
+ // cache away — keep one instance per JWKS URL for the life of the process.
7
+ const jwksCache = new Map();
8
+ function getRemoteJwks(jwksUrl) {
9
+ let jwks = jwksCache.get(jwksUrl);
10
+ if (!jwks) {
11
+ jwks = createRemoteJWKSet(new URL(jwksUrl));
12
+ jwksCache.set(jwksUrl, jwks);
13
+ }
14
+ return jwks;
15
+ }
16
+ /**
17
+ * Verifies an App Bridge session token (Contract 3): ES256 signature against
18
+ * `GET /.well-known/jwks.json`, `aud === clientId`, `iss === apiBase origin`,
19
+ * `exp`/`nbf` within `clockToleranceSec`, and the `typ` claim — a JWT that
20
+ * merely happens to be signed by the same keys but was minted for a
21
+ * different purpose (e.g. a future token type) will carry a different `typ`
22
+ * and must not be accepted here. Throws (jose's `JWTVerifyResult`-shaped
23
+ * errors, e.g. `JWTExpired`, `JWTClaimValidationFailed`) on any failure.
24
+ */
25
+ export async function verifySessionToken(token, options) {
26
+ const apiBase = options.apiBase ?? DEFAULT_API_BASE;
27
+ const issuer = new URL(apiBase).origin;
28
+ const jwksUrl = options.jwksUrl ?? `${issuer}/.well-known/jwks.json`;
29
+ const jwks = getRemoteJwks(jwksUrl);
30
+ const { payload } = await jwtVerify(token, jwks, {
31
+ algorithms: ['ES256'],
32
+ audience: options.clientId,
33
+ issuer,
34
+ clockTolerance: options.clockToleranceSec ?? 30,
35
+ });
36
+ if (payload.typ !== SESSION_TOKEN_TYP) {
37
+ throw new Error(`OneBooks session token: unexpected "typ" claim (expected "${SESSION_TOKEN_TYP}", got ${JSON.stringify(payload.typ)}).`);
38
+ }
39
+ return payload;
40
+ }
41
+ //# sourceMappingURL=session-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-token.js","sourceRoot":"","sources":["../src/session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAmB,MAAM,MAAM,CAAC;AAEtE,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AACvD,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AA4BjD,uEAAuE;AACvE,4EAA4E;AAC5E,2EAA2E;AAC3E,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE3E,SAAS,aAAa,CAAC,OAAe;IACpC,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,kBAAkB,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5C,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,OAAkC;IAElC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,GAAG,MAAM,wBAAwB,CAAC;IACrE,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/C,UAAU,EAAE,CAAC,OAAO,CAAC;QACrB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM;QACN,cAAc,EAAE,OAAO,CAAC,iBAAiB,IAAI,EAAE;KAChD,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,GAAG,KAAK,iBAAiB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,6DAA6D,iBAAiB,UAAU,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3I,CAAC;IAED,OAAO,OAA6B,CAAC;AACvC,CAAC"}
@@ -0,0 +1,46 @@
1
+ export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
+ export type QueryValue = string | number | boolean | undefined | null | Array<string | number>;
3
+ export type QueryParams = Record<string, QueryValue>;
4
+ export interface OneBooksClientOptions {
5
+ /** A static access token. Provide this OR `getAccessToken`, not both. */
6
+ accessToken?: string;
7
+ /**
8
+ * Called before every request to obtain a (possibly refreshed) access
9
+ * token. Use this over a static `accessToken` whenever your app refreshes
10
+ * tokens elsewhere (a token store, `oauth.refresh`, `exchangeSessionToken`
11
+ * re-run per request, etc).
12
+ */
13
+ getAccessToken?: () => string | Promise<string>;
14
+ /** Default `https://api.getonebooks.com`. */
15
+ apiBase?: string;
16
+ /** Sent as the `OneBooks-Version` request header when set. */
17
+ apiVersion?: string;
18
+ /**
19
+ * Max retry attempts. Default 3. A `429` is retried for every method; a
20
+ * network error or `502`/`503`/`504` only for GET/HEAD or a request with an
21
+ * `idempotencyKey` (see `canReplayAfterAmbiguousFailure`).
22
+ */
23
+ maxRetries?: number;
24
+ /** Override for testing, or to plug in a non-global fetch implementation. */
25
+ fetch?: typeof fetch;
26
+ }
27
+ export interface RequestOptions {
28
+ query?: QueryParams;
29
+ body?: unknown;
30
+ /**
31
+ * Sent as the `Idempotency-Key` header. Setting it also makes a non-GET
32
+ * request safe to retry after a network error or `502`/`503`/`504`. Only
33
+ * some write endpoints de-duplicate on it (e.g. invoice create, payments,
34
+ * manual journals) — elsewhere it is accepted but has no server-side
35
+ * effect, so it does not make a retried write safe there.
36
+ */
37
+ idempotencyKey?: string;
38
+ headers?: Record<string, string>;
39
+ signal?: AbortSignal;
40
+ }
41
+ export interface PaginatedResponse<T> {
42
+ data: T[];
43
+ hasMore: boolean;
44
+ nextCursor: string | null;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;AAC/F,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ /** The webhook delivery body (Contract 5). */
2
+ export interface WebhookEventEnvelope<T = unknown> {
3
+ id: string;
4
+ eventId: string | null;
5
+ type: string;
6
+ apiVersion: string;
7
+ businessId: string;
8
+ created: number;
9
+ data: T;
10
+ }
11
+ export interface VerifyWebhookOptions {
12
+ /** The exact raw request body bytes/string — signing covers the raw body, not a re-serialized JSON.parse/stringify round trip. */
13
+ rawBody: string | Buffer;
14
+ /** The `X-Webhook-Signature` header value, e.g. `t=1765465600,v1=abc…[,v1=def…]`. */
15
+ signatureHeader: string | null | undefined;
16
+ /** Provide `secret` OR `secrets` (during a rotation window, pass both the new and previous secret). */
17
+ secret?: string;
18
+ secrets?: string[];
19
+ /** Default 300. */
20
+ toleranceSec?: number;
21
+ }
22
+ /**
23
+ * Verifies a webhook delivery's `X-Webhook-Signature` header
24
+ * (`t=<unix>,v1=<hex>[,v1=<hex>]`, HMAC-SHA256 of `"<t>.<rawBody>"`) and
25
+ * returns the parsed event on success. Accepts the delivery if **any**
26
+ * provided secret matches **any** `v1` value (supports secret rotation:
27
+ * pass both the current and previous secret while a rotation is in
28
+ * progress). Rejects timestamps outside `toleranceSec` of now, to blunt
29
+ * replay of a captured delivery. Throws on any verification failure — never
30
+ * returns a "valid: false" value, so a caller can't accidentally ignore the
31
+ * result.
32
+ */
33
+ export declare function verifyWebhook<T = unknown>(options: VerifyWebhookOptions): WebhookEventEnvelope<T>;
34
+ /**
35
+ * Parses a webhook delivery body WITHOUT verifying its signature. Only use
36
+ * this on a body you already ran through `verifyWebhook()` (e.g. to type a
37
+ * value you verified earlier) — this function performs no cryptographic
38
+ * check on its own.
39
+ */
40
+ export declare function parseWebhookEvent<T = unknown>(rawBody: string | Buffer): WebhookEventEnvelope<T>;
41
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB,CAAC,CAAC,GAAG,OAAO;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,CAAC,CAAC;CACT;AAED,MAAM,WAAW,oBAAoB;IACnC,kIAAkI;IAClI,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,qFAAqF;IACrF,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mBAAmB;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAwCD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,oBAAoB,GAAG,oBAAoB,CAAC,CAAC,CAAC,CA+BjG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAGhG"}
@@ -0,0 +1,86 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ function parseSignatureHeader(header) {
3
+ let timestamp;
4
+ const signatures = [];
5
+ for (const part of header.split(',')) {
6
+ const eq = part.indexOf('=');
7
+ if (eq === -1)
8
+ continue;
9
+ const key = part.slice(0, eq).trim();
10
+ const value = part.slice(eq + 1).trim();
11
+ if (key === 't')
12
+ timestamp = value;
13
+ else if (key === 'v1' && value)
14
+ signatures.push(value);
15
+ }
16
+ if (!timestamp || signatures.length === 0) {
17
+ throw new Error('OneBooks webhook: malformed X-Webhook-Signature header.');
18
+ }
19
+ return { timestamp, signatures };
20
+ }
21
+ function hmacSha256(secret, payload) {
22
+ return createHmac('sha256', secret).update(payload, 'utf8').digest();
23
+ }
24
+ /** Constant-time compare of a hex-encoded signature against the expected digest. */
25
+ function signatureMatches(hexSignature, expected) {
26
+ let provided;
27
+ try {
28
+ provided = Buffer.from(hexSignature, 'hex');
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ if (provided.length !== expected.length)
34
+ return false;
35
+ return timingSafeEqual(provided, expected);
36
+ }
37
+ /**
38
+ * Verifies a webhook delivery's `X-Webhook-Signature` header
39
+ * (`t=<unix>,v1=<hex>[,v1=<hex>]`, HMAC-SHA256 of `"<t>.<rawBody>"`) and
40
+ * returns the parsed event on success. Accepts the delivery if **any**
41
+ * provided secret matches **any** `v1` value (supports secret rotation:
42
+ * pass both the current and previous secret while a rotation is in
43
+ * progress). Rejects timestamps outside `toleranceSec` of now, to blunt
44
+ * replay of a captured delivery. Throws on any verification failure — never
45
+ * returns a "valid: false" value, so a caller can't accidentally ignore the
46
+ * result.
47
+ */
48
+ export function verifyWebhook(options) {
49
+ const secrets = options.secrets ?? (options.secret ? [options.secret] : []);
50
+ if (secrets.length === 0) {
51
+ throw new Error('OneBooks webhook: provide at least one secret ("secret" or "secrets").');
52
+ }
53
+ if (!options.signatureHeader) {
54
+ throw new Error('OneBooks webhook: missing X-Webhook-Signature header.');
55
+ }
56
+ const { timestamp, signatures } = parseSignatureHeader(options.signatureHeader);
57
+ const toleranceSec = options.toleranceSec ?? 300;
58
+ const timestampNum = Number(timestamp);
59
+ if (!Number.isFinite(timestampNum)) {
60
+ throw new Error('OneBooks webhook: invalid timestamp in X-Webhook-Signature header.');
61
+ }
62
+ if (Math.abs(Date.now() / 1000 - timestampNum) > toleranceSec) {
63
+ throw new Error('OneBooks webhook: timestamp outside tolerance (stale or replayed delivery?).');
64
+ }
65
+ const rawBody = typeof options.rawBody === 'string' ? options.rawBody : options.rawBody.toString('utf8');
66
+ const signedPayload = `${timestamp}.${rawBody}`;
67
+ const verified = secrets.some((secret) => {
68
+ const expected = hmacSha256(secret, signedPayload);
69
+ return signatures.some((sig) => signatureMatches(sig, expected));
70
+ });
71
+ if (!verified) {
72
+ throw new Error('OneBooks webhook: signature verification failed.');
73
+ }
74
+ return JSON.parse(rawBody);
75
+ }
76
+ /**
77
+ * Parses a webhook delivery body WITHOUT verifying its signature. Only use
78
+ * this on a body you already ran through `verifyWebhook()` (e.g. to type a
79
+ * value you verified earlier) — this function performs no cryptographic
80
+ * check on its own.
81
+ */
82
+ export function parseWebhookEvent(rawBody) {
83
+ const text = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');
84
+ return JSON.parse(text);
85
+ }
86
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@onebooks/node",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Server-side SDK for the OneBooks API: authenticated requests with retries, OAuth helpers (PKCE, code exchange, refresh, session-token exchange), ES256 session-token verification and webhook signature verification.",
5
+ "keywords": [
6
+ "onebooks",
7
+ "accounting",
8
+ "api-client",
9
+ "oauth",
10
+ "webhooks",
11
+ "session-token",
12
+ "sdk"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://developer.getonebooks.com/docs/sdk-node/",
16
+ "type": "module",
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "types": "./dist/index.d.ts",
29
+ "main": "./dist/index.js",
30
+ "sideEffects": false,
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "provenance": true
39
+ },
40
+ "scripts": {
41
+ "build": "tsc -p tsconfig.json",
42
+ "test": "tsx --test test/**/*.test.ts",
43
+ "typecheck": "tsc -p tsconfig.json --noEmit",
44
+ "clean": "rm -rf dist"
45
+ },
46
+ "dependencies": {
47
+ "jose": "^6.2.12"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.10.7",
51
+ "tsx": "^4.20.0",
52
+ "typescript": "~5.9.3"
53
+ }
54
+ }