@atribu/node 0.1.0 → 0.1.1
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 +4 -0
- package/dist/admin/index.cjs +1 -1
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js.map +1 -1
- package/dist/oauth/index.cjs +1 -1
- package/dist/oauth/index.cjs.map +1 -1
- package/dist/oauth/index.js +1 -1
- package/dist/oauth/index.js.map +1 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +3 -3
- package/dist/test/index.d.ts +3 -3
- package/dist/test/index.js.map +1 -1
- package/package.json +5 -5
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/webhooks/verify.ts","../../src/next/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","/**\n * Verify Atribu webhook signatures using Web Crypto.\n *\n * Why Web Crypto: works in Node 18+, Bun, Deno, Vercel Edge, Cloudflare\n * Workers. No `node:crypto` import keeps this subpath edge-safe.\n *\n * Header format (Stripe-style): `t=<unix_seconds>,v1=<hex_hmac_sha256>`\n * Signed string: `<t>.<rawBody>`\n *\n * Rotation: pass `previousSecret` during the grace window after rotating.\n * Atribu always signs with the current secret; the previous slot exists\n * only so subscribers can dual-verify during their own deploy.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport type { AtribuWebhookEvent } from \"./types\";\n\nexport interface VerifyWebhookOptions {\n /** Raw, unparsed request body (string or Uint8Array). */\n rawBody: string | Uint8Array;\n /** Value of the `X-Atribu-Signature` header. */\n signature: string | null | undefined;\n /** Current HMAC secret. */\n secret: string;\n /** Previous secret during rotation grace. */\n previousSecret?: string | null;\n /** Max age of the signed timestamp in seconds. Default 300 (5 minutes). */\n tolerance?: number;\n /** Inject for tests; defaults to Date.now(). */\n now?: () => number;\n}\n\ninterface ParsedHeader {\n t: number;\n v1: string;\n}\n\nexport async function verifyWebhook(opts: VerifyWebhookOptions): Promise<AtribuWebhookEvent> {\n if (!opts.signature) {\n throw new AtribuWebhookError(\"missing_signature\", \"Missing X-Atribu-Signature header\");\n }\n\n const parsed = parseHeader(opts.signature);\n const bodyString = typeof opts.rawBody === \"string\" ? opts.rawBody : decodeUtf8(opts.rawBody);\n\n const nowMs = (opts.now ?? Date.now)();\n const tolerance = opts.tolerance ?? 300;\n const ageSeconds = Math.abs(nowMs / 1000 - parsed.t);\n if (ageSeconds > tolerance) {\n throw new AtribuWebhookError(\n \"expired_timestamp\",\n `Signed timestamp is ${Math.round(ageSeconds)}s old (tolerance ${tolerance}s)`,\n );\n }\n\n const signingInput = `${parsed.t}.${bodyString}`;\n const matchesCurrent = await safeCompareHmac(opts.secret, signingInput, parsed.v1);\n if (!matchesCurrent) {\n const previous = opts.previousSecret;\n if (!previous || !(await safeCompareHmac(previous, signingInput, parsed.v1))) {\n throw new AtribuWebhookError(\"invalid_signature\", \"Signature does not match\");\n }\n }\n\n return JSON.parse(bodyString) as AtribuWebhookEvent;\n}\n\nfunction parseHeader(header: string): ParsedHeader {\n let t: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n const value = part.slice(eq + 1).trim();\n if (key === \"t\") t = Number(value);\n else if (key === \"v1\") v1 = value;\n }\n if (t === null || !Number.isFinite(t) || !v1) {\n throw new AtribuWebhookError(\"malformed_header\", `Malformed signature header: ${header}`);\n }\n return { t, v1 };\n}\n\nasync function safeCompareHmac(secret: string, input: string, expectedHex: string): Promise<boolean> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(input));\n const computed = bufferToHex(sig);\n return constantTimeEqualHex(computed, expectedHex);\n}\n\nfunction bufferToHex(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n hex += (b < 16 ? \"0\" : \"\") + b.toString(16);\n }\n return hex;\n}\n\nfunction constantTimeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeUtf8(buf: Uint8Array): string {\n return new TextDecoder(\"utf-8\").decode(buf);\n}\n","/**\n * Next.js App Router webhook handler.\n *\n * Usage:\n *\n * // app/api/atribu-webhook/route.ts\n * import { withAtribuWebhook } from \"@atribu/sdk/next\";\n *\n * export const POST = withAtribuWebhook({\n * secret: process.env.ATRIBU_WEBHOOK_SECRET!,\n * previousSecret: process.env.ATRIBU_WEBHOOK_PREVIOUS_SECRET,\n * onEvent: async (event) => {\n * if (event.type === \"message.received\" && event.provider === \"whatsapp\") {\n * // event.data.wa_message_id typed\n * }\n * },\n * });\n *\n * Reads the raw body via `req.text()`, calls `verifyWebhook`, dispatches\n * the typed event to `onEvent`, and returns a 200/401 response.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport { verifyWebhook } from \"../webhooks/verify\";\nimport type { AtribuWebhookEvent } from \"../webhooks/types\";\n\nexport interface WithAtribuWebhookOptions {\n secret: string;\n previousSecret?: string | null;\n tolerance?: number;\n onEvent: (event: AtribuWebhookEvent, ctx: WebhookContext) => Promise<void> | void;\n /** Called when a request arrives but signature validation fails. */\n onInvalidSignature?: (error: AtribuWebhookError, request: Request) => void;\n /** Called for any uncaught error inside `onEvent`. Default: log to console. */\n onError?: (error: unknown, event: AtribuWebhookEvent | null) => void;\n}\n\nexport interface WebhookContext {\n request: Request;\n deliveryId: string | null;\n signatureTimestamp: number | null;\n}\n\nexport function withAtribuWebhook(\n opts: WithAtribuWebhookOptions,\n): (request: Request) => Promise<Response> {\n if (!opts.secret) throw new Error(\"withAtribuWebhook: secret is required\");\n\n return async function handler(request: Request): Promise<Response> {\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-atribu-signature\");\n const deliveryId = request.headers.get(\"x-atribu-delivery-id\");\n\n let event: AtribuWebhookEvent;\n try {\n event = await verifyWebhook({\n rawBody,\n signature,\n secret: opts.secret,\n previousSecret: opts.previousSecret ?? null,\n tolerance: opts.tolerance,\n });\n } catch (err) {\n if (err instanceof AtribuWebhookError) {\n opts.onInvalidSignature?.(err, request);\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status: 401,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n throw err;\n }\n\n const ctx: WebhookContext = {\n request,\n deliveryId,\n signatureTimestamp: parseTimestamp(signature),\n };\n\n try {\n await opts.onEvent(event, ctx);\n } catch (err) {\n if (opts.onError) opts.onError(err, event);\n else console.error(\"[atribu webhook] onEvent threw:\", err);\n // Return 500 so Atribu retries — surface real failures via DLQ.\n return new Response(JSON.stringify({ error: \"handler_error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n return new Response(null, { status: 200 });\n };\n}\n\nfunction parseTimestamp(header: string | null): number | null {\n if (!header) return null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n if (key !== \"t\") continue;\n const n = Number(part.slice(eq + 1).trim());\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/webhooks/verify.ts","../../src/next/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","/**\n * Verify Atribu webhook signatures using Web Crypto.\n *\n * Why Web Crypto: works in Node 18+, Bun, Deno, Vercel Edge, Cloudflare\n * Workers. No `node:crypto` import keeps this subpath edge-safe.\n *\n * Header format (Stripe-style): `t=<unix_seconds>,v1=<hex_hmac_sha256>`\n * Signed string: `<t>.<rawBody>`\n *\n * Rotation: pass `previousSecret` during the grace window after rotating.\n * Atribu always signs with the current secret; the previous slot exists\n * only so subscribers can dual-verify during their own deploy.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport type { AtribuWebhookEvent } from \"./types\";\n\nexport interface VerifyWebhookOptions {\n /** Raw, unparsed request body (string or Uint8Array). */\n rawBody: string | Uint8Array;\n /** Value of the `X-Atribu-Signature` header. */\n signature: string | null | undefined;\n /** Current HMAC secret. */\n secret: string;\n /** Previous secret during rotation grace. */\n previousSecret?: string | null;\n /** Max age of the signed timestamp in seconds. Default 300 (5 minutes). */\n tolerance?: number;\n /** Inject for tests; defaults to Date.now(). */\n now?: () => number;\n}\n\ninterface ParsedHeader {\n t: number;\n v1: string;\n}\n\nexport async function verifyWebhook(opts: VerifyWebhookOptions): Promise<AtribuWebhookEvent> {\n if (!opts.signature) {\n throw new AtribuWebhookError(\"missing_signature\", \"Missing X-Atribu-Signature header\");\n }\n\n const parsed = parseHeader(opts.signature);\n const bodyString = typeof opts.rawBody === \"string\" ? opts.rawBody : decodeUtf8(opts.rawBody);\n\n const nowMs = (opts.now ?? Date.now)();\n const tolerance = opts.tolerance ?? 300;\n const ageSeconds = Math.abs(nowMs / 1000 - parsed.t);\n if (ageSeconds > tolerance) {\n throw new AtribuWebhookError(\n \"expired_timestamp\",\n `Signed timestamp is ${Math.round(ageSeconds)}s old (tolerance ${tolerance}s)`,\n );\n }\n\n const signingInput = `${parsed.t}.${bodyString}`;\n const matchesCurrent = await safeCompareHmac(opts.secret, signingInput, parsed.v1);\n if (!matchesCurrent) {\n const previous = opts.previousSecret;\n if (!previous || !(await safeCompareHmac(previous, signingInput, parsed.v1))) {\n throw new AtribuWebhookError(\"invalid_signature\", \"Signature does not match\");\n }\n }\n\n return JSON.parse(bodyString) as AtribuWebhookEvent;\n}\n\nfunction parseHeader(header: string): ParsedHeader {\n let t: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n const value = part.slice(eq + 1).trim();\n if (key === \"t\") t = Number(value);\n else if (key === \"v1\") v1 = value;\n }\n if (t === null || !Number.isFinite(t) || !v1) {\n throw new AtribuWebhookError(\"malformed_header\", `Malformed signature header: ${header}`);\n }\n return { t, v1 };\n}\n\nasync function safeCompareHmac(secret: string, input: string, expectedHex: string): Promise<boolean> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(input));\n const computed = bufferToHex(sig);\n return constantTimeEqualHex(computed, expectedHex);\n}\n\nfunction bufferToHex(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n hex += (b < 16 ? \"0\" : \"\") + b.toString(16);\n }\n return hex;\n}\n\nfunction constantTimeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeUtf8(buf: Uint8Array): string {\n return new TextDecoder(\"utf-8\").decode(buf);\n}\n","/**\n * Next.js App Router webhook handler.\n *\n * Usage:\n *\n * // app/api/atribu-webhook/route.ts\n * import { withAtribuWebhook } from \"@atribu/node/next\";\n *\n * export const POST = withAtribuWebhook({\n * secret: process.env.ATRIBU_WEBHOOK_SECRET!,\n * previousSecret: process.env.ATRIBU_WEBHOOK_PREVIOUS_SECRET,\n * onEvent: async (event) => {\n * if (event.type === \"message.received\" && event.provider === \"whatsapp\") {\n * // event.data.wa_message_id typed\n * }\n * },\n * });\n *\n * Reads the raw body via `req.text()`, calls `verifyWebhook`, dispatches\n * the typed event to `onEvent`, and returns a 200/401 response.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport { verifyWebhook } from \"../webhooks/verify\";\nimport type { AtribuWebhookEvent } from \"../webhooks/types\";\n\nexport interface WithAtribuWebhookOptions {\n secret: string;\n previousSecret?: string | null;\n tolerance?: number;\n onEvent: (event: AtribuWebhookEvent, ctx: WebhookContext) => Promise<void> | void;\n /** Called when a request arrives but signature validation fails. */\n onInvalidSignature?: (error: AtribuWebhookError, request: Request) => void;\n /** Called for any uncaught error inside `onEvent`. Default: log to console. */\n onError?: (error: unknown, event: AtribuWebhookEvent | null) => void;\n}\n\nexport interface WebhookContext {\n request: Request;\n deliveryId: string | null;\n signatureTimestamp: number | null;\n}\n\nexport function withAtribuWebhook(\n opts: WithAtribuWebhookOptions,\n): (request: Request) => Promise<Response> {\n if (!opts.secret) throw new Error(\"withAtribuWebhook: secret is required\");\n\n return async function handler(request: Request): Promise<Response> {\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-atribu-signature\");\n const deliveryId = request.headers.get(\"x-atribu-delivery-id\");\n\n let event: AtribuWebhookEvent;\n try {\n event = await verifyWebhook({\n rawBody,\n signature,\n secret: opts.secret,\n previousSecret: opts.previousSecret ?? null,\n tolerance: opts.tolerance,\n });\n } catch (err) {\n if (err instanceof AtribuWebhookError) {\n opts.onInvalidSignature?.(err, request);\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status: 401,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n throw err;\n }\n\n const ctx: WebhookContext = {\n request,\n deliveryId,\n signatureTimestamp: parseTimestamp(signature),\n };\n\n try {\n await opts.onEvent(event, ctx);\n } catch (err) {\n if (opts.onError) opts.onError(err, event);\n else console.error(\"[atribu webhook] onEvent threw:\", err);\n // Return 500 so Atribu retries — surface real failures via DLQ.\n return new Response(JSON.stringify({ error: \"handler_error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n return new Response(null, { status: 200 });\n };\n}\n\nfunction parseTimestamp(header: string | null): number | null {\n if (!header) return null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n if (key !== \"t\") continue;\n const n = Number(part.slice(eq + 1).trim());\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n"]}
|
package/dist/next/index.d.cts
CHANGED
|
@@ -7,7 +7,7 @@ import { A as AtribuWebhookEvent } from '../types-Dc6tIN_V.cjs';
|
|
|
7
7
|
* Usage:
|
|
8
8
|
*
|
|
9
9
|
* // app/api/atribu-webhook/route.ts
|
|
10
|
-
* import { withAtribuWebhook } from "@atribu/
|
|
10
|
+
* import { withAtribuWebhook } from "@atribu/node/next";
|
|
11
11
|
*
|
|
12
12
|
* export const POST = withAtribuWebhook({
|
|
13
13
|
* secret: process.env.ATRIBU_WEBHOOK_SECRET!,
|
package/dist/next/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { A as AtribuWebhookEvent } from '../types-Dc6tIN_V.js';
|
|
|
7
7
|
* Usage:
|
|
8
8
|
*
|
|
9
9
|
* // app/api/atribu-webhook/route.ts
|
|
10
|
-
* import { withAtribuWebhook } from "@atribu/
|
|
10
|
+
* import { withAtribuWebhook } from "@atribu/node/next";
|
|
11
11
|
*
|
|
12
12
|
* export const POST = withAtribuWebhook({
|
|
13
13
|
* secret: process.env.ATRIBU_WEBHOOK_SECRET!,
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/webhooks/verify.ts","../../src/next/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","/**\n * Verify Atribu webhook signatures using Web Crypto.\n *\n * Why Web Crypto: works in Node 18+, Bun, Deno, Vercel Edge, Cloudflare\n * Workers. No `node:crypto` import keeps this subpath edge-safe.\n *\n * Header format (Stripe-style): `t=<unix_seconds>,v1=<hex_hmac_sha256>`\n * Signed string: `<t>.<rawBody>`\n *\n * Rotation: pass `previousSecret` during the grace window after rotating.\n * Atribu always signs with the current secret; the previous slot exists\n * only so subscribers can dual-verify during their own deploy.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport type { AtribuWebhookEvent } from \"./types\";\n\nexport interface VerifyWebhookOptions {\n /** Raw, unparsed request body (string or Uint8Array). */\n rawBody: string | Uint8Array;\n /** Value of the `X-Atribu-Signature` header. */\n signature: string | null | undefined;\n /** Current HMAC secret. */\n secret: string;\n /** Previous secret during rotation grace. */\n previousSecret?: string | null;\n /** Max age of the signed timestamp in seconds. Default 300 (5 minutes). */\n tolerance?: number;\n /** Inject for tests; defaults to Date.now(). */\n now?: () => number;\n}\n\ninterface ParsedHeader {\n t: number;\n v1: string;\n}\n\nexport async function verifyWebhook(opts: VerifyWebhookOptions): Promise<AtribuWebhookEvent> {\n if (!opts.signature) {\n throw new AtribuWebhookError(\"missing_signature\", \"Missing X-Atribu-Signature header\");\n }\n\n const parsed = parseHeader(opts.signature);\n const bodyString = typeof opts.rawBody === \"string\" ? opts.rawBody : decodeUtf8(opts.rawBody);\n\n const nowMs = (opts.now ?? Date.now)();\n const tolerance = opts.tolerance ?? 300;\n const ageSeconds = Math.abs(nowMs / 1000 - parsed.t);\n if (ageSeconds > tolerance) {\n throw new AtribuWebhookError(\n \"expired_timestamp\",\n `Signed timestamp is ${Math.round(ageSeconds)}s old (tolerance ${tolerance}s)`,\n );\n }\n\n const signingInput = `${parsed.t}.${bodyString}`;\n const matchesCurrent = await safeCompareHmac(opts.secret, signingInput, parsed.v1);\n if (!matchesCurrent) {\n const previous = opts.previousSecret;\n if (!previous || !(await safeCompareHmac(previous, signingInput, parsed.v1))) {\n throw new AtribuWebhookError(\"invalid_signature\", \"Signature does not match\");\n }\n }\n\n return JSON.parse(bodyString) as AtribuWebhookEvent;\n}\n\nfunction parseHeader(header: string): ParsedHeader {\n let t: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n const value = part.slice(eq + 1).trim();\n if (key === \"t\") t = Number(value);\n else if (key === \"v1\") v1 = value;\n }\n if (t === null || !Number.isFinite(t) || !v1) {\n throw new AtribuWebhookError(\"malformed_header\", `Malformed signature header: ${header}`);\n }\n return { t, v1 };\n}\n\nasync function safeCompareHmac(secret: string, input: string, expectedHex: string): Promise<boolean> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(input));\n const computed = bufferToHex(sig);\n return constantTimeEqualHex(computed, expectedHex);\n}\n\nfunction bufferToHex(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n hex += (b < 16 ? \"0\" : \"\") + b.toString(16);\n }\n return hex;\n}\n\nfunction constantTimeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeUtf8(buf: Uint8Array): string {\n return new TextDecoder(\"utf-8\").decode(buf);\n}\n","/**\n * Next.js App Router webhook handler.\n *\n * Usage:\n *\n * // app/api/atribu-webhook/route.ts\n * import { withAtribuWebhook } from \"@atribu/sdk/next\";\n *\n * export const POST = withAtribuWebhook({\n * secret: process.env.ATRIBU_WEBHOOK_SECRET!,\n * previousSecret: process.env.ATRIBU_WEBHOOK_PREVIOUS_SECRET,\n * onEvent: async (event) => {\n * if (event.type === \"message.received\" && event.provider === \"whatsapp\") {\n * // event.data.wa_message_id typed\n * }\n * },\n * });\n *\n * Reads the raw body via `req.text()`, calls `verifyWebhook`, dispatches\n * the typed event to `onEvent`, and returns a 200/401 response.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport { verifyWebhook } from \"../webhooks/verify\";\nimport type { AtribuWebhookEvent } from \"../webhooks/types\";\n\nexport interface WithAtribuWebhookOptions {\n secret: string;\n previousSecret?: string | null;\n tolerance?: number;\n onEvent: (event: AtribuWebhookEvent, ctx: WebhookContext) => Promise<void> | void;\n /** Called when a request arrives but signature validation fails. */\n onInvalidSignature?: (error: AtribuWebhookError, request: Request) => void;\n /** Called for any uncaught error inside `onEvent`. Default: log to console. */\n onError?: (error: unknown, event: AtribuWebhookEvent | null) => void;\n}\n\nexport interface WebhookContext {\n request: Request;\n deliveryId: string | null;\n signatureTimestamp: number | null;\n}\n\nexport function withAtribuWebhook(\n opts: WithAtribuWebhookOptions,\n): (request: Request) => Promise<Response> {\n if (!opts.secret) throw new Error(\"withAtribuWebhook: secret is required\");\n\n return async function handler(request: Request): Promise<Response> {\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-atribu-signature\");\n const deliveryId = request.headers.get(\"x-atribu-delivery-id\");\n\n let event: AtribuWebhookEvent;\n try {\n event = await verifyWebhook({\n rawBody,\n signature,\n secret: opts.secret,\n previousSecret: opts.previousSecret ?? null,\n tolerance: opts.tolerance,\n });\n } catch (err) {\n if (err instanceof AtribuWebhookError) {\n opts.onInvalidSignature?.(err, request);\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status: 401,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n throw err;\n }\n\n const ctx: WebhookContext = {\n request,\n deliveryId,\n signatureTimestamp: parseTimestamp(signature),\n };\n\n try {\n await opts.onEvent(event, ctx);\n } catch (err) {\n if (opts.onError) opts.onError(err, event);\n else console.error(\"[atribu webhook] onEvent threw:\", err);\n // Return 500 so Atribu retries — surface real failures via DLQ.\n return new Response(JSON.stringify({ error: \"handler_error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n return new Response(null, { status: 200 });\n };\n}\n\nfunction parseTimestamp(header: string | null): number | null {\n if (!header) return null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n if (key !== \"t\") continue;\n const n = Number(part.slice(eq + 1).trim());\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/webhooks/verify.ts","../../src/next/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","/**\n * Verify Atribu webhook signatures using Web Crypto.\n *\n * Why Web Crypto: works in Node 18+, Bun, Deno, Vercel Edge, Cloudflare\n * Workers. No `node:crypto` import keeps this subpath edge-safe.\n *\n * Header format (Stripe-style): `t=<unix_seconds>,v1=<hex_hmac_sha256>`\n * Signed string: `<t>.<rawBody>`\n *\n * Rotation: pass `previousSecret` during the grace window after rotating.\n * Atribu always signs with the current secret; the previous slot exists\n * only so subscribers can dual-verify during their own deploy.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport type { AtribuWebhookEvent } from \"./types\";\n\nexport interface VerifyWebhookOptions {\n /** Raw, unparsed request body (string or Uint8Array). */\n rawBody: string | Uint8Array;\n /** Value of the `X-Atribu-Signature` header. */\n signature: string | null | undefined;\n /** Current HMAC secret. */\n secret: string;\n /** Previous secret during rotation grace. */\n previousSecret?: string | null;\n /** Max age of the signed timestamp in seconds. Default 300 (5 minutes). */\n tolerance?: number;\n /** Inject for tests; defaults to Date.now(). */\n now?: () => number;\n}\n\ninterface ParsedHeader {\n t: number;\n v1: string;\n}\n\nexport async function verifyWebhook(opts: VerifyWebhookOptions): Promise<AtribuWebhookEvent> {\n if (!opts.signature) {\n throw new AtribuWebhookError(\"missing_signature\", \"Missing X-Atribu-Signature header\");\n }\n\n const parsed = parseHeader(opts.signature);\n const bodyString = typeof opts.rawBody === \"string\" ? opts.rawBody : decodeUtf8(opts.rawBody);\n\n const nowMs = (opts.now ?? Date.now)();\n const tolerance = opts.tolerance ?? 300;\n const ageSeconds = Math.abs(nowMs / 1000 - parsed.t);\n if (ageSeconds > tolerance) {\n throw new AtribuWebhookError(\n \"expired_timestamp\",\n `Signed timestamp is ${Math.round(ageSeconds)}s old (tolerance ${tolerance}s)`,\n );\n }\n\n const signingInput = `${parsed.t}.${bodyString}`;\n const matchesCurrent = await safeCompareHmac(opts.secret, signingInput, parsed.v1);\n if (!matchesCurrent) {\n const previous = opts.previousSecret;\n if (!previous || !(await safeCompareHmac(previous, signingInput, parsed.v1))) {\n throw new AtribuWebhookError(\"invalid_signature\", \"Signature does not match\");\n }\n }\n\n return JSON.parse(bodyString) as AtribuWebhookEvent;\n}\n\nfunction parseHeader(header: string): ParsedHeader {\n let t: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n const value = part.slice(eq + 1).trim();\n if (key === \"t\") t = Number(value);\n else if (key === \"v1\") v1 = value;\n }\n if (t === null || !Number.isFinite(t) || !v1) {\n throw new AtribuWebhookError(\"malformed_header\", `Malformed signature header: ${header}`);\n }\n return { t, v1 };\n}\n\nasync function safeCompareHmac(secret: string, input: string, expectedHex: string): Promise<boolean> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(input));\n const computed = bufferToHex(sig);\n return constantTimeEqualHex(computed, expectedHex);\n}\n\nfunction bufferToHex(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n hex += (b < 16 ? \"0\" : \"\") + b.toString(16);\n }\n return hex;\n}\n\nfunction constantTimeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeUtf8(buf: Uint8Array): string {\n return new TextDecoder(\"utf-8\").decode(buf);\n}\n","/**\n * Next.js App Router webhook handler.\n *\n * Usage:\n *\n * // app/api/atribu-webhook/route.ts\n * import { withAtribuWebhook } from \"@atribu/node/next\";\n *\n * export const POST = withAtribuWebhook({\n * secret: process.env.ATRIBU_WEBHOOK_SECRET!,\n * previousSecret: process.env.ATRIBU_WEBHOOK_PREVIOUS_SECRET,\n * onEvent: async (event) => {\n * if (event.type === \"message.received\" && event.provider === \"whatsapp\") {\n * // event.data.wa_message_id typed\n * }\n * },\n * });\n *\n * Reads the raw body via `req.text()`, calls `verifyWebhook`, dispatches\n * the typed event to `onEvent`, and returns a 200/401 response.\n */\n\nimport { AtribuWebhookError } from \"../errors\";\nimport { verifyWebhook } from \"../webhooks/verify\";\nimport type { AtribuWebhookEvent } from \"../webhooks/types\";\n\nexport interface WithAtribuWebhookOptions {\n secret: string;\n previousSecret?: string | null;\n tolerance?: number;\n onEvent: (event: AtribuWebhookEvent, ctx: WebhookContext) => Promise<void> | void;\n /** Called when a request arrives but signature validation fails. */\n onInvalidSignature?: (error: AtribuWebhookError, request: Request) => void;\n /** Called for any uncaught error inside `onEvent`. Default: log to console. */\n onError?: (error: unknown, event: AtribuWebhookEvent | null) => void;\n}\n\nexport interface WebhookContext {\n request: Request;\n deliveryId: string | null;\n signatureTimestamp: number | null;\n}\n\nexport function withAtribuWebhook(\n opts: WithAtribuWebhookOptions,\n): (request: Request) => Promise<Response> {\n if (!opts.secret) throw new Error(\"withAtribuWebhook: secret is required\");\n\n return async function handler(request: Request): Promise<Response> {\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-atribu-signature\");\n const deliveryId = request.headers.get(\"x-atribu-delivery-id\");\n\n let event: AtribuWebhookEvent;\n try {\n event = await verifyWebhook({\n rawBody,\n signature,\n secret: opts.secret,\n previousSecret: opts.previousSecret ?? null,\n tolerance: opts.tolerance,\n });\n } catch (err) {\n if (err instanceof AtribuWebhookError) {\n opts.onInvalidSignature?.(err, request);\n return new Response(JSON.stringify({ error: err.code, message: err.message }), {\n status: 401,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n throw err;\n }\n\n const ctx: WebhookContext = {\n request,\n deliveryId,\n signatureTimestamp: parseTimestamp(signature),\n };\n\n try {\n await opts.onEvent(event, ctx);\n } catch (err) {\n if (opts.onError) opts.onError(err, event);\n else console.error(\"[atribu webhook] onEvent threw:\", err);\n // Return 500 so Atribu retries — surface real failures via DLQ.\n return new Response(JSON.stringify({ error: \"handler_error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n return new Response(null, { status: 200 });\n };\n}\n\nfunction parseTimestamp(header: string | null): number | null {\n if (!header) return null;\n for (const part of header.split(\",\")) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n const key = part.slice(0, eq).trim();\n if (key !== \"t\") continue;\n const n = Number(part.slice(eq + 1).trim());\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n"]}
|
package/dist/oauth/index.cjs
CHANGED
package/dist/oauth/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/oauth/authorize-url.ts","../../src/version.ts","../../src/runtime.ts","../../src/oauth/exchange.ts","../../src/oauth/revoke.ts","../../src/oauth/id-token-hint.ts","../../src/oauth/pkce.ts"],"names":["DEFAULT_BASE_URL","body","base64"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","import { AtribuConfigError } from \"../errors\";\n\nexport type OauthProvider = \"whatsapp\" | \"instagram\";\nexport type OauthScope = \"whatsapp\" | \"instagram\";\n\nexport interface BuildAuthorizeUrlInput {\n baseUrl?: string;\n clientId: string;\n redirectUri: string;\n provider: OauthProvider;\n scope: OauthScope | OauthScope[];\n state: string;\n /** Pre-signed `id_token_hint` JWT — use `signIdTokenHint` to mint. */\n idTokenHint: string;\n /** PKCE challenge (base64url of SHA-256). */\n codeChallenge?: string;\n codeChallengeMethod?: \"S256\" | \"plain\";\n /** Forwards extra query params unchanged. */\n extras?: Record<string, string>;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\n/**\n * Build the `/oauth/authorize` URL the user should be redirected to.\n *\n * Pure function — returns a string. No network calls, no crypto. The only\n * runtime check is required-arg validation.\n */\nexport function buildAuthorizeUrl(input: BuildAuthorizeUrlInput): string {\n for (const key of [\"clientId\", \"redirectUri\", \"provider\", \"state\", \"idTokenHint\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`buildAuthorizeUrl: ${key} is required`);\n }\n if (input.codeChallenge && !input.codeChallengeMethod) {\n throw new AtribuConfigError(\"codeChallengeMethod is required when codeChallenge is set\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const scope = Array.isArray(input.scope) ? input.scope.join(\" \") : input.scope;\n\n const url = new URL(\"/oauth/authorize\", baseUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"provider\", input.provider);\n url.searchParams.set(\"scope\", scope);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"id_token_hint\", input.idTokenHint);\n if (input.codeChallenge && input.codeChallengeMethod) {\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", input.codeChallengeMethod);\n }\n if (input.extras) {\n for (const [k, v] of Object.entries(input.extras)) url.searchParams.set(k, v);\n }\n return url.toString();\n}\n","export const SDK_VERSION = \"0.1.0\";\n","export type Runtime = \"node\" | \"bun\" | \"deno\" | \"edge\" | \"browser\" | \"unknown\";\n\ndeclare const Deno: unknown;\ndeclare const Bun: unknown;\ndeclare const EdgeRuntime: unknown;\n\nexport function detectRuntime(): Runtime {\n if (typeof Deno !== \"undefined\") return \"deno\";\n if (typeof Bun !== \"undefined\") return \"bun\";\n if (typeof EdgeRuntime !== \"undefined\") return \"edge\";\n if (\n typeof process !== \"undefined\" &&\n typeof (process as { versions?: { node?: string } }).versions?.node === \"string\"\n ) {\n return \"node\";\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") return \"browser\";\n return \"unknown\";\n}\n\nexport function runtimeTag(): string {\n const rt = detectRuntime();\n if (rt === \"node\") {\n const v = (process as { versions?: { node?: string } }).versions?.node;\n return v ? `node/${v}` : \"node\";\n }\n return rt;\n}\n","/**\n * Exchange an authorization code for an Atribu access token.\n *\n * Calls `POST /oauth/token` with form-urlencoded body (RFC 6749 default).\n * Tries `client_secret_basic` (Authorization header) by default; consumers\n * can opt into `client_secret_post` via `authMethod: \"body\"`.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface ExchangeCodeInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n /** \"basic\" (default) sends Authorization: Basic; \"body\" puts creds in form body. */\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nexport interface ExchangeCodeResult {\n accessToken: string;\n tokenType: \"bearer\";\n scope: string[];\n connectionId: string | null;\n profileId: string;\n workspaceId: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function exchangeCode(input: ExchangeCodeInput): Promise<ExchangeCodeResult> {\n for (const key of [\"clientId\", \"clientSecret\", \"code\", \"redirectUri\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`exchangeCode: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/token`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n };\n if (input.codeVerifier) form.code_verifier = input.codeVerifier;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n const text = await res.text();\n const parsed = text ? safeJson(text) : null;\n\n if (!res.ok) {\n const body = (parsed ?? {}) as { error?: unknown; error_description?: unknown };\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n }\n const body = parsed as {\n access_token: string;\n token_type: string;\n scope?: string;\n connection_id?: string;\n profile_id: string;\n workspace_id: string;\n };\n return {\n accessToken: body.access_token,\n tokenType: \"bearer\",\n scope: typeof body.scope === \"string\" ? body.scope.split(/\\s+/).filter(Boolean) : [],\n connectionId: body.connection_id ?? null,\n profileId: body.profile_id,\n workspaceId: body.workspace_id,\n };\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","/**\n * Revoke an Atribu access token via RFC 7009.\n *\n * Always returns void on success — the server returns 200 regardless of\n * whether the token existed (RFC §2.2; no enumeration). Authentication\n * errors (bad client credentials) still throw AtribuOauthError.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface RevokeTokenInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n token: string;\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function revokeToken(input: RevokeTokenInput): Promise<void> {\n for (const key of [\"clientId\", \"clientSecret\", \"token\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`revokeToken: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/revoke`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n token: input.token,\n token_type_hint: \"access_token\",\n };\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n if (res.ok) return;\n const text = await res.text();\n let body: { error?: unknown; error_description?: unknown } = {};\n try {\n body = text ? (JSON.parse(text) as typeof body) : {};\n } catch {\n body = {};\n }\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","/**\n * Sign an `id_token_hint` JWT (HS256) for an Atribu OAuth flow.\n *\n * Uses `jose` (optional peer dep). If `jose` isn't installed, throws a\n * clear runtime error. Vitrina-style consumers almost always already have\n * `jose` for their own auth.\n *\n * Why HS256 not RS256: the shared secret is the OAuth app's\n * `jwt_signing_secret` (rotatable, paired). RS256 would mean Atribu\n * publishes a JWKS endpoint and we manage keypairs — out of scope today.\n */\n\nimport { AtribuConfigError } from \"../errors\";\n\nexport interface IdTokenHintPayload {\n /** End-user's stable identifier in your system. */\n subject: string;\n /** End-user's email. Used by Atribu to silent-provision / find existing profile. */\n email: string;\n /** Token audience. Use \"atribu\" unless directed otherwise. */\n audience?: string;\n /** Issuer (your app's client_id, by convention). */\n issuer?: string;\n /** Expiration relative to now. Default \"5m\". Examples: \"5m\", \"10m\", numeric seconds. */\n expiresIn?: string | number;\n /** Atribu's recognized claims plus any custom ones. */\n extraClaims?: Record<string, unknown>;\n}\n\nexport interface SignIdTokenHintInput extends IdTokenHintPayload {\n /** The OAuth app's `jwt_signing_secret`. */\n jwtSigningSecret: string;\n}\n\ninterface JoseModule {\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader(header: { alg: string; typ?: string }): JoseSignJwt;\n setIssuedAt(time?: number): JoseSignJwt;\n setExpirationTime(time: string | number): JoseSignJwt;\n setIssuer(issuer: string): JoseSignJwt;\n setSubject(subject: string): JoseSignJwt;\n setAudience(audience: string | string[]): JoseSignJwt;\n sign(secret: Uint8Array | CryptoKey): Promise<string>;\n };\n}\ntype JoseSignJwt = InstanceType<JoseModule[\"SignJWT\"]>;\n\nlet cachedJose: JoseModule | null = null;\nlet joseLoadAttempted = false;\n\nasync function loadJose(): Promise<JoseModule> {\n if (cachedJose) return cachedJose;\n if (joseLoadAttempted && !cachedJose) {\n throw new AtribuConfigError(\n \"signIdTokenHint requires the `jose` peer dependency. Run `npm install jose`.\",\n );\n }\n joseLoadAttempted = true;\n try {\n const mod = (await import(\"jose\")) as unknown as JoseModule;\n cachedJose = mod;\n return mod;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"import failed\";\n throw new AtribuConfigError(\n `signIdTokenHint requires the \\`jose\\` peer dependency. Install with \\`npm install jose\\`. (${message})`,\n );\n }\n}\n\nexport async function signIdTokenHint(input: SignIdTokenHintInput): Promise<string> {\n if (!input.jwtSigningSecret) {\n throw new AtribuConfigError(\"signIdTokenHint: jwtSigningSecret is required\");\n }\n if (!input.subject) throw new AtribuConfigError(\"signIdTokenHint: subject is required\");\n if (!input.email) throw new AtribuConfigError(\"signIdTokenHint: email is required\");\n\n const jose = await loadJose();\n const claims: Record<string, unknown> = { email: input.email, ...(input.extraClaims ?? {}) };\n\n const jwt = new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setIssuedAt()\n .setSubject(input.subject)\n .setAudience(input.audience ?? \"atribu\")\n .setExpirationTime(input.expiresIn ?? \"5m\");\n if (input.issuer) jwt.setIssuer(input.issuer);\n\n return jwt.sign(new TextEncoder().encode(input.jwtSigningSecret));\n}\n","/**\n * PKCE helpers (RFC 7636).\n *\n * Generates an unguessable `code_verifier` and the base64url(SHA-256) digest\n * to use as `code_challenge` with `code_challenge_method=S256`.\n */\n\nconst VERIFIER_CHARS =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n\nexport function generateCodeVerifier(length = 64): string {\n if (length < 43 || length > 128) {\n throw new Error(\"PKCE verifier length must be 43–128\");\n }\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += VERIFIER_CHARS[bytes[i]! % VERIFIER_CHARS.length];\n }\n return out;\n}\n\nexport async function computeCodeChallenge(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);\n const b64 =\n typeof btoa === \"function\"\n ? btoa(bin)\n : Buffer.from(bytes).toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/oauth/authorize-url.ts","../../src/version.ts","../../src/runtime.ts","../../src/oauth/exchange.ts","../../src/oauth/revoke.ts","../../src/oauth/id-token-hint.ts","../../src/oauth/pkce.ts"],"names":["DEFAULT_BASE_URL","body","base64"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","import { AtribuConfigError } from \"../errors\";\n\nexport type OauthProvider = \"whatsapp\" | \"instagram\";\nexport type OauthScope = \"whatsapp\" | \"instagram\";\n\nexport interface BuildAuthorizeUrlInput {\n baseUrl?: string;\n clientId: string;\n redirectUri: string;\n provider: OauthProvider;\n scope: OauthScope | OauthScope[];\n state: string;\n /** Pre-signed `id_token_hint` JWT — use `signIdTokenHint` to mint. */\n idTokenHint: string;\n /** PKCE challenge (base64url of SHA-256). */\n codeChallenge?: string;\n codeChallengeMethod?: \"S256\" | \"plain\";\n /** Forwards extra query params unchanged. */\n extras?: Record<string, string>;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\n/**\n * Build the `/oauth/authorize` URL the user should be redirected to.\n *\n * Pure function — returns a string. No network calls, no crypto. The only\n * runtime check is required-arg validation.\n */\nexport function buildAuthorizeUrl(input: BuildAuthorizeUrlInput): string {\n for (const key of [\"clientId\", \"redirectUri\", \"provider\", \"state\", \"idTokenHint\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`buildAuthorizeUrl: ${key} is required`);\n }\n if (input.codeChallenge && !input.codeChallengeMethod) {\n throw new AtribuConfigError(\"codeChallengeMethod is required when codeChallenge is set\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const scope = Array.isArray(input.scope) ? input.scope.join(\" \") : input.scope;\n\n const url = new URL(\"/oauth/authorize\", baseUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"provider\", input.provider);\n url.searchParams.set(\"scope\", scope);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"id_token_hint\", input.idTokenHint);\n if (input.codeChallenge && input.codeChallengeMethod) {\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", input.codeChallengeMethod);\n }\n if (input.extras) {\n for (const [k, v] of Object.entries(input.extras)) url.searchParams.set(k, v);\n }\n return url.toString();\n}\n","export const SDK_VERSION = \"0.1.1\";\n","export type Runtime = \"node\" | \"bun\" | \"deno\" | \"edge\" | \"browser\" | \"unknown\";\n\ndeclare const Deno: unknown;\ndeclare const Bun: unknown;\ndeclare const EdgeRuntime: unknown;\n\nexport function detectRuntime(): Runtime {\n if (typeof Deno !== \"undefined\") return \"deno\";\n if (typeof Bun !== \"undefined\") return \"bun\";\n if (typeof EdgeRuntime !== \"undefined\") return \"edge\";\n if (\n typeof process !== \"undefined\" &&\n typeof (process as { versions?: { node?: string } }).versions?.node === \"string\"\n ) {\n return \"node\";\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") return \"browser\";\n return \"unknown\";\n}\n\nexport function runtimeTag(): string {\n const rt = detectRuntime();\n if (rt === \"node\") {\n const v = (process as { versions?: { node?: string } }).versions?.node;\n return v ? `node/${v}` : \"node\";\n }\n return rt;\n}\n","/**\n * Exchange an authorization code for an Atribu access token.\n *\n * Calls `POST /oauth/token` with form-urlencoded body (RFC 6749 default).\n * Tries `client_secret_basic` (Authorization header) by default; consumers\n * can opt into `client_secret_post` via `authMethod: \"body\"`.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface ExchangeCodeInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n /** \"basic\" (default) sends Authorization: Basic; \"body\" puts creds in form body. */\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nexport interface ExchangeCodeResult {\n accessToken: string;\n tokenType: \"bearer\";\n scope: string[];\n connectionId: string | null;\n profileId: string;\n workspaceId: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function exchangeCode(input: ExchangeCodeInput): Promise<ExchangeCodeResult> {\n for (const key of [\"clientId\", \"clientSecret\", \"code\", \"redirectUri\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`exchangeCode: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/token`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n };\n if (input.codeVerifier) form.code_verifier = input.codeVerifier;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n const text = await res.text();\n const parsed = text ? safeJson(text) : null;\n\n if (!res.ok) {\n const body = (parsed ?? {}) as { error?: unknown; error_description?: unknown };\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n }\n const body = parsed as {\n access_token: string;\n token_type: string;\n scope?: string;\n connection_id?: string;\n profile_id: string;\n workspace_id: string;\n };\n return {\n accessToken: body.access_token,\n tokenType: \"bearer\",\n scope: typeof body.scope === \"string\" ? body.scope.split(/\\s+/).filter(Boolean) : [],\n connectionId: body.connection_id ?? null,\n profileId: body.profile_id,\n workspaceId: body.workspace_id,\n };\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","/**\n * Revoke an Atribu access token via RFC 7009.\n *\n * Always returns void on success — the server returns 200 regardless of\n * whether the token existed (RFC §2.2; no enumeration). Authentication\n * errors (bad client credentials) still throw AtribuOauthError.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface RevokeTokenInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n token: string;\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function revokeToken(input: RevokeTokenInput): Promise<void> {\n for (const key of [\"clientId\", \"clientSecret\", \"token\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`revokeToken: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/revoke`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n token: input.token,\n token_type_hint: \"access_token\",\n };\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n if (res.ok) return;\n const text = await res.text();\n let body: { error?: unknown; error_description?: unknown } = {};\n try {\n body = text ? (JSON.parse(text) as typeof body) : {};\n } catch {\n body = {};\n }\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","/**\n * Sign an `id_token_hint` JWT (HS256) for an Atribu OAuth flow.\n *\n * Uses `jose` (optional peer dep). If `jose` isn't installed, throws a\n * clear runtime error. Vitrina-style consumers almost always already have\n * `jose` for their own auth.\n *\n * Why HS256 not RS256: the shared secret is the OAuth app's\n * `jwt_signing_secret` (rotatable, paired). RS256 would mean Atribu\n * publishes a JWKS endpoint and we manage keypairs — out of scope today.\n */\n\nimport { AtribuConfigError } from \"../errors\";\n\nexport interface IdTokenHintPayload {\n /** End-user's stable identifier in your system. */\n subject: string;\n /** End-user's email. Used by Atribu to silent-provision / find existing profile. */\n email: string;\n /** Token audience. Use \"atribu\" unless directed otherwise. */\n audience?: string;\n /** Issuer (your app's client_id, by convention). */\n issuer?: string;\n /** Expiration relative to now. Default \"5m\". Examples: \"5m\", \"10m\", numeric seconds. */\n expiresIn?: string | number;\n /** Atribu's recognized claims plus any custom ones. */\n extraClaims?: Record<string, unknown>;\n}\n\nexport interface SignIdTokenHintInput extends IdTokenHintPayload {\n /** The OAuth app's `jwt_signing_secret`. */\n jwtSigningSecret: string;\n}\n\ninterface JoseModule {\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader(header: { alg: string; typ?: string }): JoseSignJwt;\n setIssuedAt(time?: number): JoseSignJwt;\n setExpirationTime(time: string | number): JoseSignJwt;\n setIssuer(issuer: string): JoseSignJwt;\n setSubject(subject: string): JoseSignJwt;\n setAudience(audience: string | string[]): JoseSignJwt;\n sign(secret: Uint8Array | CryptoKey): Promise<string>;\n };\n}\ntype JoseSignJwt = InstanceType<JoseModule[\"SignJWT\"]>;\n\nlet cachedJose: JoseModule | null = null;\nlet joseLoadAttempted = false;\n\nasync function loadJose(): Promise<JoseModule> {\n if (cachedJose) return cachedJose;\n if (joseLoadAttempted && !cachedJose) {\n throw new AtribuConfigError(\n \"signIdTokenHint requires the `jose` peer dependency. Run `npm install jose`.\",\n );\n }\n joseLoadAttempted = true;\n try {\n const mod = (await import(\"jose\")) as unknown as JoseModule;\n cachedJose = mod;\n return mod;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"import failed\";\n throw new AtribuConfigError(\n `signIdTokenHint requires the \\`jose\\` peer dependency. Install with \\`npm install jose\\`. (${message})`,\n );\n }\n}\n\nexport async function signIdTokenHint(input: SignIdTokenHintInput): Promise<string> {\n if (!input.jwtSigningSecret) {\n throw new AtribuConfigError(\"signIdTokenHint: jwtSigningSecret is required\");\n }\n if (!input.subject) throw new AtribuConfigError(\"signIdTokenHint: subject is required\");\n if (!input.email) throw new AtribuConfigError(\"signIdTokenHint: email is required\");\n\n const jose = await loadJose();\n const claims: Record<string, unknown> = { email: input.email, ...(input.extraClaims ?? {}) };\n\n const jwt = new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setIssuedAt()\n .setSubject(input.subject)\n .setAudience(input.audience ?? \"atribu\")\n .setExpirationTime(input.expiresIn ?? \"5m\");\n if (input.issuer) jwt.setIssuer(input.issuer);\n\n return jwt.sign(new TextEncoder().encode(input.jwtSigningSecret));\n}\n","/**\n * PKCE helpers (RFC 7636).\n *\n * Generates an unguessable `code_verifier` and the base64url(SHA-256) digest\n * to use as `code_challenge` with `code_challenge_method=S256`.\n */\n\nconst VERIFIER_CHARS =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n\nexport function generateCodeVerifier(length = 64): string {\n if (length < 43 || length > 128) {\n throw new Error(\"PKCE verifier length must be 43–128\");\n }\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += VERIFIER_CHARS[bytes[i]! % VERIFIER_CHARS.length];\n }\n return out;\n}\n\nexport async function computeCodeChallenge(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);\n const b64 =\n typeof btoa === \"function\"\n ? btoa(bin)\n : Buffer.from(bytes).toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"]}
|
package/dist/oauth/index.js
CHANGED
package/dist/oauth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/oauth/authorize-url.ts","../../src/version.ts","../../src/runtime.ts","../../src/oauth/exchange.ts","../../src/oauth/revoke.ts","../../src/oauth/id-token-hint.ts","../../src/oauth/pkce.ts"],"names":["DEFAULT_BASE_URL","body","base64"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","import { AtribuConfigError } from \"../errors\";\n\nexport type OauthProvider = \"whatsapp\" | \"instagram\";\nexport type OauthScope = \"whatsapp\" | \"instagram\";\n\nexport interface BuildAuthorizeUrlInput {\n baseUrl?: string;\n clientId: string;\n redirectUri: string;\n provider: OauthProvider;\n scope: OauthScope | OauthScope[];\n state: string;\n /** Pre-signed `id_token_hint` JWT — use `signIdTokenHint` to mint. */\n idTokenHint: string;\n /** PKCE challenge (base64url of SHA-256). */\n codeChallenge?: string;\n codeChallengeMethod?: \"S256\" | \"plain\";\n /** Forwards extra query params unchanged. */\n extras?: Record<string, string>;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\n/**\n * Build the `/oauth/authorize` URL the user should be redirected to.\n *\n * Pure function — returns a string. No network calls, no crypto. The only\n * runtime check is required-arg validation.\n */\nexport function buildAuthorizeUrl(input: BuildAuthorizeUrlInput): string {\n for (const key of [\"clientId\", \"redirectUri\", \"provider\", \"state\", \"idTokenHint\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`buildAuthorizeUrl: ${key} is required`);\n }\n if (input.codeChallenge && !input.codeChallengeMethod) {\n throw new AtribuConfigError(\"codeChallengeMethod is required when codeChallenge is set\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const scope = Array.isArray(input.scope) ? input.scope.join(\" \") : input.scope;\n\n const url = new URL(\"/oauth/authorize\", baseUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"provider\", input.provider);\n url.searchParams.set(\"scope\", scope);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"id_token_hint\", input.idTokenHint);\n if (input.codeChallenge && input.codeChallengeMethod) {\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", input.codeChallengeMethod);\n }\n if (input.extras) {\n for (const [k, v] of Object.entries(input.extras)) url.searchParams.set(k, v);\n }\n return url.toString();\n}\n","export const SDK_VERSION = \"0.1.0\";\n","export type Runtime = \"node\" | \"bun\" | \"deno\" | \"edge\" | \"browser\" | \"unknown\";\n\ndeclare const Deno: unknown;\ndeclare const Bun: unknown;\ndeclare const EdgeRuntime: unknown;\n\nexport function detectRuntime(): Runtime {\n if (typeof Deno !== \"undefined\") return \"deno\";\n if (typeof Bun !== \"undefined\") return \"bun\";\n if (typeof EdgeRuntime !== \"undefined\") return \"edge\";\n if (\n typeof process !== \"undefined\" &&\n typeof (process as { versions?: { node?: string } }).versions?.node === \"string\"\n ) {\n return \"node\";\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") return \"browser\";\n return \"unknown\";\n}\n\nexport function runtimeTag(): string {\n const rt = detectRuntime();\n if (rt === \"node\") {\n const v = (process as { versions?: { node?: string } }).versions?.node;\n return v ? `node/${v}` : \"node\";\n }\n return rt;\n}\n","/**\n * Exchange an authorization code for an Atribu access token.\n *\n * Calls `POST /oauth/token` with form-urlencoded body (RFC 6749 default).\n * Tries `client_secret_basic` (Authorization header) by default; consumers\n * can opt into `client_secret_post` via `authMethod: \"body\"`.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface ExchangeCodeInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n /** \"basic\" (default) sends Authorization: Basic; \"body\" puts creds in form body. */\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nexport interface ExchangeCodeResult {\n accessToken: string;\n tokenType: \"bearer\";\n scope: string[];\n connectionId: string | null;\n profileId: string;\n workspaceId: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function exchangeCode(input: ExchangeCodeInput): Promise<ExchangeCodeResult> {\n for (const key of [\"clientId\", \"clientSecret\", \"code\", \"redirectUri\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`exchangeCode: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/token`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n };\n if (input.codeVerifier) form.code_verifier = input.codeVerifier;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n const text = await res.text();\n const parsed = text ? safeJson(text) : null;\n\n if (!res.ok) {\n const body = (parsed ?? {}) as { error?: unknown; error_description?: unknown };\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n }\n const body = parsed as {\n access_token: string;\n token_type: string;\n scope?: string;\n connection_id?: string;\n profile_id: string;\n workspace_id: string;\n };\n return {\n accessToken: body.access_token,\n tokenType: \"bearer\",\n scope: typeof body.scope === \"string\" ? body.scope.split(/\\s+/).filter(Boolean) : [],\n connectionId: body.connection_id ?? null,\n profileId: body.profile_id,\n workspaceId: body.workspace_id,\n };\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","/**\n * Revoke an Atribu access token via RFC 7009.\n *\n * Always returns void on success — the server returns 200 regardless of\n * whether the token existed (RFC §2.2; no enumeration). Authentication\n * errors (bad client credentials) still throw AtribuOauthError.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface RevokeTokenInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n token: string;\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function revokeToken(input: RevokeTokenInput): Promise<void> {\n for (const key of [\"clientId\", \"clientSecret\", \"token\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`revokeToken: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/revoke`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n token: input.token,\n token_type_hint: \"access_token\",\n };\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n if (res.ok) return;\n const text = await res.text();\n let body: { error?: unknown; error_description?: unknown } = {};\n try {\n body = text ? (JSON.parse(text) as typeof body) : {};\n } catch {\n body = {};\n }\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","/**\n * Sign an `id_token_hint` JWT (HS256) for an Atribu OAuth flow.\n *\n * Uses `jose` (optional peer dep). If `jose` isn't installed, throws a\n * clear runtime error. Vitrina-style consumers almost always already have\n * `jose` for their own auth.\n *\n * Why HS256 not RS256: the shared secret is the OAuth app's\n * `jwt_signing_secret` (rotatable, paired). RS256 would mean Atribu\n * publishes a JWKS endpoint and we manage keypairs — out of scope today.\n */\n\nimport { AtribuConfigError } from \"../errors\";\n\nexport interface IdTokenHintPayload {\n /** End-user's stable identifier in your system. */\n subject: string;\n /** End-user's email. Used by Atribu to silent-provision / find existing profile. */\n email: string;\n /** Token audience. Use \"atribu\" unless directed otherwise. */\n audience?: string;\n /** Issuer (your app's client_id, by convention). */\n issuer?: string;\n /** Expiration relative to now. Default \"5m\". Examples: \"5m\", \"10m\", numeric seconds. */\n expiresIn?: string | number;\n /** Atribu's recognized claims plus any custom ones. */\n extraClaims?: Record<string, unknown>;\n}\n\nexport interface SignIdTokenHintInput extends IdTokenHintPayload {\n /** The OAuth app's `jwt_signing_secret`. */\n jwtSigningSecret: string;\n}\n\ninterface JoseModule {\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader(header: { alg: string; typ?: string }): JoseSignJwt;\n setIssuedAt(time?: number): JoseSignJwt;\n setExpirationTime(time: string | number): JoseSignJwt;\n setIssuer(issuer: string): JoseSignJwt;\n setSubject(subject: string): JoseSignJwt;\n setAudience(audience: string | string[]): JoseSignJwt;\n sign(secret: Uint8Array | CryptoKey): Promise<string>;\n };\n}\ntype JoseSignJwt = InstanceType<JoseModule[\"SignJWT\"]>;\n\nlet cachedJose: JoseModule | null = null;\nlet joseLoadAttempted = false;\n\nasync function loadJose(): Promise<JoseModule> {\n if (cachedJose) return cachedJose;\n if (joseLoadAttempted && !cachedJose) {\n throw new AtribuConfigError(\n \"signIdTokenHint requires the `jose` peer dependency. Run `npm install jose`.\",\n );\n }\n joseLoadAttempted = true;\n try {\n const mod = (await import(\"jose\")) as unknown as JoseModule;\n cachedJose = mod;\n return mod;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"import failed\";\n throw new AtribuConfigError(\n `signIdTokenHint requires the \\`jose\\` peer dependency. Install with \\`npm install jose\\`. (${message})`,\n );\n }\n}\n\nexport async function signIdTokenHint(input: SignIdTokenHintInput): Promise<string> {\n if (!input.jwtSigningSecret) {\n throw new AtribuConfigError(\"signIdTokenHint: jwtSigningSecret is required\");\n }\n if (!input.subject) throw new AtribuConfigError(\"signIdTokenHint: subject is required\");\n if (!input.email) throw new AtribuConfigError(\"signIdTokenHint: email is required\");\n\n const jose = await loadJose();\n const claims: Record<string, unknown> = { email: input.email, ...(input.extraClaims ?? {}) };\n\n const jwt = new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setIssuedAt()\n .setSubject(input.subject)\n .setAudience(input.audience ?? \"atribu\")\n .setExpirationTime(input.expiresIn ?? \"5m\");\n if (input.issuer) jwt.setIssuer(input.issuer);\n\n return jwt.sign(new TextEncoder().encode(input.jwtSigningSecret));\n}\n","/**\n * PKCE helpers (RFC 7636).\n *\n * Generates an unguessable `code_verifier` and the base64url(SHA-256) digest\n * to use as `code_challenge` with `code_challenge_method=S256`.\n */\n\nconst VERIFIER_CHARS =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n\nexport function generateCodeVerifier(length = 64): string {\n if (length < 43 || length > 128) {\n throw new Error(\"PKCE verifier length must be 43–128\");\n }\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += VERIFIER_CHARS[bytes[i]! % VERIFIER_CHARS.length];\n }\n return out;\n}\n\nexport async function computeCodeChallenge(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);\n const b64 =\n typeof btoa === \"function\"\n ? btoa(bin)\n : Buffer.from(bytes).toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/oauth/authorize-url.ts","../../src/version.ts","../../src/runtime.ts","../../src/oauth/exchange.ts","../../src/oauth/revoke.ts","../../src/oauth/id-token-hint.ts","../../src/oauth/pkce.ts"],"names":["DEFAULT_BASE_URL","body","base64"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Atribu error class hierarchy.\n *\n * Why typed errors: consumers need to branch on auth-failure vs rate-limit vs\n * server-error vs validation-error to decide whether to retry, refresh\n * credentials, or surface to the user. A single `Error` with a status code\n * forces every consumer to write the same `if (err.status === 401)` ladder.\n *\n * Why no automatic retries: the SDK derives a `retry` hint from status +\n * Retry-After + error code, but consumers' queue/job systems decide whether\n * to act on it. Auto-retry inside the SDK hides backpressure signals and\n * makes error budgets opaque.\n */\n\nimport type { RetryHint } from \"./retry\";\n\nexport type ApiErrorCode =\n | \"unauthorized\"\n | \"forbidden\"\n | \"insufficient_scope\"\n | \"not_found\"\n | \"invalid_parameter\"\n | \"invalid_request\"\n | \"validation_error\"\n | \"invalid_content\"\n | \"invalid_date_range\"\n | \"rate_limit_exceeded\"\n | \"connection_not_ready\"\n | \"provider_error\"\n | \"service_unavailable\"\n | \"internal_error\"\n | string;\n\nexport class AtribuError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class AtribuConfigError extends AtribuError {}\n\nexport class AtribuTransportError extends AtribuError {\n readonly cause: unknown;\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\nexport interface ApiErrorBody {\n code: ApiErrorCode;\n message: string;\n status: number;\n request_id?: string;\n}\n\nexport class AtribuApiError extends AtribuError {\n readonly code: ApiErrorCode;\n readonly status: number;\n readonly requestId: string | null;\n readonly retry: RetryHint;\n readonly responseBody: unknown;\n\n constructor(args: {\n code: ApiErrorCode;\n message: string;\n status: number;\n requestId: string | null;\n retry: RetryHint;\n responseBody: unknown;\n }) {\n super(`[${args.code}] ${args.message}`);\n this.code = args.code;\n this.status = args.status;\n this.requestId = args.requestId;\n this.retry = args.retry;\n this.responseBody = args.responseBody;\n }\n\n isRetryable(): boolean {\n return this.retry.action === \"retry\" || this.retry.action === \"retry_after\";\n }\n isAuthFailure(): boolean {\n return this.status === 401 || this.code === \"unauthorized\";\n }\n isRateLimit(): boolean {\n return this.status === 429 || this.code === \"rate_limit_exceeded\";\n }\n}\n\nexport type OauthErrorCode =\n | \"invalid_request\"\n | \"invalid_client\"\n | \"invalid_grant\"\n | \"unauthorized_client\"\n | \"unsupported_grant_type\"\n | \"invalid_scope\"\n | \"server_error\"\n | \"unsupported_token_type\"\n | string;\n\nexport class AtribuOauthError extends AtribuError {\n readonly code: OauthErrorCode;\n readonly status: number;\n readonly description: string | null;\n\n constructor(args: { code: OauthErrorCode; description: string | null; status: number }) {\n super(`[oauth/${args.code}] ${args.description ?? args.code}`);\n this.code = args.code;\n this.status = args.status;\n this.description = args.description;\n }\n}\n\nexport type WebhookErrorCode =\n | \"missing_signature\"\n | \"malformed_header\"\n | \"expired_timestamp\"\n | \"invalid_signature\";\n\nexport class AtribuWebhookError extends AtribuError {\n readonly code: WebhookErrorCode;\n constructor(code: WebhookErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n","import { AtribuConfigError } from \"../errors\";\n\nexport type OauthProvider = \"whatsapp\" | \"instagram\";\nexport type OauthScope = \"whatsapp\" | \"instagram\";\n\nexport interface BuildAuthorizeUrlInput {\n baseUrl?: string;\n clientId: string;\n redirectUri: string;\n provider: OauthProvider;\n scope: OauthScope | OauthScope[];\n state: string;\n /** Pre-signed `id_token_hint` JWT — use `signIdTokenHint` to mint. */\n idTokenHint: string;\n /** PKCE challenge (base64url of SHA-256). */\n codeChallenge?: string;\n codeChallengeMethod?: \"S256\" | \"plain\";\n /** Forwards extra query params unchanged. */\n extras?: Record<string, string>;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\n/**\n * Build the `/oauth/authorize` URL the user should be redirected to.\n *\n * Pure function — returns a string. No network calls, no crypto. The only\n * runtime check is required-arg validation.\n */\nexport function buildAuthorizeUrl(input: BuildAuthorizeUrlInput): string {\n for (const key of [\"clientId\", \"redirectUri\", \"provider\", \"state\", \"idTokenHint\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`buildAuthorizeUrl: ${key} is required`);\n }\n if (input.codeChallenge && !input.codeChallengeMethod) {\n throw new AtribuConfigError(\"codeChallengeMethod is required when codeChallenge is set\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const scope = Array.isArray(input.scope) ? input.scope.join(\" \") : input.scope;\n\n const url = new URL(\"/oauth/authorize\", baseUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", input.clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"provider\", input.provider);\n url.searchParams.set(\"scope\", scope);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"id_token_hint\", input.idTokenHint);\n if (input.codeChallenge && input.codeChallengeMethod) {\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", input.codeChallengeMethod);\n }\n if (input.extras) {\n for (const [k, v] of Object.entries(input.extras)) url.searchParams.set(k, v);\n }\n return url.toString();\n}\n","export const SDK_VERSION = \"0.1.1\";\n","export type Runtime = \"node\" | \"bun\" | \"deno\" | \"edge\" | \"browser\" | \"unknown\";\n\ndeclare const Deno: unknown;\ndeclare const Bun: unknown;\ndeclare const EdgeRuntime: unknown;\n\nexport function detectRuntime(): Runtime {\n if (typeof Deno !== \"undefined\") return \"deno\";\n if (typeof Bun !== \"undefined\") return \"bun\";\n if (typeof EdgeRuntime !== \"undefined\") return \"edge\";\n if (\n typeof process !== \"undefined\" &&\n typeof (process as { versions?: { node?: string } }).versions?.node === \"string\"\n ) {\n return \"node\";\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") return \"browser\";\n return \"unknown\";\n}\n\nexport function runtimeTag(): string {\n const rt = detectRuntime();\n if (rt === \"node\") {\n const v = (process as { versions?: { node?: string } }).versions?.node;\n return v ? `node/${v}` : \"node\";\n }\n return rt;\n}\n","/**\n * Exchange an authorization code for an Atribu access token.\n *\n * Calls `POST /oauth/token` with form-urlencoded body (RFC 6749 default).\n * Tries `client_secret_basic` (Authorization header) by default; consumers\n * can opt into `client_secret_post` via `authMethod: \"body\"`.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface ExchangeCodeInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n /** \"basic\" (default) sends Authorization: Basic; \"body\" puts creds in form body. */\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nexport interface ExchangeCodeResult {\n accessToken: string;\n tokenType: \"bearer\";\n scope: string[];\n connectionId: string | null;\n profileId: string;\n workspaceId: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function exchangeCode(input: ExchangeCodeInput): Promise<ExchangeCodeResult> {\n for (const key of [\"clientId\", \"clientSecret\", \"code\", \"redirectUri\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`exchangeCode: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/token`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n };\n if (input.codeVerifier) form.code_verifier = input.codeVerifier;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n const text = await res.text();\n const parsed = text ? safeJson(text) : null;\n\n if (!res.ok) {\n const body = (parsed ?? {}) as { error?: unknown; error_description?: unknown };\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n }\n const body = parsed as {\n access_token: string;\n token_type: string;\n scope?: string;\n connection_id?: string;\n profile_id: string;\n workspace_id: string;\n };\n return {\n accessToken: body.access_token,\n tokenType: \"bearer\",\n scope: typeof body.scope === \"string\" ? body.scope.split(/\\s+/).filter(Boolean) : [],\n connectionId: body.connection_id ?? null,\n profileId: body.profile_id,\n workspaceId: body.workspace_id,\n };\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","/**\n * Revoke an Atribu access token via RFC 7009.\n *\n * Always returns void on success — the server returns 200 regardless of\n * whether the token existed (RFC §2.2; no enumeration). Authentication\n * errors (bad client credentials) still throw AtribuOauthError.\n */\n\nimport {\n AtribuConfigError,\n AtribuOauthError,\n AtribuTransportError,\n} from \"../errors\";\nimport { SDK_VERSION } from \"../version\";\nimport { runtimeTag } from \"../runtime\";\n\nexport interface RevokeTokenInput {\n baseUrl?: string;\n clientId: string;\n clientSecret: string;\n token: string;\n authMethod?: \"basic\" | \"body\";\n fetch?: typeof fetch;\n timeoutMs?: number;\n signal?: AbortSignal;\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nexport async function revokeToken(input: RevokeTokenInput): Promise<void> {\n for (const key of [\"clientId\", \"clientSecret\", \"token\"] as const) {\n if (!input[key]) throw new AtribuConfigError(`revokeToken: ${key} is required`);\n }\n const fetchImpl = input.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new AtribuConfigError(\"globalThis.fetch is not available; pass a `fetch` impl\");\n }\n const baseUrl = (input.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const url = `${baseUrl}/oauth/revoke`;\n const authMethod = input.authMethod ?? \"basic\";\n\n const form: Record<string, string> = {\n token: input.token,\n token_type_hint: \"access_token\",\n };\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"User-Agent\": `@atribu/node/${SDK_VERSION} (${runtimeTag()})`,\n };\n if (authMethod === \"basic\") {\n headers.Authorization = `Basic ${base64(`${input.clientId}:${input.clientSecret}`)}`;\n } else {\n form.client_id = input.clientId;\n form.client_secret = input.clientSecret;\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), input.timeoutMs ?? 30_000);\n const signal = input.signal ?? controller.signal;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: new URLSearchParams(form).toString(),\n signal,\n });\n } catch (err) {\n clearTimeout(timeout);\n throw new AtribuTransportError(\n err instanceof Error ? err.message : \"fetch failed\",\n err,\n );\n }\n clearTimeout(timeout);\n\n if (res.ok) return;\n const text = await res.text();\n let body: { error?: unknown; error_description?: unknown } = {};\n try {\n body = text ? (JSON.parse(text) as typeof body) : {};\n } catch {\n body = {};\n }\n throw new AtribuOauthError({\n code: typeof body.error === \"string\" ? body.error : \"server_error\",\n description: typeof body.error_description === \"string\" ? body.error_description : null,\n status: res.status,\n });\n}\n\nfunction base64(input: string): string {\n if (typeof btoa === \"function\") return btoa(input);\n return Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","/**\n * Sign an `id_token_hint` JWT (HS256) for an Atribu OAuth flow.\n *\n * Uses `jose` (optional peer dep). If `jose` isn't installed, throws a\n * clear runtime error. Vitrina-style consumers almost always already have\n * `jose` for their own auth.\n *\n * Why HS256 not RS256: the shared secret is the OAuth app's\n * `jwt_signing_secret` (rotatable, paired). RS256 would mean Atribu\n * publishes a JWKS endpoint and we manage keypairs — out of scope today.\n */\n\nimport { AtribuConfigError } from \"../errors\";\n\nexport interface IdTokenHintPayload {\n /** End-user's stable identifier in your system. */\n subject: string;\n /** End-user's email. Used by Atribu to silent-provision / find existing profile. */\n email: string;\n /** Token audience. Use \"atribu\" unless directed otherwise. */\n audience?: string;\n /** Issuer (your app's client_id, by convention). */\n issuer?: string;\n /** Expiration relative to now. Default \"5m\". Examples: \"5m\", \"10m\", numeric seconds. */\n expiresIn?: string | number;\n /** Atribu's recognized claims plus any custom ones. */\n extraClaims?: Record<string, unknown>;\n}\n\nexport interface SignIdTokenHintInput extends IdTokenHintPayload {\n /** The OAuth app's `jwt_signing_secret`. */\n jwtSigningSecret: string;\n}\n\ninterface JoseModule {\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader(header: { alg: string; typ?: string }): JoseSignJwt;\n setIssuedAt(time?: number): JoseSignJwt;\n setExpirationTime(time: string | number): JoseSignJwt;\n setIssuer(issuer: string): JoseSignJwt;\n setSubject(subject: string): JoseSignJwt;\n setAudience(audience: string | string[]): JoseSignJwt;\n sign(secret: Uint8Array | CryptoKey): Promise<string>;\n };\n}\ntype JoseSignJwt = InstanceType<JoseModule[\"SignJWT\"]>;\n\nlet cachedJose: JoseModule | null = null;\nlet joseLoadAttempted = false;\n\nasync function loadJose(): Promise<JoseModule> {\n if (cachedJose) return cachedJose;\n if (joseLoadAttempted && !cachedJose) {\n throw new AtribuConfigError(\n \"signIdTokenHint requires the `jose` peer dependency. Run `npm install jose`.\",\n );\n }\n joseLoadAttempted = true;\n try {\n const mod = (await import(\"jose\")) as unknown as JoseModule;\n cachedJose = mod;\n return mod;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"import failed\";\n throw new AtribuConfigError(\n `signIdTokenHint requires the \\`jose\\` peer dependency. Install with \\`npm install jose\\`. (${message})`,\n );\n }\n}\n\nexport async function signIdTokenHint(input: SignIdTokenHintInput): Promise<string> {\n if (!input.jwtSigningSecret) {\n throw new AtribuConfigError(\"signIdTokenHint: jwtSigningSecret is required\");\n }\n if (!input.subject) throw new AtribuConfigError(\"signIdTokenHint: subject is required\");\n if (!input.email) throw new AtribuConfigError(\"signIdTokenHint: email is required\");\n\n const jose = await loadJose();\n const claims: Record<string, unknown> = { email: input.email, ...(input.extraClaims ?? {}) };\n\n const jwt = new jose.SignJWT(claims)\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setIssuedAt()\n .setSubject(input.subject)\n .setAudience(input.audience ?? \"atribu\")\n .setExpirationTime(input.expiresIn ?? \"5m\");\n if (input.issuer) jwt.setIssuer(input.issuer);\n\n return jwt.sign(new TextEncoder().encode(input.jwtSigningSecret));\n}\n","/**\n * PKCE helpers (RFC 7636).\n *\n * Generates an unguessable `code_verifier` and the base64url(SHA-256) digest\n * to use as `code_challenge` with `code_challenge_method=S256`.\n */\n\nconst VERIFIER_CHARS =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n\nexport function generateCodeVerifier(length = 64): string {\n if (length < 43 || length > 128) {\n throw new Error(\"PKCE verifier length must be 43–128\");\n }\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += VERIFIER_CHARS[bytes[i]! % VERIFIER_CHARS.length];\n }\n return out;\n}\n\nexport async function computeCodeChallenge(verifier: string): Promise<string> {\n const data = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);\n const b64 =\n typeof btoa === \"function\"\n ? btoa(bin)\n : Buffer.from(bytes).toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"]}
|