@iqauth/sdk 2.6.3 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +173 -1
  2. package/dist/browser-session.d.mts +4 -4
  3. package/dist/browser-session.d.ts +4 -4
  4. package/dist/browser-session.js +181 -41
  5. package/dist/browser-session.mjs +3 -3
  6. package/dist/browser.d.mts +5 -5
  7. package/dist/browser.d.ts +5 -5
  8. package/dist/browser.js +271 -32
  9. package/dist/browser.mjs +10 -8
  10. package/dist/{chunk-6I6RM4MN.mjs → chunk-6PJRLRB4.mjs} +33 -3
  11. package/dist/chunk-C2ZTBOAC.mjs +36 -0
  12. package/dist/{chunk-LIZYFXH7.mjs → chunk-DFWHSDYQ.mjs} +1 -1
  13. package/dist/chunk-GLXSIGVS.mjs +66 -0
  14. package/dist/{chunk-TKZTCPEK.mjs → chunk-GN37E64I.mjs} +32 -40
  15. package/dist/{chunk-WQWBJSSS.mjs → chunk-HVHNYPDC.mjs} +6 -6
  16. package/dist/{chunk-W3F4JYGP.mjs → chunk-JXQI62A7.mjs} +108 -18
  17. package/dist/{chunk-UNYDG2L4.mjs → chunk-NUO2I65G.mjs} +56 -23
  18. package/dist/chunk-PMAFENVI.mjs +229 -0
  19. package/dist/chunk-RR2MGPTK.mjs +2724 -0
  20. package/dist/{chunk-76W5TLQQ.mjs → chunk-RTJAIBXY.mjs} +220 -20
  21. package/dist/{chunk-6TDJJER7.mjs → chunk-RUJXRTEW.mjs} +164 -5
  22. package/dist/{chunk-3JULWS6F.mjs → chunk-WCELYTJ3.mjs} +3 -3
  23. package/dist/{chunk-MKKZULZR.mjs → chunk-WIFG74IK.mjs} +1 -1
  24. package/dist/{chunk-BVV54LPI.mjs → chunk-YVALAG3B.mjs} +10 -4
  25. package/dist/cli/index.js +2 -2
  26. package/dist/cli/index.mjs +2 -2
  27. package/dist/{client-kYlJFgPv.d.mts → client-BGFnBpfc.d.mts} +47 -4
  28. package/dist/{client-BNQe3AgF.d.ts → client-CDQ21LvW.d.ts} +47 -4
  29. package/dist/{doctor-YYNHNMLD.mjs → doctor-JAFXWU3X.mjs} +2 -2
  30. package/dist/errors-Jl1Jtm-6.d.mts +107 -0
  31. package/dist/errors-Jl1Jtm-6.d.ts +107 -0
  32. package/dist/{express-B6_1vBYZ.d.mts → express-CVNQEkOr.d.mts} +2 -2
  33. package/dist/{express-CHpfa7D_.d.ts → express-Piv2WhWM.d.ts} +2 -2
  34. package/dist/express.d.mts +7 -6
  35. package/dist/express.d.ts +7 -6
  36. package/dist/express.js +349 -52
  37. package/dist/express.mjs +39 -12
  38. package/dist/fastify.d.mts +2 -0
  39. package/dist/fastify.d.ts +2 -0
  40. package/dist/fastify.js +332 -52
  41. package/dist/fastify.mjs +23 -8
  42. package/dist/hono.d.mts +2 -0
  43. package/dist/hono.d.ts +2 -0
  44. package/dist/hono.js +329 -52
  45. package/dist/hono.mjs +20 -8
  46. package/dist/index-5KSZEnDe.d.ts +1626 -0
  47. package/dist/index-CKoZHAoc.d.mts +1626 -0
  48. package/dist/index.d.mts +56 -8
  49. package/dist/index.d.ts +56 -8
  50. package/dist/index.js +565 -69
  51. package/dist/index.mjs +29 -9
  52. package/dist/{keys-NLWFAOEM.mjs → keys-6Y776TG2.mjs} +2 -2
  53. package/dist/locales.d.mts +1 -1
  54. package/dist/locales.d.ts +1 -1
  55. package/dist/mobile.d.mts +77 -7
  56. package/dist/mobile.d.ts +77 -7
  57. package/dist/mobile.js +276 -41
  58. package/dist/mobile.mjs +98 -3
  59. package/dist/next.d.mts +2 -1
  60. package/dist/next.d.ts +2 -1
  61. package/dist/next.js +391 -201
  62. package/dist/next.mjs +22 -7
  63. package/dist/pkce-7WKV4OIN.mjs +11 -0
  64. package/dist/{provisioningBridge-DnTfzdZK.d.ts → provisioningBridge-CGpMRie4.d.ts} +1 -1
  65. package/dist/{provisioningBridge-88xjOS2n.d.mts → provisioningBridge-M5G47LWO.d.mts} +1 -1
  66. package/dist/{publishableKey-BaR0HoAH.d.ts → publishableKey-f2kq-rKw.d.mts} +1 -1
  67. package/dist/{publishableKey-BaR0HoAH.d.mts → publishableKey-f2kq-rKw.d.ts} +1 -1
  68. package/dist/react-permissions.d.mts +52 -0
  69. package/dist/react-permissions.d.ts +52 -0
  70. package/dist/react-permissions.js +239 -0
  71. package/dist/react-permissions.mjs +97 -0
  72. package/dist/react.d.mts +9 -1624
  73. package/dist/react.d.ts +9 -1624
  74. package/dist/react.js +343 -36
  75. package/dist/react.mjs +59 -2611
  76. package/dist/{reverify-4UEJXUS6.mjs → reverify-C64QXKJO.mjs} +2 -2
  77. package/dist/server/handlers.d.mts +148 -3
  78. package/dist/server/handlers.d.ts +148 -3
  79. package/dist/server/handlers.js +410 -11
  80. package/dist/server/handlers.mjs +12 -3
  81. package/dist/server.d.mts +151 -8
  82. package/dist/server.d.ts +151 -8
  83. package/dist/server.js +406 -50
  84. package/dist/server.mjs +93 -11
  85. package/dist/service.d.mts +4 -4
  86. package/dist/service.d.ts +4 -4
  87. package/dist/service.js +181 -41
  88. package/dist/service.mjs +3 -3
  89. package/dist/{signIn-CiIBTJIh.d.mts → signIn-BLFnz8SV.d.ts} +78 -3
  90. package/dist/{signIn-CCY4JE5G.mjs → signIn-SHBW6Z4T.mjs} +2 -1
  91. package/dist/{signIn-OCr88Zf8.d.ts → signIn-T-CZ6t6r.d.mts} +78 -3
  92. package/dist/test.mjs +3 -3
  93. package/dist/{tokens-DCyzzn8L.d.mts → tokens-Bqhmqq_R.d.ts} +9 -2
  94. package/dist/{tokens-aHiGFr_E.d.ts → tokens-CITeoG6P.d.mts} +9 -2
  95. package/dist/{types-6bNdxesb.d.ts → types-BdQ2lqfT.d.mts} +1 -1
  96. package/dist/{types-6bNdxesb.d.mts → types-BdQ2lqfT.d.ts} +1 -1
  97. package/dist/{types-DZAflmmq.d.mts → types-XOV9XPVi.d.mts} +99 -10
  98. package/dist/{types-DZAflmmq.d.ts → types-XOV9XPVi.d.ts} +99 -10
  99. package/dist/webhooks.d.mts +100 -17
  100. package/dist/webhooks.d.ts +100 -17
  101. package/dist/webhooks.js +164 -15
  102. package/dist/webhooks.mjs +7 -1
  103. package/dist/ws.d.mts +2 -2
  104. package/dist/ws.d.ts +2 -2
  105. package/dist/ws.js +80 -30
  106. package/dist/ws.mjs +4 -4
  107. package/docs/error-handling.md +101 -0
  108. package/docs/guides/effective-permissions.md +171 -0
  109. package/package.json +13 -3
  110. package/dist/chunk-UKZLOHZG.mjs +0 -83
  111. package/dist/errors-CDdl24MP.d.mts +0 -52
  112. package/dist/errors-CDdl24MP.d.ts +0 -52
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IQAuthError
3
- } from "./chunk-6I6RM4MN.mjs";
3
+ } from "./chunk-6PJRLRB4.mjs";
4
4
  import {
5
5
  __require
6
6
  } from "./chunk-Y6FXYEAI.mjs";
@@ -23,6 +23,18 @@ var DEFAULT_TOKEN_AUDIENCE = [
23
23
  "iqvalidate"
24
24
  ];
25
25
  var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
26
+ function classifyJoseError(err) {
27
+ if (err instanceof joseErrors.JWTExpired) {
28
+ return { code: "token_expired", message: "Token has expired" };
29
+ }
30
+ if (err instanceof joseErrors.JOSEError) {
31
+ return { code: "token_invalid", message: err.message };
32
+ }
33
+ if (err instanceof Error) {
34
+ return { code: "token_invalid", message: err.message };
35
+ }
36
+ return { code: "token_invalid", message: "Token verification failed" };
37
+ }
26
38
  function decodeProtectedHeader(token) {
27
39
  const parts = token.split(".");
28
40
  if (parts.length < 2) return null;
@@ -59,11 +71,11 @@ var TokensModule = class {
59
71
  async verify(token, options = {}) {
60
72
  const header = decodeProtectedHeader(token);
61
73
  if (!header) {
62
- throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
74
+ throw new IQAuthError("token_invalid", "Unable to decode token");
63
75
  }
64
76
  const kid = header.kid;
65
77
  if (!kid) {
66
- throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
78
+ throw new IQAuthError("token_invalid", "Token missing kid header");
67
79
  }
68
80
  let cache = await this.ensureCache();
69
81
  if (!cache.byKid.has(kid)) {
@@ -71,7 +83,7 @@ var TokensModule = class {
71
83
  cache = await this.ensureCache();
72
84
  }
73
85
  if (!cache.byKid.has(kid)) {
74
- throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
86
+ throw new IQAuthError("token_invalid", `Unknown key ID: ${kid}`);
75
87
  }
76
88
  const issuer = options.issuer ?? this.defaultIssuer;
77
89
  const audience = options.audience ?? this.defaultAudience;
@@ -87,16 +99,8 @@ var TokensModule = class {
87
99
  const { payload } = await jwtVerify(token, cache.verifier, verifyOptions);
88
100
  return payload;
89
101
  } catch (err) {
90
- if (err instanceof joseErrors.JWTExpired) {
91
- throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
92
- }
93
- if (err instanceof joseErrors.JOSEError) {
94
- throw new IQAuthError("TOKEN_INVALID", err.message);
95
- }
96
- if (err instanceof Error) {
97
- throw new IQAuthError("TOKEN_INVALID", err.message);
98
- }
99
- throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
102
+ const classified = classifyJoseError(err);
103
+ throw new IQAuthError(classified.code, classified.message, void 0, err);
100
104
  }
101
105
  }
102
106
  /**
@@ -138,7 +142,7 @@ var TokensModule = class {
138
142
  getClaims(token) {
139
143
  const claims = this.decode(token);
140
144
  if (!claims) {
141
- throw new IQAuthError("TOKEN_INVALID", "Unable to decode token claims");
145
+ throw new IQAuthError("token_invalid", "Unable to decode token claims");
142
146
  }
143
147
  return claims;
144
148
  }
@@ -148,7 +152,7 @@ var TokensModule = class {
148
152
  }
149
153
  await this.refreshJwks();
150
154
  if (!this.jwksCache) {
151
- throw new IQAuthError("INTERNAL_ERROR", "JWKS cache unavailable after refresh");
155
+ throw new IQAuthError("jwks_unavailable", "JWKS cache unavailable after refresh");
152
156
  }
153
157
  return this.jwksCache;
154
158
  }
@@ -158,22 +162,38 @@ var TokensModule = class {
158
162
  }
159
163
  this.inFlightRefresh = (async () => {
160
164
  try {
161
- const res = await fetch(`${this.baseUrl}/.well-known/jwks.json`);
165
+ let res;
166
+ try {
167
+ res = await fetch(`${this.baseUrl}/.well-known/jwks.json`);
168
+ } catch (err) {
169
+ throw new IQAuthError(
170
+ "network",
171
+ err instanceof Error ? err.message : "JWKS fetch network error",
172
+ void 0,
173
+ err
174
+ );
175
+ }
162
176
  if (!res.ok) {
163
177
  throw new IQAuthError(
164
- "INTERNAL_ERROR",
165
- `Failed to fetch JWKS: ${res.status}`
178
+ "jwks_fetch_failed",
179
+ `Failed to fetch JWKS: ${res.status}`,
180
+ res.status
166
181
  );
167
182
  }
168
183
  let jwks;
169
184
  try {
170
185
  jwks = await res.json();
171
- } catch {
172
- throw new IQAuthError("INTERNAL_ERROR", "Malformed JWKS response: invalid JSON");
186
+ } catch (err) {
187
+ throw new IQAuthError(
188
+ "jwks_fetch_failed",
189
+ "Malformed JWKS response: invalid JSON",
190
+ res.status,
191
+ err
192
+ );
173
193
  }
174
194
  if (!jwks || !Array.isArray(jwks.keys)) {
175
195
  throw new IQAuthError(
176
- "INTERNAL_ERROR",
196
+ "jwks_fetch_failed",
177
197
  "Malformed JWKS response: expected { keys: [...] }"
178
198
  );
179
199
  }
@@ -181,7 +201,7 @@ var TokensModule = class {
181
201
  for (const key of jwks.keys) {
182
202
  if (!key || typeof key.kid !== "string" || typeof key.n !== "string" && typeof key.x !== "string" || key.kty === "RSA" && (typeof key.n !== "string" || typeof key.e !== "string")) {
183
203
  throw new IQAuthError(
184
- "INTERNAL_ERROR",
204
+ "jwks_fetch_failed",
185
205
  "Malformed JWKS response: key missing required fields"
186
206
  );
187
207
  }
@@ -199,6 +219,19 @@ var TokensModule = class {
199
219
  clearCache() {
200
220
  this.jwksCache = null;
201
221
  }
222
+ /**
223
+ * Task #126: Eagerly populate the JWKS cache so the first verify() call
224
+ * doesn't pay a network round-trip. Safe to call repeatedly — single-flight
225
+ * behavior is shared with the lazy refresh path. Errors are swallowed so
226
+ * callers (e.g. `attachHelpers` auto-prewarm) can fire-and-forget.
227
+ */
228
+ async prewarm() {
229
+ if (this.jwksCache && Date.now() - this.jwksCache.fetchedAt <= JWKS_CACHE_TTL_MS) return;
230
+ try {
231
+ await this.refreshJwks();
232
+ } catch {
233
+ }
234
+ }
202
235
  };
203
236
 
204
237
  export {
@@ -0,0 +1,229 @@
1
+ // src/webhooks.ts
2
+ import crypto from "crypto";
3
+ var WebhookSignatureError = class extends Error {
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = "WebhookSignatureError";
7
+ this.code = code;
8
+ }
9
+ };
10
+ var IQAUTH_SIGNATURE_HEADER = "x-iqauth-signature";
11
+ var LEGACY_SIGNATURE_HEADERS = [
12
+ "x-webhook-signature",
13
+ "x-iq-auth-signature",
14
+ "x-signature"
15
+ ];
16
+ function toBuffer(p) {
17
+ if (typeof p === "string") return Buffer.from(p, "utf8");
18
+ if (Buffer.isBuffer(p)) return p;
19
+ return Buffer.from(p);
20
+ }
21
+ function parseHeader(header) {
22
+ let t = NaN;
23
+ const v1 = [];
24
+ const trimmed = header.trim();
25
+ if (/^[0-9a-f]+$/i.test(trimmed)) {
26
+ v1.push(trimmed.toLowerCase());
27
+ return { t, v1 };
28
+ }
29
+ for (const part of trimmed.split(",")) {
30
+ const eqIdx = part.indexOf("=");
31
+ if (eqIdx === -1) continue;
32
+ const key = part.slice(0, eqIdx).trim().toLowerCase();
33
+ const value = part.slice(eqIdx + 1).trim();
34
+ if (!value) continue;
35
+ if (key === "t") t = Number(value);
36
+ else if (key === "v1") v1.push(value.toLowerCase());
37
+ }
38
+ return { t, v1 };
39
+ }
40
+ function timingSafeEqualHex(a, b) {
41
+ if (a.length !== b.length) return false;
42
+ try {
43
+ return crypto.timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function computeSignatures(secret, body, t) {
49
+ const modern = crypto.createHmac("sha256", secret).update(body).digest("hex");
50
+ const legacy = Number.isFinite(t) ? crypto.createHmac("sha256", secret).update(`${t}.`).update(body).digest("hex") : null;
51
+ return { modern, legacy };
52
+ }
53
+ function verifyWebhookSignature(opts) {
54
+ const headerRaw = Array.isArray(opts.header) ? opts.header[0] : opts.header;
55
+ if (!headerRaw || typeof headerRaw !== "string") {
56
+ throw new WebhookSignatureError("MISSING_HEADER", "Missing X-IQAuth-Signature header");
57
+ }
58
+ if (!opts.secret) {
59
+ throw new WebhookSignatureError("MISSING_SECRET", "secret is required");
60
+ }
61
+ const { t, v1 } = parseHeader(headerRaw);
62
+ if (v1.length === 0) {
63
+ throw new WebhookSignatureError("MALFORMED_HEADER", `Could not parse signature header: ${headerRaw}`);
64
+ }
65
+ const tolerance = opts.toleranceSeconds ?? 300;
66
+ if (Number.isFinite(t)) {
67
+ const now = opts.nowSeconds ?? Math.floor(Date.now() / 1e3);
68
+ if (Math.abs(now - t) > tolerance) {
69
+ throw new WebhookSignatureError(
70
+ "TIMESTAMP_OUT_OF_TOLERANCE",
71
+ `Signature timestamp ${t} is outside the ${tolerance}s tolerance window (now=${now})`
72
+ );
73
+ }
74
+ }
75
+ const body = toBuffer(opts.payload);
76
+ const { modern, legacy } = computeSignatures(opts.secret, body, t);
77
+ const matched = v1.some((sig) => {
78
+ const lower = sig.toLowerCase();
79
+ if (timingSafeEqualHex(lower, modern)) return true;
80
+ if (legacy && timingSafeEqualHex(lower, legacy)) return true;
81
+ return false;
82
+ });
83
+ if (!matched) {
84
+ throw new WebhookSignatureError("SIGNATURE_MISMATCH", "Webhook signature does not match expected value");
85
+ }
86
+ let parsed;
87
+ try {
88
+ parsed = JSON.parse(body.toString("utf8"));
89
+ } catch {
90
+ throw new WebhookSignatureError("MALFORMED_BODY", "Webhook body is not valid JSON");
91
+ }
92
+ return parsed;
93
+ }
94
+ function isValidWebhookSignature(opts) {
95
+ try {
96
+ verifyWebhookSignature(opts);
97
+ return true;
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+ function readHeader(headers, name) {
103
+ if (typeof headers.get === "function") {
104
+ return headers.get(name);
105
+ }
106
+ const lower = name.toLowerCase();
107
+ const obj = headers;
108
+ if (lower in obj) return obj[lower];
109
+ for (const [k, v] of Object.entries(obj)) {
110
+ if (k.toLowerCase() === lower) return v;
111
+ }
112
+ return void 0;
113
+ }
114
+ function pickHeaderValue(value) {
115
+ if (value == null) return null;
116
+ if (Array.isArray(value)) return value[0] ?? null;
117
+ return value;
118
+ }
119
+ function envelopeError(message) {
120
+ throw new WebhookSignatureError("MALFORMED_ENVELOPE", message);
121
+ }
122
+ function parseWebhookEvent(rawBody, headers, secrets, opts = {}) {
123
+ if (!Array.isArray(secrets) || secrets.length === 0 || secrets.every((s) => !s)) {
124
+ throw new WebhookSignatureError("MISSING_SECRET", "At least one signing secret is required");
125
+ }
126
+ let headerValue = pickHeaderValue(readHeader(headers, IQAUTH_SIGNATURE_HEADER));
127
+ let usedHeader = IQAUTH_SIGNATURE_HEADER;
128
+ if (!headerValue) {
129
+ for (const legacy of LEGACY_SIGNATURE_HEADERS) {
130
+ const v = pickHeaderValue(readHeader(headers, legacy));
131
+ if (v) {
132
+ headerValue = v;
133
+ usedHeader = legacy;
134
+ const log = opts.onDeprecation ?? ((m) => console.warn(m));
135
+ log(
136
+ `[iqauth] deprecation: webhook delivery used legacy header "${legacy}"; migrate sender to "X-IQAuth-Signature" (back-compat removed in next minor).`
137
+ );
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ if (!headerValue) {
143
+ throw new WebhookSignatureError(
144
+ "MISSING_HEADER",
145
+ `Missing webhook signature header. Expected "X-IQAuth-Signature" (or one of: ${LEGACY_SIGNATURE_HEADERS.join(", ")}).`
146
+ );
147
+ }
148
+ const { t, v1 } = parseHeader(headerValue);
149
+ if (v1.length === 0) {
150
+ throw new WebhookSignatureError(
151
+ "MALFORMED_HEADER",
152
+ `Could not parse "${usedHeader}" header value: ${headerValue}`
153
+ );
154
+ }
155
+ const body = toBuffer(rawBody);
156
+ let verifiedIdx = -1;
157
+ for (let i = 0; i < secrets.length; i++) {
158
+ const secret = secrets[i];
159
+ if (!secret) continue;
160
+ const { modern, legacy } = computeSignatures(secret, body, t);
161
+ const ok = v1.some((sig) => {
162
+ const lower = sig.toLowerCase();
163
+ if (timingSafeEqualHex(lower, modern)) return true;
164
+ if (legacy && timingSafeEqualHex(lower, legacy)) return true;
165
+ return false;
166
+ });
167
+ if (ok) {
168
+ verifiedIdx = i;
169
+ break;
170
+ }
171
+ }
172
+ if (verifiedIdx === -1) {
173
+ throw new WebhookSignatureError(
174
+ "SIGNATURE_MISMATCH",
175
+ "Webhook signature does not match any provided secret"
176
+ );
177
+ }
178
+ let parsed;
179
+ try {
180
+ parsed = JSON.parse(body.toString("utf8"));
181
+ } catch {
182
+ throw new WebhookSignatureError("MALFORMED_BODY", "Webhook body is not valid JSON");
183
+ }
184
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
185
+ envelopeError("Webhook body must be a JSON object");
186
+ }
187
+ const { id, type, subject, time, data, tenantId, specversion } = parsed;
188
+ if (specversion !== "1.0") {
189
+ envelopeError(`Envelope \`specversion\` must be "1.0" (got: ${JSON.stringify(specversion)})`);
190
+ }
191
+ if (typeof id !== "string" || !id) envelopeError("Envelope missing required string `id`");
192
+ if (typeof type !== "string" || !type) envelopeError("Envelope missing required string `type`");
193
+ if (typeof subject !== "string" || !subject) envelopeError("Envelope missing required string `subject`");
194
+ if (typeof time !== "string" || !time) envelopeError("Envelope missing required string `time`");
195
+ if (typeof tenantId !== "string" || !tenantId) envelopeError("Envelope missing required string `tenantId`");
196
+ if (data === void 0 || data === null || typeof data !== "object" || Array.isArray(data)) {
197
+ envelopeError("Envelope `data` must be an object");
198
+ }
199
+ const tolerance = opts.toleranceSeconds ?? 300;
200
+ const eventMs = Date.parse(time);
201
+ if (!Number.isFinite(eventMs)) envelopeError(`Envelope \`time\` is not a valid ISO timestamp: ${time}`);
202
+ const nowMs = opts.nowMs ?? Date.now();
203
+ if (Math.abs(nowMs - eventMs) > tolerance * 1e3) {
204
+ throw new WebhookSignatureError(
205
+ "TIMESTAMP_OUT_OF_TOLERANCE",
206
+ `Envelope time ${time} is outside the ${tolerance}s tolerance window (now=${new Date(nowMs).toISOString()})`
207
+ );
208
+ }
209
+ return {
210
+ specversion: "1.0",
211
+ id,
212
+ type,
213
+ subject,
214
+ time,
215
+ tenantId,
216
+ data,
217
+ idempotencyKey: id,
218
+ verifiedWithSecretIndex: verifiedIdx
219
+ };
220
+ }
221
+
222
+ export {
223
+ WebhookSignatureError,
224
+ IQAUTH_SIGNATURE_HEADER,
225
+ LEGACY_SIGNATURE_HEADERS,
226
+ verifyWebhookSignature,
227
+ isValidWebhookSignature,
228
+ parseWebhookEvent
229
+ };