@iqauth/sdk 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +43 -3
  2. package/dist/browser-session.d.mts +1 -2
  3. package/dist/browser-session.d.ts +1 -2
  4. package/dist/browser-session.js +89 -68
  5. package/dist/browser-session.mjs +2 -1
  6. package/dist/browser.d.mts +2 -2
  7. package/dist/browser.d.ts +2 -2
  8. package/dist/browser.js +69 -7
  9. package/dist/browser.mjs +2 -2
  10. package/dist/{chunk-ZESHDJDU.mjs → chunk-EKTNEZIH.mjs} +5 -8
  11. package/dist/{chunk-JQRTY5MY.mjs → chunk-KGEPDXHU.mjs} +12 -8
  12. package/dist/{chunk-S3M2IXCE.mjs → chunk-RACIPVLD.mjs} +15 -9
  13. package/dist/chunk-UNYDG2L4.mjs +209 -0
  14. package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
  15. package/dist/chunk-WQWBJSSS.mjs +119 -0
  16. package/dist/cli/index.js +21 -0
  17. package/dist/cli/index.mjs +1 -1
  18. package/dist/{client-DXbHb2ul.d.ts → client-DTX4hNdS.d.ts} +16 -21
  19. package/dist/{client-Dv4v92Mj.d.mts → client-vdh2a9fJ.d.mts} +16 -21
  20. package/dist/{doctor-OHJRZBBT.mjs → doctor-A5E7LSFW.mjs} +2 -1
  21. package/dist/{express-BZmF1llh.d.mts → express-A0-dWEMy.d.mts} +1 -1
  22. package/dist/{express-B4o3P8vK.d.ts → express-Bo_pJKHN.d.ts} +1 -1
  23. package/dist/express.d.mts +75 -5
  24. package/dist/express.d.ts +75 -5
  25. package/dist/express.js +353 -94
  26. package/dist/express.mjs +210 -12
  27. package/dist/fastify.js +153 -88
  28. package/dist/fastify.mjs +10 -9
  29. package/dist/hono.js +152 -88
  30. package/dist/hono.mjs +9 -9
  31. package/dist/index.d.mts +3 -4
  32. package/dist/index.d.ts +3 -4
  33. package/dist/index.js +148 -72
  34. package/dist/index.mjs +16 -12
  35. package/dist/mobile.d.mts +1 -2
  36. package/dist/mobile.d.ts +1 -2
  37. package/dist/mobile.js +89 -68
  38. package/dist/mobile.mjs +2 -1
  39. package/dist/next.d.mts +9 -0
  40. package/dist/next.d.ts +9 -0
  41. package/dist/next.js +164 -1649
  42. package/dist/next.mjs +13 -16
  43. package/dist/{publishableKey-B5DIK81A.d.mts → publishableKey-BaR0HoAH.d.mts} +10 -1
  44. package/dist/{publishableKey-B5DIK81A.d.ts → publishableKey-BaR0HoAH.d.ts} +10 -1
  45. package/dist/react.d.mts +35 -3
  46. package/dist/react.d.ts +35 -3
  47. package/dist/react.js +78 -18
  48. package/dist/react.mjs +14 -2
  49. package/dist/server/handlers.d.mts +2 -0
  50. package/dist/server/handlers.d.ts +2 -0
  51. package/dist/server/handlers.js +72 -17
  52. package/dist/server/handlers.mjs +3 -2
  53. package/dist/server.d.mts +2 -3
  54. package/dist/server.d.ts +2 -3
  55. package/dist/server.js +151 -89
  56. package/dist/server.mjs +7 -6
  57. package/dist/service.d.mts +1 -2
  58. package/dist/service.d.ts +1 -2
  59. package/dist/service.js +89 -68
  60. package/dist/service.mjs +2 -1
  61. package/dist/{signIn-CEMdUAwd.d.mts → signIn-Cd0P4y9d.d.mts} +9 -1
  62. package/dist/{signIn-VRNzlNyG.d.ts → signIn-DKakyzeu.d.ts} +9 -1
  63. package/package.json +3 -2
  64. package/dist/chunk-5WFR6Y33.mjs +0 -59
package/dist/next.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/next.ts
@@ -36,6 +26,17 @@ __export(next_exports, {
36
26
  });
37
27
  module.exports = __toCommonJS(next_exports);
38
28
 
29
+ // src/errors.ts
30
+ var IQAuthError = class extends Error {
31
+ constructor(code, message, status, raw) {
32
+ super(message);
33
+ this.name = "IQAuthError";
34
+ this.code = code;
35
+ this.status = status;
36
+ this.raw = raw;
37
+ }
38
+ };
39
+
39
40
  // src/publishableKey.ts
40
41
  function b64urlDecode(input) {
41
42
  const pad = input.length % 4 === 0 ? "" : "=".repeat(4 - input.length % 4);
@@ -49,20 +50,60 @@ function b64urlDecode(input) {
49
50
  const { Buffer: Buffer2 } = require("buffer");
50
51
  return Buffer2.from(normalized, "base64").toString("utf8");
51
52
  }
52
- function parsePublishableKey(raw) {
53
- if (typeof raw !== "string") return null;
54
- const m = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
55
- if (!m) return null;
53
+ function isValidIssuerUrl(iss) {
54
+ if (typeof iss !== "string" || iss.length === 0) return false;
55
+ if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
56
56
  try {
57
- const json = JSON.parse(b64urlDecode(m[2]));
58
- if (!json || typeof json !== "object") return null;
59
- if (typeof json.iss !== "string" || typeof json.appId !== "string" || typeof json.tenantId !== "string" || typeof json.kid !== "string") {
60
- return null;
61
- }
62
- return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
57
+ const u = new URL(iss);
58
+ if (u.protocol !== "http:" && u.protocol !== "https:") return false;
59
+ if (!u.hostname) return false;
60
+ return true;
63
61
  } catch {
64
- return null;
62
+ return false;
63
+ }
64
+ }
65
+ function assertPublishableKey(raw, opts) {
66
+ const ctx = opts?.context ? `${opts.context}: ` : "";
67
+ if (typeof raw !== "string" || raw.length === 0) {
68
+ throw new IQAuthError(
69
+ "CONFIG_INVALID",
70
+ `${ctx}IQAuth publishable key is missing. Set IQAUTH_PUBLISHABLE_KEY (or pass publishableKey) to a pk_test_\u2026 or pk_live_\u2026 value from the IQAuth admin console.`
71
+ );
72
+ }
73
+ const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
74
+ if (!shapeMatch) {
75
+ throw new IQAuthError(
76
+ "CONFIG_INVALID",
77
+ `${ctx}IQAuth publishable key is malformed (got ${raw.slice(0, 12)}\u2026). Expected pk_test_\u2026 or pk_live_\u2026; regenerate the key from the IQAuth admin console.`
78
+ );
79
+ }
80
+ let decoded;
81
+ try {
82
+ decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
83
+ } catch {
84
+ throw new IQAuthError(
85
+ "CONFIG_INVALID",
86
+ `${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
87
+ );
88
+ }
89
+ if (!isPublishableKeyPayload(decoded)) {
90
+ throw new IQAuthError(
91
+ "CONFIG_INVALID",
92
+ `${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
93
+ );
94
+ }
95
+ if (!isValidIssuerUrl(decoded.iss)) {
96
+ throw new IQAuthError(
97
+ "CONFIG_INVALID",
98
+ `${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console \u2014 the new key will encode a valid issuer URL.`
99
+ );
65
100
  }
101
+ return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
102
+ }
103
+ function isPublishableKeyPayload(value) {
104
+ if (!value || typeof value !== "object") return false;
105
+ const v = value;
106
+ return typeof v.iss === "string" && typeof v.appId === "string" && typeof v.tenantId === "string" && typeof v.kid === "string";
66
107
  }
67
108
 
68
109
  // src/server/handlers.ts
@@ -85,12 +126,7 @@ function shouldClearCookiesOnFailure(policy, status, errorCode) {
85
126
  var ACCESS_TOKEN_TTL_SECONDS = 60 * 15;
86
127
  var REFRESH_TOKEN_TTL_SECONDS = 60 * 60 * 24 * 30;
87
128
  function resolve(config) {
88
- const parsed = parsePublishableKey(config.publishableKey);
89
- if (!parsed) {
90
- throw new Error(
91
- "@iqauth/sdk: invalid publishable key passed to iqAuth helpers (expected pk_test_\u2026 or pk_live_\u2026)"
92
- );
93
- }
129
+ const parsed = assertPublishableKey(config.publishableKey, { context: "@iqauth/sdk helpers" });
94
130
  const inferredIssuer = parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`;
95
131
  return {
96
132
  publishableKey: config.publishableKey,
@@ -261,6 +297,15 @@ async function handleSignout(config, input) {
261
297
  } catch {
262
298
  }
263
299
  }
300
+ if (input.endSsoSession !== false && input.ssoCookieHeader) {
301
+ try {
302
+ await cfg.fetchImpl(`${cfg.issuer}/oidc/sso-logout`, {
303
+ method: "POST",
304
+ headers: { Cookie: input.ssoCookieHeader }
305
+ });
306
+ } catch {
307
+ }
308
+ }
264
309
  return {
265
310
  status: 200,
266
311
  body: { success: true, data: { signedOut: true } },
@@ -268,380 +313,8 @@ async function handleSignout(config, input) {
268
313
  };
269
314
  }
270
315
 
271
- // src/errors.ts
272
- var IQAuthError = class extends Error {
273
- constructor(code, message, status, raw) {
274
- super(message);
275
- this.name = "IQAuthError";
276
- this.code = code;
277
- this.status = status;
278
- this.raw = raw;
279
- }
280
- };
281
-
282
- // src/http.ts
283
- var DEFAULT_RETRY = {
284
- maxAttempts: 3,
285
- baseDelayMs: 100,
286
- maxDelayMs: 2e3
287
- };
288
- function resolveRetry(cfg) {
289
- return {
290
- maxAttempts: Math.max(1, cfg?.maxAttempts ?? DEFAULT_RETRY.maxAttempts),
291
- baseDelayMs: Math.max(0, cfg?.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs),
292
- maxDelayMs: Math.max(0, cfg?.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs)
293
- };
294
- }
295
- function sleep(ms) {
296
- if (ms <= 0) return Promise.resolve();
297
- return new Promise((resolve2) => setTimeout(resolve2, ms));
298
- }
299
- var HttpClient = class {
300
- constructor(config) {
301
- this.refreshPromise = null;
302
- this.config = config;
303
- this.retryConfig = resolveRetry(config.retry);
304
- }
305
- computeBackoffDelay(attempt) {
306
- const exp = Math.min(this.retryConfig.maxDelayMs, this.retryConfig.baseDelayMs * 2 ** (attempt - 1));
307
- return Math.floor(Math.random() * exp);
308
- }
309
- isRetryableStatus(status) {
310
- return status === 429 || status >= 500 && status <= 599;
311
- }
312
- async fetchWithRetry(url, init) {
313
- const { maxAttempts } = this.retryConfig;
314
- let lastError;
315
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
316
- try {
317
- const res = await fetch(url, init);
318
- if (this.isRetryableStatus(res.status) && attempt < maxAttempts) {
319
- await sleep(this.computeBackoffDelay(attempt));
320
- continue;
321
- }
322
- return res;
323
- } catch (err) {
324
- lastError = err;
325
- if (attempt >= maxAttempts) break;
326
- await sleep(this.computeBackoffDelay(attempt));
327
- }
328
- }
329
- throw lastError instanceof Error ? lastError : new IQAuthError("INTERNAL_ERROR", "Network request failed");
330
- }
331
- get baseUrl() {
332
- return this.config.baseUrl;
333
- }
334
- get environment() {
335
- return this.config.environment;
336
- }
337
- isBrowserSession() {
338
- return this.config.environment === "browser_session";
339
- }
340
- hasCredentials() {
341
- return this.isBrowserSession() || !!(this.config.getApiKey() || this.config.getAccessToken());
342
- }
343
- buildHeaders(overrideAuth) {
344
- const headers = {
345
- "Content-Type": "application/json"
346
- };
347
- if (this.isBrowserSession()) {
348
- const headerName = this.config.sessionHeaderName || "x-iqauth-session";
349
- headers[headerName] = this.config.sessionHeaderValue || "cookie";
350
- return headers;
351
- }
352
- const authMode = overrideAuth || (this.config.getApiKey() ? "apikey" : "bearer");
353
- if (authMode === "apikey") {
354
- const apiKey = this.config.getApiKey();
355
- if (apiKey) {
356
- headers["X-API-Key"] = apiKey;
357
- }
358
- } else {
359
- const token = this.config.getAccessToken();
360
- if (token) {
361
- headers["Authorization"] = `Bearer ${token}`;
362
- }
363
- }
364
- return headers;
365
- }
366
- isTokenExpiringSoon() {
367
- const token = this.config.getAccessToken();
368
- if (!token) return false;
369
- try {
370
- const parts = token.split(".");
371
- if (parts.length !== 3) return false;
372
- const payload = JSON.parse(
373
- typeof atob === "function" ? atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")) : Buffer.from(parts[1], "base64url").toString("utf8")
374
- );
375
- if (!payload.exp) return false;
376
- const now = Math.floor(Date.now() / 1e3);
377
- return payload.exp - now < 60;
378
- } catch {
379
- return false;
380
- }
381
- }
382
- async attemptRefresh() {
383
- if (this.refreshPromise) {
384
- return this.refreshPromise;
385
- }
386
- this.refreshPromise = (async () => {
387
- try {
388
- const res = await this.fetchWithRetry(`${this.config.baseUrl}/api/v1/auth/refresh`, {
389
- method: "POST",
390
- headers: this.buildHeaders(),
391
- ...this.isBrowserSession() ? { credentials: "include" } : (() => {
392
- const refreshToken = this.config.getRefreshToken();
393
- if (!refreshToken) throw new IQAuthError("TOKEN_INVALID", "No refresh token available");
394
- return { body: JSON.stringify({ refreshToken }) };
395
- })()
396
- });
397
- const body = await res.json();
398
- if (!body.success) {
399
- throw new IQAuthError(
400
- body.error.code,
401
- body.error.message,
402
- res.status,
403
- body
404
- );
405
- }
406
- if (this.isBrowserSession()) {
407
- return;
408
- }
409
- if (!body.data.accessToken || !body.data.refreshToken) {
410
- throw new IQAuthError("TOKEN_INVALID", "Refresh response did not include a token pair");
411
- }
412
- const tokens = {
413
- accessToken: body.data.accessToken,
414
- refreshToken: body.data.refreshToken
415
- };
416
- this.config.setTokens(tokens);
417
- this.config.onTokenRefresh?.(tokens);
418
- } finally {
419
- this.refreshPromise = null;
420
- }
421
- })();
422
- return this.refreshPromise;
423
- }
424
- async request(method, path, body, options) {
425
- return this.requestWithRetry(method, path, body, options, false);
426
- }
427
- async requestWithRetry(method, path, body, options, hasRetried) {
428
- if (this.config.autoRefresh && !options?.skipAutoRefresh && !this.isBrowserSession() && this.config.getRefreshToken() && this.isTokenExpiringSoon()) {
429
- await this.attemptRefresh();
430
- }
431
- const url = `${this.config.baseUrl}${path}`;
432
- const headers = this.buildHeaders(options?.authMode);
433
- const fetchOptions = {
434
- method,
435
- headers,
436
- ...this.isBrowserSession() ? { credentials: "include" } : {}
437
- };
438
- if (body !== void 0 && method !== "GET") {
439
- fetchOptions.body = JSON.stringify(body);
440
- }
441
- const res = await this.fetchWithRetry(url, fetchOptions);
442
- if (res.status === 204) {
443
- return void 0;
444
- }
445
- const responseBody = await res.json();
446
- if (!responseBody.success) {
447
- const shouldRetryRefresh = !hasRetried && this.config.autoRefresh && !options?.skipAutoRefresh && responseBody.error.code === "TOKEN_EXPIRED" && (this.isBrowserSession() || !!this.config.getRefreshToken());
448
- if (shouldRetryRefresh) {
449
- await this.attemptRefresh();
450
- return this.requestWithRetry(method, path, body, options, true);
451
- }
452
- throw new IQAuthError(
453
- responseBody.error.code,
454
- responseBody.error.message,
455
- res.status,
456
- responseBody
457
- );
458
- }
459
- return responseBody.data;
460
- }
461
- async requestRaw(method, path, body) {
462
- const url = `${this.config.baseUrl}${path}`;
463
- const headers = { "Content-Type": "application/json" };
464
- if (this.isBrowserSession()) {
465
- const headerName = this.config.sessionHeaderName || "x-iqauth-session";
466
- headers[headerName] = this.config.sessionHeaderValue || "cookie";
467
- }
468
- const token = this.config.getAccessToken();
469
- if (token) {
470
- headers["Authorization"] = `Bearer ${token}`;
471
- }
472
- const fetchOptions = { method, headers };
473
- if (this.isBrowserSession()) {
474
- fetchOptions.credentials = "include";
475
- }
476
- if (body !== void 0 && method !== "GET") {
477
- fetchOptions.body = JSON.stringify(body);
478
- }
479
- const res = await fetch(url, fetchOptions);
480
- return await res.json();
481
- }
482
- };
483
-
484
- // src/modules/auth.ts
485
- function parseLoginResponse(data, browserSessionMode) {
486
- if (data.accessToken && data.refreshToken && data.user) {
487
- return {
488
- status: "authenticated",
489
- authMode: "token",
490
- tokens: { accessToken: data.accessToken, refreshToken: data.refreshToken },
491
- user: data.user
492
- };
493
- }
494
- if (browserSessionMode && data.user) {
495
- return {
496
- status: "authenticated",
497
- authMode: "session",
498
- user: data.user
499
- };
500
- }
501
- if (data.mfaChallengeToken && !data.tenantSelectionToken) {
502
- return {
503
- status: "mfa_required",
504
- mfaChallengeToken: data.mfaChallengeToken,
505
- availableMethods: data.availableMethods ?? []
506
- };
507
- }
508
- if (data.tenantSelectionToken && data.tenants) {
509
- return {
510
- status: "tenant_selection",
511
- tenantSelectionToken: data.tenantSelectionToken,
512
- tenants: data.tenants
513
- };
514
- }
515
- throw new Error("Unexpected login response shape");
516
- }
517
- var AuthModule = class {
518
- constructor(http) {
519
- this.http = http;
520
- }
521
- async login(email, password) {
522
- const data = await this.http.request(
523
- "POST",
524
- "/api/v1/auth/login",
525
- { email, password },
526
- { skipAutoRefresh: true }
527
- );
528
- return parseLoginResponse(data, this.http.isBrowserSession());
529
- }
530
- async signup(input) {
531
- const data = await this.http.request(
532
- "POST",
533
- "/api/v1/auth/signup",
534
- input,
535
- { skipAutoRefresh: true }
536
- );
537
- return parseLoginResponse(data, this.http.isBrowserSession());
538
- }
539
- async completeMfa(mfaChallengeToken, code, method) {
540
- const data = await this.http.request(
541
- "POST",
542
- "/api/v1/mfa/verify",
543
- { mfaChallengeToken, code, method },
544
- { skipAutoRefresh: true }
545
- );
546
- return parseMfaResponse(data, this.http.isBrowserSession());
547
- }
548
- async completeMfaWithBackup(mfaChallengeToken, backupCode) {
549
- const data = await this.http.request(
550
- "POST",
551
- "/api/v1/mfa/verify-backup",
552
- { mfaChallengeToken, backupCode },
553
- { skipAutoRefresh: true }
554
- );
555
- return parseMfaResponse(data, this.http.isBrowserSession());
556
- }
557
- async sendMfaChallenge(mfaChallengeToken, method) {
558
- return this.http.request("POST", "/api/v1/mfa/challenge", {
559
- mfaChallengeToken,
560
- method
561
- }, { skipAutoRefresh: true });
562
- }
563
- async selectTenant(tenantSelectionToken, tenantId) {
564
- const data = await this.http.request(
565
- "POST",
566
- "/api/v1/auth/select-tenant",
567
- {
568
- tenantSelectionToken,
569
- tenantId
570
- },
571
- { skipAutoRefresh: true }
572
- );
573
- return parseLoginResponse(data, this.http.isBrowserSession());
574
- }
575
- async logout() {
576
- return this.http.request("POST", "/api/v1/auth/logout");
577
- }
578
- async logoutAll() {
579
- return this.http.request("POST", "/api/v1/auth/logout-all");
580
- }
581
- async refreshTokens(refreshToken) {
582
- if (this.http.isBrowserSession()) {
583
- throw new Error("refreshTokens(refreshToken) is not used in browser_session mode; the backend session should own refresh.");
584
- }
585
- const data = await this.http.request(
586
- "POST",
587
- "/api/v1/auth/refresh",
588
- { refreshToken },
589
- { skipAutoRefresh: true }
590
- );
591
- return { accessToken: data.accessToken, refreshToken: data.refreshToken };
592
- }
593
- async forgotPassword(email) {
594
- return this.http.request("POST", "/api/v1/auth/password/reset/request", { email }, { skipAutoRefresh: true });
595
- }
596
- async resetPassword(token, newPassword) {
597
- return this.http.request("POST", "/api/v1/auth/password/reset/confirm", { token, newPassword }, { skipAutoRefresh: true });
598
- }
599
- async changePassword(currentPassword, newPassword) {
600
- return this.http.request("POST", "/api/v1/auth/password/change", {
601
- currentPassword,
602
- newPassword
603
- });
604
- }
605
- async verifyToken() {
606
- return this.http.request("GET", "/api/v1/auth/verify");
607
- }
608
- async exchangeOAuthCode(code) {
609
- const data = await this.http.request(
610
- "POST",
611
- "/api/v1/auth/oauth/exchange",
612
- { code },
613
- { skipAutoRefresh: true }
614
- );
615
- return parseLoginResponse(data, this.http.isBrowserSession());
616
- }
617
- async getSessionUser() {
618
- return this.http.request("GET", "/api/v1/auth/me");
619
- }
620
- };
621
- function parseMfaResponse(data, browserSessionMode) {
622
- if (data.accessToken && data.refreshToken && data.user) {
623
- return {
624
- authMode: "token",
625
- tokens: { accessToken: data.accessToken, refreshToken: data.refreshToken },
626
- user: data.user,
627
- ...data.remainingBackupCodes !== void 0 ? { remainingBackupCodes: data.remainingBackupCodes } : {},
628
- ...data.warning ? { warning: data.warning } : {}
629
- };
630
- }
631
- if (browserSessionMode && data.user) {
632
- return {
633
- authMode: "session",
634
- user: data.user,
635
- ...data.remainingBackupCodes !== void 0 ? { remainingBackupCodes: data.remainingBackupCodes } : {},
636
- ...data.warning ? { warning: data.warning } : {}
637
- };
638
- }
639
- throw new Error("Unexpected MFA response shape");
640
- }
641
-
642
316
  // src/modules/tokens.ts
643
- var import_crypto = __toESM(require("crypto"));
644
- var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
317
+ var import_jose = require("jose");
645
318
  var JWKS_CACHE_TTL_MS = 60 * 60 * 1e3;
646
319
  var DEFAULT_TOKEN_ISSUER = [
647
320
  "https://auth.dispositioniq.com",
@@ -654,6 +327,24 @@ var DEFAULT_TOKEN_AUDIENCE = [
654
327
  "iqvalidate"
655
328
  ];
656
329
  var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
330
+ function decodeProtectedHeader(token) {
331
+ const parts = token.split(".");
332
+ if (parts.length < 2) return null;
333
+ try {
334
+ const padded = parts[0] + "=".repeat((4 - parts[0].length % 4) % 4);
335
+ const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
336
+ let json;
337
+ if (typeof atob === "function") {
338
+ json = atob(b64);
339
+ } else {
340
+ const { Buffer: Buffer2 } = require("buffer");
341
+ json = Buffer2.from(b64, "base64").toString("utf8");
342
+ }
343
+ return JSON.parse(json);
344
+ } catch {
345
+ return null;
346
+ }
347
+ }
657
348
  var TokensModule = class {
658
349
  constructor(baseUrl, options = {}) {
659
350
  this.jwksCache = null;
@@ -664,49 +355,49 @@ var TokensModule = class {
664
355
  this.defaultClockTolerance = options.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
665
356
  }
666
357
  /**
667
- * Verify a JWT access token using RS256 via JWKS from /.well-known/jwks.json.
668
- * Caches JWKS keys for 1 hour. Retries once on unknown `kid`.
669
- *
670
- * @remarks Validates against /.well-known/jwks.json. Issuer, audience, and
671
- * clock tolerance default to client config but can be overridden per call.
358
+ * Verify a JWT access token using RS256/ES256 via JWKS from
359
+ * `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
360
+ * Node, browser, and edge runtimes alike — no `node:crypto` dependency.
361
+ * Caches JWKS for 1 hour and refetches once on unknown `kid`.
672
362
  */
673
363
  async verify(token, options = {}) {
674
- const decoded = import_jsonwebtoken.default.decode(token, { complete: true });
675
- if (!decoded || typeof decoded === "string") {
364
+ const header = decodeProtectedHeader(token);
365
+ if (!header) {
676
366
  throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
677
367
  }
678
- const kid = decoded.header.kid;
368
+ const kid = header.kid;
679
369
  if (!kid) {
680
370
  throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
681
371
  }
682
- let publicKey = await this.getPublicKey(kid);
683
- if (!publicKey) {
684
- await this.refreshJwks();
685
- publicKey = await this.getPublicKey(kid);
372
+ let cache = await this.ensureCache();
373
+ if (!cache.byKid.has(kid)) {
374
+ this.jwksCache = null;
375
+ cache = await this.ensureCache();
686
376
  }
687
- if (!publicKey) {
377
+ if (!cache.byKid.has(kid)) {
688
378
  throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
689
379
  }
690
380
  const issuer = options.issuer ?? this.defaultIssuer;
691
381
  const audience = options.audience ?? this.defaultAudience;
692
382
  const clockTolerance = options.clockTolerance ?? this.defaultClockTolerance;
693
- const algorithms = options.algorithms ?? ["RS256"];
383
+ const algorithms = options.algorithms ?? ["RS256", "ES256"];
384
+ const verifyOptions = {
385
+ algorithms,
386
+ clockTolerance,
387
+ issuer,
388
+ audience
389
+ };
694
390
  try {
695
- const verifyOptions = {
696
- algorithms,
697
- clockTolerance,
698
- // The jsonwebtoken types insist on tuple types for arrays; runtime
699
- // accepts plain string[] so we cast to satisfy the compiler.
700
- issuer,
701
- audience
702
- };
703
- const verified = import_jsonwebtoken.default.verify(token, publicKey, verifyOptions);
704
- return verified;
391
+ const { payload } = await (0, import_jose.jwtVerify)(token, cache.verifier, verifyOptions);
392
+ return payload;
705
393
  } catch (err) {
394
+ if (err instanceof import_jose.errors.JWTExpired) {
395
+ throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
396
+ }
397
+ if (err instanceof import_jose.errors.JOSEError) {
398
+ throw new IQAuthError("TOKEN_INVALID", err.message);
399
+ }
706
400
  if (err instanceof Error) {
707
- if (err.name === "TokenExpiredError") {
708
- throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
709
- }
710
401
  throw new IQAuthError("TOKEN_INVALID", err.message);
711
402
  }
712
403
  throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
@@ -714,29 +405,40 @@ var TokensModule = class {
714
405
  }
715
406
  /**
716
407
  * Decode a JWT without verification. Returns null if malformed.
717
- *
718
- * @remarks Local decode only — no network call
719
408
  */
720
409
  decode(token) {
721
- const decoded = import_jsonwebtoken.default.decode(token);
722
- return decoded;
410
+ try {
411
+ const parts = token.split(".");
412
+ if (parts.length < 2) return null;
413
+ const payload = parts[1];
414
+ const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
415
+ const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
416
+ let json;
417
+ if (typeof atob === "function") {
418
+ json = atob(b64);
419
+ } else {
420
+ const { Buffer: Buffer2 } = require("buffer");
421
+ json = Buffer2.from(b64, "base64").toString("utf8");
422
+ }
423
+ try {
424
+ json = decodeURIComponent(escape(json));
425
+ } catch {
426
+ }
427
+ const claims = JSON.parse(json);
428
+ if (!claims || typeof claims !== "object") return null;
429
+ return claims;
430
+ } catch {
431
+ return null;
432
+ }
723
433
  }
724
- /**
725
- * Check if a token is expired based on the `exp` claim.
726
- *
727
- * @remarks Local check only — no network call
728
- */
434
+ /** Check if a token is expired based on the `exp` claim. */
729
435
  isExpired(token) {
730
436
  const claims = this.decode(token);
731
437
  if (!claims?.exp) return true;
732
438
  const now = Math.floor(Date.now() / 1e3);
733
439
  return claims.exp <= now;
734
440
  }
735
- /**
736
- * Get the claims from a token without verification.
737
- *
738
- * @remarks Local decode only — no network call
739
- */
441
+ /** Get the claims from a token without verification. */
740
442
  getClaims(token) {
741
443
  const claims = this.decode(token);
742
444
  if (!claims) {
@@ -744,11 +446,15 @@ var TokensModule = class {
744
446
  }
745
447
  return claims;
746
448
  }
747
- async getPublicKey(kid) {
748
- if (!this.jwksCache || Date.now() - this.jwksCache.fetchedAt > JWKS_CACHE_TTL_MS) {
749
- await this.refreshJwks();
449
+ async ensureCache() {
450
+ if (this.jwksCache && Date.now() - this.jwksCache.fetchedAt <= JWKS_CACHE_TTL_MS) {
451
+ return this.jwksCache;
750
452
  }
751
- return this.jwksCache?.keys.get(kid) ?? null;
453
+ await this.refreshJwks();
454
+ if (!this.jwksCache) {
455
+ throw new IQAuthError("INTERNAL_ERROR", "JWKS cache unavailable after refresh");
456
+ }
457
+ return this.jwksCache;
752
458
  }
753
459
  async refreshJwks() {
754
460
  if (this.inFlightRefresh) {
@@ -775,1218 +481,30 @@ var TokensModule = class {
775
481
  "Malformed JWKS response: expected { keys: [...] }"
776
482
  );
777
483
  }
778
- const keys = /* @__PURE__ */ new Map();
484
+ const byKid = /* @__PURE__ */ new Set();
779
485
  for (const key of jwks.keys) {
780
- if (!key || typeof key.kid !== "string" || typeof key.n !== "string" || typeof key.e !== "string") {
486
+ 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")) {
781
487
  throw new IQAuthError(
782
488
  "INTERNAL_ERROR",
783
489
  "Malformed JWKS response: key missing required fields"
784
490
  );
785
491
  }
786
- const pem = this.jwkToPem(key);
787
- keys.set(key.kid, pem);
492
+ byKid.add(key.kid);
788
493
  }
789
- this.jwksCache = { keys, fetchedAt: Date.now() };
494
+ const verifier = (0, import_jose.createLocalJWKSet)({ keys: jwks.keys });
495
+ this.jwksCache = { raw: jwks.keys, byKid, verifier, fetchedAt: Date.now() };
790
496
  } finally {
791
497
  this.inFlightRefresh = null;
792
498
  }
793
499
  })();
794
500
  return this.inFlightRefresh;
795
501
  }
796
- jwkToPem(jwk) {
797
- const keyObject = import_crypto.default.createPublicKey({
798
- key: {
799
- kty: jwk.kty,
800
- n: jwk.n,
801
- e: jwk.e
802
- },
803
- format: "jwk"
804
- });
805
- return keyObject.export({ type: "spki", format: "pem" });
806
- }
807
502
  /** @internal Exposed for testing — clears JWKS cache */
808
503
  clearCache() {
809
504
  this.jwksCache = null;
810
505
  }
811
506
  };
812
507
 
813
- // src/modules/sessions.ts
814
- var SessionsModule = class {
815
- constructor(http) {
816
- this.http = http;
817
- }
818
- /**
819
- * List all active sessions for the current user.
820
- *
821
- * @remarks Wraps GET /api/v1/sessions
822
- */
823
- async list() {
824
- return this.http.request("GET", "/api/v1/sessions");
825
- }
826
- /**
827
- * Revoke (terminate) a specific session by ID.
828
- *
829
- * @remarks Wraps DELETE /api/v1/sessions/:sessionId
830
- */
831
- async revoke(sessionId) {
832
- return this.http.request("DELETE", `/api/v1/sessions/${sessionId}`);
833
- }
834
- /**
835
- * Revoke all sessions for the current user.
836
- *
837
- * @remarks Wraps POST /api/v1/auth/logout-all
838
- */
839
- async revokeAll() {
840
- return this.http.request("POST", "/api/v1/auth/logout-all");
841
- }
842
- };
843
-
844
- // src/modules/users.ts
845
- var UsersModule = class {
846
- constructor(http) {
847
- this.http = http;
848
- }
849
- /**
850
- * Get the currently authenticated user's profile.
851
- *
852
- * @remarks Wraps GET /api/v1/users/me
853
- */
854
- async getCurrent() {
855
- return this.http.request("GET", "/api/v1/users/me");
856
- }
857
- /**
858
- * Get a user by ID.
859
- *
860
- * @remarks Wraps GET /api/v1/users/:id
861
- */
862
- async getById(userId) {
863
- return this.http.request("GET", `/api/v1/users/${userId}`);
864
- }
865
- /**
866
- * List users in the current tenant. Requires tenant_admin role.
867
- *
868
- * @remarks Wraps GET /api/v1/users
869
- */
870
- async list(params) {
871
- const query = new URLSearchParams();
872
- if (params?.email) query.set("email", params.email);
873
- if (params?.tenantId) query.set("tenantId", params.tenantId);
874
- const qs = query.toString();
875
- return this.http.request("GET", `/api/v1/users${qs ? `?${qs}` : ""}`);
876
- }
877
- /**
878
- * Provision (create) a new user in a tenant. Requires platform_admin or tenant-scoped API key with admin role.
879
- *
880
- * @remarks Wraps POST /api/v1/tenants/:tenantId/users/provision
881
- */
882
- async create(tenantId, data) {
883
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/provision`, data);
884
- }
885
- /**
886
- * Update the current user's profile (name, picture).
887
- *
888
- * @remarks Wraps PATCH /api/v1/users/me
889
- */
890
- async update(data) {
891
- return this.http.request("PATCH", "/api/v1/users/me", data);
892
- }
893
- /**
894
- * Deactivate a user in the current tenant. Requires tenant_admin role.
895
- *
896
- * @remarks Wraps PATCH /api/v1/users/:id/deactivate
897
- */
898
- async deactivate(userId) {
899
- return this.http.request("PATCH", `/api/v1/users/${userId}/deactivate`);
900
- }
901
- /**
902
- * Reactivate a user in the current tenant. Requires tenant_admin role.
903
- *
904
- * @remarks Wraps PATCH /api/v1/users/:id/reactivate
905
- */
906
- async reactivate(userId) {
907
- return this.http.request("PATCH", `/api/v1/users/${userId}/reactivate`);
908
- }
909
- /**
910
- * Unlock a locked user account in the current tenant. Requires tenant_admin role.
911
- *
912
- * @remarks Wraps PATCH /api/v1/users/:id/unlock
913
- */
914
- async unlock(userId) {
915
- return this.http.request("PATCH", `/api/v1/users/${userId}/unlock`);
916
- }
917
- /**
918
- * Get effective permissions for a user for a specific product.
919
- *
920
- * @remarks Wraps GET /api/v1/users/:id/permissions?product=...
921
- */
922
- async getPermissions(userId, product) {
923
- return this.http.request("GET", `/api/v1/users/${userId}/permissions?product=${encodeURIComponent(product)}`);
924
- }
925
- /**
926
- * Change the current user's password.
927
- *
928
- * @remarks Wraps POST /api/v1/auth/password/change
929
- */
930
- async updatePassword(currentPassword, newPassword) {
931
- return this.http.request("POST", "/api/v1/auth/password/change", {
932
- currentPassword,
933
- newPassword
934
- });
935
- }
936
- };
937
-
938
- // src/modules/permissions.ts
939
- var PermissionsModule = class {
940
- constructor(claimsProvider) {
941
- this.getClaims = claimsProvider;
942
- }
943
- /**
944
- * Get the roles from the current JWT claims.
945
- *
946
- * @remarks Extracted from JWT claim `roles` (string[])
947
- */
948
- getRoles() {
949
- return this.getClaims()?.roles ?? [];
950
- }
951
- /**
952
- * Get the entitlements from the current JWT claims.
953
- *
954
- * @remarks Extracted from JWT claim `entitlements` (string[])
955
- */
956
- getEntitlements() {
957
- return this.getClaims()?.entitlements ?? [];
958
- }
959
- /**
960
- * Check if the current user has a specific role.
961
- *
962
- * @remarks Checks against JWT claim `roles`
963
- */
964
- hasRole(role) {
965
- return this.getRoles().includes(role);
966
- }
967
- /**
968
- * Check if the current user has a specific entitlement.
969
- *
970
- * @remarks Checks against JWT claim `entitlements`
971
- */
972
- hasEntitlement(entitlement) {
973
- return this.getEntitlements().includes(entitlement);
974
- }
975
- /**
976
- * Check if the current user has all of the specified roles.
977
- *
978
- * @remarks Checks against JWT claim `roles`
979
- */
980
- hasAllRoles(roles) {
981
- const userRoles = this.getRoles();
982
- return roles.every((r) => userRoles.includes(r));
983
- }
984
- /**
985
- * Check if the current user has any of the specified roles.
986
- *
987
- * @remarks Checks against JWT claim `roles`
988
- */
989
- hasAnyRole(roles) {
990
- const userRoles = this.getRoles();
991
- return roles.some((r) => userRoles.includes(r));
992
- }
993
- /**
994
- * Check if the current user has all of the specified entitlements.
995
- *
996
- * @remarks Checks against JWT claim `entitlements`
997
- */
998
- hasAllEntitlements(entitlements) {
999
- const userEntitlements = this.getEntitlements();
1000
- return entitlements.every((e) => userEntitlements.includes(e));
1001
- }
1002
- /**
1003
- * Check if the current user has any of the specified entitlements.
1004
- *
1005
- * @remarks Checks against JWT claim `entitlements`
1006
- */
1007
- hasAnyEntitlement(entitlements) {
1008
- const userEntitlements = this.getEntitlements();
1009
- return entitlements.some((e) => userEntitlements.includes(e));
1010
- }
1011
- };
1012
-
1013
- // src/modules/oidc.ts
1014
- var import_crypto2 = __toESM(require("crypto"));
1015
- var InMemoryOidcStateStore = class {
1016
- constructor() {
1017
- this.map = /* @__PURE__ */ new Map();
1018
- }
1019
- set(state, value) {
1020
- this.map.set(state, value);
1021
- }
1022
- get(state) {
1023
- const entry = this.map.get(state);
1024
- if (!entry) return null;
1025
- if (entry.expiresAt < Date.now()) {
1026
- this.map.delete(state);
1027
- return null;
1028
- }
1029
- return entry;
1030
- }
1031
- delete(state) {
1032
- this.map.delete(state);
1033
- }
1034
- };
1035
- var DEFAULT_REQUEST_TTL_MS = 10 * 60 * 1e3;
1036
- function base64UrlEncode(buf) {
1037
- return buf.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
1038
- }
1039
- var OidcModule = class {
1040
- constructor(http, baseUrl, options = {}) {
1041
- this.http = http;
1042
- this.baseUrl = baseUrl;
1043
- this.stateStore = options.stateStore ?? new InMemoryOidcStateStore();
1044
- this.requestTtlMs = options.requestTtlMs ?? DEFAULT_REQUEST_TTL_MS;
1045
- this.tokensModule = options.tokens;
1046
- }
1047
- /** @internal Allow the client to inject its TokensModule after construction. */
1048
- _setTokensModule(tokens) {
1049
- if (!this.tokensModule) this.tokensModule = tokens;
1050
- }
1051
- /**
1052
- * Fetch the OpenID Connect discovery document.
1053
- *
1054
- * @remarks Wraps GET /.well-known/openid-configuration
1055
- */
1056
- async getDiscovery() {
1057
- const res = await fetch(`${this.baseUrl}/.well-known/openid-configuration`);
1058
- if (!res.ok) throw new Error(`Failed to fetch OIDC discovery: ${res.status}`);
1059
- return res.json();
1060
- }
1061
- /**
1062
- * Fetch the JSON Web Key Set.
1063
- *
1064
- * @remarks Wraps GET /.well-known/jwks.json
1065
- */
1066
- async getJwks() {
1067
- const res = await fetch(`${this.baseUrl}/.well-known/jwks.json`);
1068
- if (!res.ok) throw new Error(`Failed to fetch JWKS: ${res.status}`);
1069
- return res.json();
1070
- }
1071
- /**
1072
- * Build an OIDC authorization URL for redirect-based login.
1073
- *
1074
- * @remarks Constructs URL pointing to /oidc/authorize. Prefer
1075
- * {@link createAuthRequest} which also generates and stores PKCE/state/nonce.
1076
- */
1077
- buildAuthorizationUrl(params) {
1078
- const url = new URL("/oidc/authorize", this.baseUrl);
1079
- url.searchParams.set("response_type", "code");
1080
- url.searchParams.set("client_id", params.clientId);
1081
- url.searchParams.set("redirect_uri", params.redirectUri);
1082
- url.searchParams.set("scope", params.scope || "openid");
1083
- url.searchParams.set("state", params.state);
1084
- if (params.nonce) url.searchParams.set("nonce", params.nonce);
1085
- if (params.codeChallenge) url.searchParams.set("code_challenge", params.codeChallenge);
1086
- if (params.codeChallengeMethod) url.searchParams.set("code_challenge_method", params.codeChallengeMethod);
1087
- return url.toString();
1088
- }
1089
- /**
1090
- * Generate `code_verifier`, `code_challenge`, `state`, and `nonce`, persist
1091
- * them via the configured storage adapter, and return an authorization URL
1092
- * ready to redirect the user to.
1093
- */
1094
- async createAuthRequest(params) {
1095
- const codeVerifier = base64UrlEncode(import_crypto2.default.randomBytes(32));
1096
- const codeChallenge = base64UrlEncode(
1097
- import_crypto2.default.createHash("sha256").update(codeVerifier).digest()
1098
- );
1099
- const state = base64UrlEncode(import_crypto2.default.randomBytes(16));
1100
- const nonce = base64UrlEncode(import_crypto2.default.randomBytes(16));
1101
- await this.stateStore.set(state, {
1102
- codeVerifier,
1103
- state,
1104
- nonce,
1105
- redirectUri: params.redirectUri,
1106
- clientId: params.clientId,
1107
- expiresAt: Date.now() + this.requestTtlMs
1108
- });
1109
- const authorizationUrl = this.buildAuthorizationUrl({
1110
- clientId: params.clientId,
1111
- redirectUri: params.redirectUri,
1112
- scope: params.scope,
1113
- state,
1114
- nonce,
1115
- codeChallenge,
1116
- codeChallengeMethod: "S256"
1117
- });
1118
- return {
1119
- authorizationUrl,
1120
- state,
1121
- nonce,
1122
- codeVerifier,
1123
- codeChallenge,
1124
- codeChallengeMethod: "S256"
1125
- };
1126
- }
1127
- /**
1128
- * Validate the callback `state`, exchange the code with the bound PKCE
1129
- * verifier, and verify that the returned `id_token` (if any) carries the
1130
- * stored `nonce` and `aud === clientId`.
1131
- */
1132
- async handleCallback(params) {
1133
- if (!params.state) {
1134
- throw new IQAuthError("VALIDATION_ERROR", "OIDC callback missing state parameter");
1135
- }
1136
- if (!params.code) {
1137
- throw new IQAuthError("VALIDATION_ERROR", "OIDC callback missing code parameter");
1138
- }
1139
- const stored = await this.stateStore.get(params.state);
1140
- if (!stored) {
1141
- throw new IQAuthError("VALIDATION_ERROR", "Unknown or expired OIDC state");
1142
- }
1143
- let tokens;
1144
- try {
1145
- tokens = await this.exchangeCode({
1146
- code: params.code,
1147
- redirectUri: stored.redirectUri,
1148
- clientId: stored.clientId,
1149
- clientSecret: params.clientSecret,
1150
- codeVerifier: stored.codeVerifier
1151
- });
1152
- } finally {
1153
- await this.stateStore.delete(params.state);
1154
- }
1155
- let idTokenClaims = null;
1156
- if (tokens.id_token) {
1157
- if (!this.tokensModule) {
1158
- throw new IQAuthError(
1159
- "INTERNAL_ERROR",
1160
- "OIDC handleCallback received an id_token but no TokensModule is configured for verification"
1161
- );
1162
- }
1163
- idTokenClaims = await this.tokensModule.verify(tokens.id_token, {
1164
- audience: stored.clientId
1165
- });
1166
- const claimsBag = idTokenClaims;
1167
- const tokenNonce = typeof claimsBag.nonce === "string" ? claimsBag.nonce : void 0;
1168
- if (!tokenNonce || tokenNonce !== stored.nonce) {
1169
- throw new IQAuthError(
1170
- "TOKEN_INVALID",
1171
- "OIDC id_token nonce did not match the stored value"
1172
- );
1173
- }
1174
- }
1175
- return { tokens, idTokenClaims };
1176
- }
1177
- /**
1178
- * Exchange an authorization code for tokens at the OIDC token endpoint.
1179
- *
1180
- * @remarks Wraps POST /oidc/token (or /api/v1/oidc/token)
1181
- */
1182
- async exchangeCode(params) {
1183
- const body = {
1184
- grant_type: "authorization_code",
1185
- code: params.code,
1186
- redirect_uri: params.redirectUri,
1187
- client_id: params.clientId
1188
- };
1189
- if (params.clientSecret) body.client_secret = params.clientSecret;
1190
- if (params.codeVerifier) body.code_verifier = params.codeVerifier;
1191
- const res = await fetch(`${this.baseUrl}/oidc/token`, {
1192
- method: "POST",
1193
- headers: { "Content-Type": "application/json" },
1194
- body: JSON.stringify(body)
1195
- });
1196
- if (!res.ok) {
1197
- const err = await res.json().catch(() => ({}));
1198
- throw new Error(
1199
- `OIDC token exchange failed: ${err.error_description || err.error || res.status}`
1200
- );
1201
- }
1202
- return res.json();
1203
- }
1204
- /**
1205
- * Get user info from the OIDC userinfo endpoint.
1206
- *
1207
- * @remarks Wraps GET /oidc/userinfo (requires Bearer token)
1208
- */
1209
- async getUserInfo() {
1210
- if (this.http.isBrowserSession()) {
1211
- throw new Error("oidc.getUserInfo() requires a bearer token and is not supported in browser_session mode.");
1212
- }
1213
- return this.http.requestRaw("GET", "/oidc/userinfo");
1214
- }
1215
- async getClientContext(clientId) {
1216
- const res = await fetch(`${this.baseUrl}/oidc/client-context?client_id=${encodeURIComponent(clientId)}`);
1217
- if (!res.ok) throw new Error(`Failed to fetch OIDC client context: ${res.status}`);
1218
- const payload = await res.json();
1219
- if (!payload.success || !payload.data) {
1220
- throw new Error("OIDC client context response was invalid");
1221
- }
1222
- return payload.data;
1223
- }
1224
- };
1225
-
1226
- // src/modules/tenants.ts
1227
- function normalizeBrandingConfig(config) {
1228
- return {
1229
- ...config,
1230
- brandName: config.brandName ?? config.companyName,
1231
- loginHeadline: config.loginHeadline ?? config.headline ?? null,
1232
- loginSubheadline: config.loginSubheadline ?? config.subheadline ?? null,
1233
- companyName: config.companyName ?? config.brandName,
1234
- headline: config.headline ?? config.loginHeadline ?? null,
1235
- subheadline: config.subheadline ?? config.loginSubheadline ?? null
1236
- };
1237
- }
1238
- var TenantsModule = class {
1239
- constructor(http) {
1240
- this.http = http;
1241
- }
1242
- async getCurrent(tenantId) {
1243
- return this.http.request("GET", `/api/v1/tenants/${tenantId}`);
1244
- }
1245
- async get(tenantId) {
1246
- return this.http.request("GET", `/api/v1/tenants/${tenantId}`);
1247
- }
1248
- async list(params) {
1249
- const query = new URLSearchParams();
1250
- if (params?.vendorId) query.set("vendorId", params.vendorId);
1251
- const qs = query.toString();
1252
- return this.http.request("GET", `/api/v1/tenants${qs ? `?${qs}` : ""}`);
1253
- }
1254
- async create(data) {
1255
- return this.http.request("POST", "/api/v1/tenants", data);
1256
- }
1257
- async update(tenantId, data) {
1258
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}`, data);
1259
- }
1260
- async delete(tenantId) {
1261
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}`);
1262
- }
1263
- async promoteToVendor(tenantId, data) {
1264
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/promote-to-vendor`, data);
1265
- }
1266
- async getUsers(tenantId) {
1267
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/users`);
1268
- }
1269
- async inviteUser(tenantId, data) {
1270
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/invite`, data);
1271
- }
1272
- async changeUserRole(tenantId, userId, role) {
1273
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}/users/${userId}/role`, { role });
1274
- }
1275
- async migrateUser(tenantId, userId, data) {
1276
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/${userId}/migrate`, data);
1277
- }
1278
- async removeUser(tenantId, userId) {
1279
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/users/${userId}`);
1280
- }
1281
- async getPasswordPolicy(tenantId) {
1282
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/password-policy`);
1283
- }
1284
- async updatePasswordPolicy(tenantId, data) {
1285
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}/password-policy`, data);
1286
- }
1287
- async getMfaPolicies(tenantId) {
1288
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/mfa-policies`);
1289
- }
1290
- async updateMfaPolicy(tenantId, role, data) {
1291
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}/mfa-policies/${role}`, data);
1292
- }
1293
- async getPublicBranding(params) {
1294
- const query = new URLSearchParams();
1295
- if (params?.vendor) query.set("vendor", params.vendor);
1296
- const qs = query.toString();
1297
- const url = `${this.http.baseUrl}/api/public/branding${qs ? `?${qs}` : ""}`;
1298
- const res = await fetch(url);
1299
- if (!res.ok) return null;
1300
- const body = await res.json();
1301
- if (body.success && body.data) return normalizeBrandingConfig(body.data);
1302
- return null;
1303
- }
1304
- async getPublicBrandingBySlug(vendorSlug) {
1305
- const url = `${this.http.baseUrl}/api/public/branding/by-slug/${encodeURIComponent(vendorSlug)}`;
1306
- const res = await fetch(url);
1307
- if (!res.ok) return null;
1308
- const body = await res.json();
1309
- if (body.success && body.data) return normalizeBrandingConfig(body.data);
1310
- return null;
1311
- }
1312
- };
1313
-
1314
- // src/modules/apps.ts
1315
- var AppsModule = class {
1316
- constructor(http) {
1317
- this.http = http;
1318
- }
1319
- /**
1320
- * Self-service Phase A: provision an app + OIDC client + key pair in one shot.
1321
- * Caller must be authenticated as a tenant_admin (or platform_admin).
1322
- *
1323
- * @remarks Wraps POST /api/v1/apps
1324
- */
1325
- async create(data) {
1326
- return this.http.request("POST", "/api/v1/apps", data);
1327
- }
1328
- async update(id, data) {
1329
- return this.http.request("PATCH", `/api/v1/apps/${encodeURIComponent(id)}`, data);
1330
- }
1331
- async remove(id) {
1332
- return this.http.request("DELETE", `/api/v1/apps/${encodeURIComponent(id)}`);
1333
- }
1334
- async listOrigins(id) {
1335
- return this.http.request("GET", `/api/v1/apps/${encodeURIComponent(id)}/origins`);
1336
- }
1337
- async addOrigin(id, origin) {
1338
- return this.http.request("POST", `/api/v1/apps/${encodeURIComponent(id)}/origins`, { origin });
1339
- }
1340
- async removeOrigin(id, origin) {
1341
- return this.http.request("DELETE", `/api/v1/apps/${encodeURIComponent(id)}/origins/${encodeURIComponent(origin)}`);
1342
- }
1343
- async listKeys(id) {
1344
- return this.http.request("GET", `/api/v1/apps/${encodeURIComponent(id)}/keys`);
1345
- }
1346
- async createKey(id, data) {
1347
- return this.http.request("POST", `/api/v1/apps/${encodeURIComponent(id)}/keys`, data);
1348
- }
1349
- async rotateKey(id, keyId) {
1350
- return this.http.request("POST", `/api/v1/apps/${encodeURIComponent(id)}/keys/${encodeURIComponent(keyId)}/rotate`, {});
1351
- }
1352
- async revokeKey(id, keyId) {
1353
- return this.http.request("DELETE", `/api/v1/apps/${encodeURIComponent(id)}/keys/${encodeURIComponent(keyId)}`);
1354
- }
1355
- /**
1356
- * List all registered applications.
1357
- * Requires `apps.view` permission on the `iqauth-admin` app.
1358
- *
1359
- * @remarks Wraps GET /api/v1/apps
1360
- */
1361
- async list() {
1362
- return this.http.request("GET", "/api/v1/apps");
1363
- }
1364
- /**
1365
- * Get a registered application by its unique key, including its permission nodes.
1366
- * Requires `apps.view` permission on the `iqauth-admin` app.
1367
- *
1368
- * @remarks Wraps GET /api/v1/apps/:appKey
1369
- */
1370
- async get(appKey) {
1371
- return this.http.request("GET", `/api/v1/apps/${encodeURIComponent(appKey)}`);
1372
- }
1373
- /**
1374
- * Register or sync an application manifest. This is an idempotent upsert —
1375
- * it creates the app if it doesn't exist, or updates it if it does.
1376
- * Permission nodes are also upserted (created or updated) from the manifest tree.
1377
- *
1378
- * Requires `platform_admin` role and `apps.manage` permission.
1379
- *
1380
- * @remarks Wraps POST /api/v1/apps/sync
1381
- */
1382
- async register(manifest) {
1383
- if (!this.http.hasCredentials()) {
1384
- throw new IQAuthError(
1385
- "AUTH_REQUIRED",
1386
- "Cannot sync app manifest: no API key or access token configured. Initialize the client with an apiKey or accessToken.",
1387
- 401
1388
- );
1389
- }
1390
- return this.http.request("POST", "/api/v1/apps/sync", manifest);
1391
- }
1392
- /**
1393
- * Check if an application is registered by its key.
1394
- * Returns `true` if the app exists, `false` otherwise.
1395
- *
1396
- * @remarks Uses GET /api/v1/apps/:appKey — catches 404 errors
1397
- */
1398
- async isRegistered(appKey) {
1399
- try {
1400
- await this.get(appKey);
1401
- return true;
1402
- } catch (err) {
1403
- if (err.code === "NOT_FOUND" || err.status === 404) {
1404
- return false;
1405
- }
1406
- throw err;
1407
- }
1408
- }
1409
- };
1410
-
1411
- // src/modules/roles.ts
1412
- var RolesModule = class {
1413
- constructor(http) {
1414
- this.http = http;
1415
- }
1416
- async list(tenantId) {
1417
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/roles`);
1418
- }
1419
- async create(tenantId, data) {
1420
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/roles`, data);
1421
- }
1422
- async update(tenantId, roleId, data) {
1423
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}/roles/${roleId}`, data);
1424
- }
1425
- async delete(tenantId, roleId) {
1426
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/roles/${roleId}`);
1427
- }
1428
- async getUserRoles(tenantId, userId) {
1429
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/users/${userId}/roles`);
1430
- }
1431
- async assignRole(tenantId, userId, data) {
1432
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/${userId}/roles`, data);
1433
- }
1434
- async removeRole(tenantId, userId, roleId) {
1435
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/users/${userId}/roles/${roleId}`);
1436
- }
1437
- };
1438
-
1439
- // src/modules/permissionGroups.ts
1440
- var PermissionGroupsModule = class {
1441
- constructor(http) {
1442
- this.http = http;
1443
- }
1444
- async list(tenantId) {
1445
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/permission-groups`);
1446
- }
1447
- async create(tenantId, name, description) {
1448
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/permission-groups`, { name, description });
1449
- }
1450
- async update(tenantId, groupId, data) {
1451
- return this.http.request("PATCH", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}`, data);
1452
- }
1453
- async delete(tenantId, groupId) {
1454
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}`);
1455
- }
1456
- async getPermissions(tenantId, groupId) {
1457
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}/permissions`);
1458
- }
1459
- async addPermission(tenantId, groupId, data) {
1460
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}/permissions`, data);
1461
- }
1462
- async removePermission(tenantId, groupId, permissionId) {
1463
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}/permissions/${permissionId}`);
1464
- }
1465
- async addInheritance(tenantId, groupId, inheritsFromGroupId) {
1466
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}/inherit`, { inheritsFromGroupId });
1467
- }
1468
- async removeInheritance(tenantId, groupId, inheritedGroupId) {
1469
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/permission-groups/${groupId}/inherit/${inheritedGroupId}`);
1470
- }
1471
- async getUserGroups(tenantId, userId) {
1472
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/users/${userId}/groups`);
1473
- }
1474
- async assignUserToGroup(tenantId, userId, groupId) {
1475
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/${userId}/groups`, { groupId });
1476
- }
1477
- async removeUserFromGroup(tenantId, userId, groupId) {
1478
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/users/${userId}/groups/${groupId}`);
1479
- }
1480
- async getUserOverrides(tenantId, userId) {
1481
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/users/${userId}/permissions/overrides`);
1482
- }
1483
- async addUserOverride(tenantId, userId, data) {
1484
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/${userId}/permissions/overrides`, data);
1485
- }
1486
- async removeUserOverride(tenantId, userId, overrideId) {
1487
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/users/${userId}/permissions/overrides/${overrideId}`);
1488
- }
1489
- async getEffectivePermissions(tenantId, userId, params) {
1490
- const query = new URLSearchParams();
1491
- if (params.product) query.set("product", params.product);
1492
- if (params.appKey) query.set("appKey", params.appKey);
1493
- const qs = query.toString();
1494
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/users/${userId}/permissions/effective${qs ? `?${qs}` : ""}`);
1495
- }
1496
- async checkPermission(tenantId, userId, appKey, nodeKey) {
1497
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/users/${userId}/permissions/check`, { appKey, nodeKey });
1498
- }
1499
- };
1500
-
1501
- // src/modules/apiKeys.ts
1502
- var ApiKeysModule = class {
1503
- constructor(http) {
1504
- this.http = http;
1505
- }
1506
- async create(data) {
1507
- return this.http.request("POST", "/api/v1/api-keys", data);
1508
- }
1509
- async list(params) {
1510
- const query = new URLSearchParams();
1511
- if (params?.tenantId) query.set("tenantId", params.tenantId);
1512
- const qs = query.toString();
1513
- return this.http.request("GET", `/api/v1/api-keys${qs ? `?${qs}` : ""}`);
1514
- }
1515
- async revoke(id) {
1516
- return this.http.request("DELETE", `/api/v1/api-keys/${id}`);
1517
- }
1518
- async introspect(apiKey) {
1519
- return this.http.request("POST", "/api/v1/api-keys/introspect", { apiKey }, { skipAutoRefresh: true });
1520
- }
1521
- };
1522
-
1523
- // src/modules/invites.ts
1524
- var InvitesModule = class {
1525
- constructor(http) {
1526
- this.http = http;
1527
- }
1528
- async create(data) {
1529
- return this.http.request("POST", "/api/v1/invites", data);
1530
- }
1531
- async validate(token) {
1532
- return this.http.request("GET", `/api/v1/invites/${token}/validate`);
1533
- }
1534
- async accept(token, data) {
1535
- return this.http.request("POST", `/api/v1/invites/${token}/accept`, data, { skipAutoRefresh: true });
1536
- }
1537
- };
1538
-
1539
- // src/modules/webhooks.ts
1540
- function normalizeWebhookDelivery(delivery) {
1541
- return {
1542
- ...delivery,
1543
- endpointId: delivery.endpointId ?? delivery.webhookEndpointId,
1544
- webhookEndpointId: delivery.webhookEndpointId ?? delivery.endpointId,
1545
- event: delivery.event ?? delivery.eventType,
1546
- eventType: delivery.eventType ?? delivery.event,
1547
- statusCode: delivery.statusCode ?? delivery.responseStatus ?? null,
1548
- responseStatus: delivery.responseStatus ?? delivery.statusCode ?? null,
1549
- response: delivery.response ?? delivery.responseBody ?? null,
1550
- responseBody: delivery.responseBody ?? delivery.response ?? null,
1551
- deliveredAt: delivery.deliveredAt ?? delivery.createdAt
1552
- };
1553
- }
1554
- var WebhooksModule = class {
1555
- constructor(http) {
1556
- this.http = http;
1557
- }
1558
- async createEndpoint(data) {
1559
- return this.http.request("POST", "/api/v1/webhooks/endpoints", data);
1560
- }
1561
- async listEndpoints() {
1562
- return this.http.request("GET", "/api/v1/webhooks/endpoints");
1563
- }
1564
- async deleteEndpoint(id) {
1565
- return this.http.request("DELETE", `/api/v1/webhooks/endpoints/${id}`);
1566
- }
1567
- async getDeliveries(endpointId) {
1568
- const deliveries = await this.http.request("GET", `/api/v1/webhooks/deliveries?endpointId=${encodeURIComponent(endpointId)}`);
1569
- return deliveries.map(normalizeWebhookDelivery);
1570
- }
1571
- async testEndpoint(id) {
1572
- return this.http.request("POST", `/api/v1/webhooks/endpoints/${id}/test`);
1573
- }
1574
- async rotateSecret(id) {
1575
- return this.http.request("POST", `/api/v1/webhooks/endpoints/${id}/rotate-secret`);
1576
- }
1577
- };
1578
-
1579
- // src/modules/entitlements.ts
1580
- var EntitlementsModule = class {
1581
- constructor(http) {
1582
- this.http = http;
1583
- }
1584
- async list(tenantId) {
1585
- return this.http.request("GET", `/api/v1/tenants/${tenantId}/entitlements`);
1586
- }
1587
- async grant(tenantId, data) {
1588
- return this.http.request("POST", `/api/v1/tenants/${tenantId}/entitlements`, data);
1589
- }
1590
- async revoke(tenantId, product) {
1591
- return this.http.request("DELETE", `/api/v1/tenants/${tenantId}/entitlements/${encodeURIComponent(product)}`);
1592
- }
1593
- };
1594
-
1595
- // src/modules/vendors.ts
1596
- var VendorsModule = class {
1597
- constructor(http) {
1598
- this.http = http;
1599
- }
1600
- async list() {
1601
- return this.http.request("GET", "/api/v1/vendors");
1602
- }
1603
- async get(vendorId) {
1604
- return this.http.request("GET", `/api/v1/vendors/${vendorId}`);
1605
- }
1606
- async create(data) {
1607
- return this.http.request("POST", "/api/v1/vendors", data);
1608
- }
1609
- async update(vendorId, data) {
1610
- return this.http.request("PATCH", `/api/v1/vendors/${vendorId}`, data);
1611
- }
1612
- async delete(vendorId) {
1613
- return this.http.request("DELETE", `/api/v1/vendors/${vendorId}`);
1614
- }
1615
- };
1616
-
1617
- // src/modules/sources.ts
1618
- var SourcesModule = class {
1619
- constructor(http) {
1620
- this.http = http;
1621
- }
1622
- async create(vendorId, data) {
1623
- return this.http.request("POST", `/api/v1/vendors/${vendorId}/sources`, data);
1624
- }
1625
- async listForVendor(vendorId) {
1626
- return this.http.request("GET", `/api/v1/vendors/${vendorId}/sources`);
1627
- }
1628
- async get(sourceId) {
1629
- return this.http.request("GET", `/api/v1/sources/${sourceId}`);
1630
- }
1631
- async update(sourceId, data) {
1632
- return this.http.request("PATCH", `/api/v1/sources/${sourceId}`, data);
1633
- }
1634
- async delete(sourceId) {
1635
- return this.http.request("DELETE", `/api/v1/sources/${sourceId}`);
1636
- }
1637
- async createClient(sourceId, data) {
1638
- return this.http.request("POST", `/api/v1/sources/${sourceId}/clients`, data);
1639
- }
1640
- async listClients(sourceId) {
1641
- return this.http.request("GET", `/api/v1/sources/${sourceId}/clients`);
1642
- }
1643
- };
1644
-
1645
- // src/modules/clients.ts
1646
- var ClientsModule = class {
1647
- constructor(http) {
1648
- this.http = http;
1649
- }
1650
- async get(clientId) {
1651
- return this.http.request("GET", `/api/v1/clients/${clientId}`);
1652
- }
1653
- async update(clientId, data) {
1654
- return this.http.request("PATCH", `/api/v1/clients/${clientId}`, data);
1655
- }
1656
- async delete(clientId) {
1657
- return this.http.request("DELETE", `/api/v1/clients/${clientId}`);
1658
- }
1659
- };
1660
-
1661
- // src/modules/hierarchy.ts
1662
- var HierarchyModule = class {
1663
- constructor(http) {
1664
- this.http = http;
1665
- }
1666
- async getGraph() {
1667
- return this.http.request("GET", "/api/v1/hierarchy");
1668
- }
1669
- async linkVendorSource(vendorId, sourceId) {
1670
- return this.http.request("POST", "/api/v1/hierarchy/link/vendor-source", { vendorId, sourceId });
1671
- }
1672
- async unlinkVendorSource(vendorId, sourceId) {
1673
- return this.http.request("DELETE", "/api/v1/hierarchy/link/vendor-source", { vendorId, sourceId });
1674
- }
1675
- async linkSourceClient(sourceId, clientId) {
1676
- return this.http.request("POST", "/api/v1/hierarchy/link/source-client", { sourceId, clientId });
1677
- }
1678
- async unlinkSourceClient(sourceId, clientId) {
1679
- return this.http.request("DELETE", "/api/v1/hierarchy/link/source-client", { sourceId, clientId });
1680
- }
1681
- };
1682
-
1683
- // src/modules/memberships.ts
1684
- var MembershipsModule = class {
1685
- constructor(http) {
1686
- this.http = http;
1687
- }
1688
- async listForUser(userId, tenantId) {
1689
- return this.http.request("GET", `/api/v1/users/${userId}/memberships?tenantId=${encodeURIComponent(tenantId)}`);
1690
- }
1691
- async listForScope(scopeType, scopeId) {
1692
- return this.http.request("GET", `/api/v1/memberships/scope/${scopeType}/${scopeId}`);
1693
- }
1694
- async grant(data) {
1695
- return this.http.request("POST", "/api/v1/memberships", data);
1696
- }
1697
- async revoke(id) {
1698
- return this.http.request("DELETE", `/api/v1/memberships/${id}`);
1699
- }
1700
- async update(id, data) {
1701
- return this.http.request("PATCH", `/api/v1/memberships/${id}`, data);
1702
- }
1703
- async listForTenant(params) {
1704
- const query = new URLSearchParams();
1705
- if (params?.scopeType) query.set("scopeType", params.scopeType);
1706
- if (params?.roleName) query.set("roleName", params.roleName);
1707
- const qs = query.toString();
1708
- return this.http.request("GET", `/api/v1/memberships/tenant${qs ? `?${qs}` : ""}`);
1709
- }
1710
- };
1711
-
1712
- // src/modules/scope.ts
1713
- var ScopeModule = class {
1714
- constructor(http) {
1715
- this.http = http;
1716
- }
1717
- async getAvailable() {
1718
- return this.http.request("GET", "/api/v1/auth/available-scopes");
1719
- }
1720
- async switchScope(scopeType, scopeId) {
1721
- return this.http.request("POST", "/api/v1/auth/switch-scope", { scopeType, scopeId });
1722
- }
1723
- };
1724
-
1725
- // src/modules/gdpr.ts
1726
- var GdprModule = class {
1727
- constructor(http) {
1728
- this.http = http;
1729
- }
1730
- async exportData() {
1731
- return this.http.request("GET", "/api/v1/gdpr/export");
1732
- }
1733
- async deleteAccount(confirmEmail) {
1734
- return this.http.request("POST", "/api/v1/gdpr/delete", { confirmEmail });
1735
- }
1736
- };
1737
-
1738
- // src/modules/pin.ts
1739
- var PinModule = class {
1740
- constructor(http) {
1741
- this.http = http;
1742
- }
1743
- async set(pin) {
1744
- return this.http.request("POST", "/api/v1/pin/set", { pin });
1745
- }
1746
- async change(currentPin, newPin) {
1747
- return this.http.request("POST", "/api/v1/pin/change", { currentPin, newPin });
1748
- }
1749
- async remove() {
1750
- return this.http.request("DELETE", "/api/v1/pin");
1751
- }
1752
- async getStatus() {
1753
- return this.http.request("GET", "/api/v1/pin/status");
1754
- }
1755
- async login(email, pin, deviceFingerprint) {
1756
- const data = await this.http.request(
1757
- "POST",
1758
- "/api/v1/pin/login",
1759
- { email, pin, ...deviceFingerprint && { deviceFingerprint } },
1760
- { skipAutoRefresh: true }
1761
- );
1762
- if (data.type === "success" && data.accessToken && data.refreshToken && data.user) {
1763
- return {
1764
- status: "authenticated",
1765
- authMode: "token",
1766
- tokens: { accessToken: data.accessToken, refreshToken: data.refreshToken },
1767
- user: data.user
1768
- };
1769
- }
1770
- if (data.type === "mfa_required" && data.mfaChallengeToken) {
1771
- return {
1772
- status: "mfa_required",
1773
- mfaChallengeToken: data.mfaChallengeToken,
1774
- availableMethods: data.availableMethods || []
1775
- };
1776
- }
1777
- if (data.type === "tenant_selection" && data.tenantSelectionToken && data.tenants) {
1778
- return {
1779
- status: "tenant_selection",
1780
- tenantSelectionToken: data.tenantSelectionToken,
1781
- tenants: data.tenants
1782
- };
1783
- }
1784
- throw new Error("Unexpected PIN login response shape");
1785
- }
1786
- };
1787
-
1788
- // src/modules/mfa.ts
1789
- var MfaModule = class {
1790
- constructor(http) {
1791
- this.http = http;
1792
- }
1793
- async getAvailableMethods() {
1794
- return this.http.request("GET", "/api/v1/mfa/methods");
1795
- }
1796
- async enrollTotp() {
1797
- return this.http.request("POST", "/api/v1/mfa/enroll/totp");
1798
- }
1799
- async verifyTotpEnrollment(code, secret) {
1800
- return this.http.request("POST", "/api/v1/mfa/enroll/totp/verify", { code, secret });
1801
- }
1802
- async enrollSms(phoneNumber) {
1803
- return this.http.request("POST", "/api/v1/mfa/enroll/sms", { phoneNumber });
1804
- }
1805
- async verifySmsEnrollment(code, phoneNumber) {
1806
- return this.http.request("POST", "/api/v1/mfa/enroll/sms/verify", { code, phoneNumber });
1807
- }
1808
- async startEmailEnrollment() {
1809
- return this.http.request("POST", "/api/v1/mfa/enroll/email/start");
1810
- }
1811
- async verifyEmailEnrollment(code) {
1812
- return this.http.request("POST", "/api/v1/mfa/enroll/email/verify", { code });
1813
- }
1814
- async listEnrollments() {
1815
- return this.http.request("GET", "/api/v1/mfa/enrollments");
1816
- }
1817
- async setPrimaryEnrollment(enrollmentId) {
1818
- return this.http.request("PATCH", `/api/v1/mfa/enrollments/${enrollmentId}/primary`);
1819
- }
1820
- async deactivateEnrollment(enrollmentId) {
1821
- return this.http.request("DELETE", `/api/v1/mfa/enrollments/${enrollmentId}`);
1822
- }
1823
- async regenerateBackupCodes() {
1824
- return this.http.request("POST", "/api/v1/mfa/backup-codes/regenerate");
1825
- }
1826
- async getBackupCodeCount() {
1827
- return this.http.request("GET", "/api/v1/mfa/backup-codes/count");
1828
- }
1829
- };
1830
-
1831
- // src/modules/branding.ts
1832
- function normalizeBrandingConfig2(config) {
1833
- return {
1834
- ...config,
1835
- brandName: config.brandName ?? config.companyName,
1836
- loginHeadline: config.loginHeadline ?? config.headline ?? null,
1837
- loginSubheadline: config.loginSubheadline ?? config.subheadline ?? null,
1838
- companyName: config.companyName ?? config.brandName,
1839
- headline: config.headline ?? config.loginHeadline ?? null,
1840
- subheadline: config.subheadline ?? config.loginSubheadline ?? null
1841
- };
1842
- }
1843
- function normalizeBrandingAsset(asset) {
1844
- return {
1845
- ...asset,
1846
- url: asset.url ?? asset.publicUrl,
1847
- publicUrl: asset.publicUrl ?? asset.url
1848
- };
1849
- }
1850
- function normalizeBrandingUpdate(data) {
1851
- return {
1852
- ...data,
1853
- brandName: data.brandName ?? data.companyName,
1854
- loginHeadline: data.loginHeadline ?? data.headline,
1855
- loginSubheadline: data.loginSubheadline ?? data.subheadline
1856
- };
1857
- }
1858
- var BrandingModule = class {
1859
- constructor(http) {
1860
- this.http = http;
1861
- }
1862
- async get(vendorId) {
1863
- const config = await this.http.request("GET", `/api/v1/branding/${vendorId}`);
1864
- return normalizeBrandingConfig2(config);
1865
- }
1866
- async updateBranding(vendorId, data) {
1867
- const config = await this.http.request("PUT", `/api/v1/branding/${vendorId}`, normalizeBrandingUpdate(data));
1868
- return normalizeBrandingConfig2(config);
1869
- }
1870
- async publishBranding(vendorId) {
1871
- const config = await this.http.request("POST", `/api/v1/branding/${vendorId}/publish`);
1872
- return normalizeBrandingConfig2(config);
1873
- }
1874
- async unpublishBranding(vendorId) {
1875
- const config = await this.http.request("POST", `/api/v1/branding/${vendorId}/unpublish`);
1876
- return normalizeBrandingConfig2(config);
1877
- }
1878
- async resetBranding(vendorId) {
1879
- const config = await this.http.request("POST", `/api/v1/branding/${vendorId}/reset`);
1880
- return normalizeBrandingConfig2(config);
1881
- }
1882
- async uploadAsset(vendorId, data) {
1883
- const asset = await this.http.request("POST", `/api/v1/branding/${vendorId}/upload`, data);
1884
- return normalizeBrandingAsset(asset);
1885
- }
1886
- async listAssets(vendorId) {
1887
- const assets = await this.http.request("GET", `/api/v1/branding/${vendorId}/assets`);
1888
- return assets.map(normalizeBrandingAsset);
1889
- }
1890
- async deleteAsset(vendorId, assetId) {
1891
- return this.http.request("DELETE", `/api/v1/branding/${vendorId}/assets/${assetId}`);
1892
- }
1893
- async listDomains(vendorId) {
1894
- return this.http.request("GET", `/api/v1/branding/${vendorId}/domains`);
1895
- }
1896
- async addDomain(vendorId, domain) {
1897
- return this.http.request("POST", `/api/v1/branding/${vendorId}/domains`, { domain });
1898
- }
1899
- async removeDomain(vendorId, domainId) {
1900
- return this.http.request("DELETE", `/api/v1/branding/${vendorId}/domains/${domainId}`);
1901
- }
1902
- };
1903
-
1904
- // src/client.ts
1905
- var IQAuthClient = class _IQAuthClient {
1906
- constructor(config) {
1907
- this.config = config;
1908
- this.environment = _IQAuthClient.resolveEnvironment(config);
1909
- this._accessToken = "accessToken" in config ? config.accessToken : void 0;
1910
- this._refreshToken = "refreshToken" in config ? config.refreshToken : void 0;
1911
- this._apiKey = "apiKey" in config ? config.apiKey : void 0;
1912
- this.httpClient = new HttpClient({
1913
- baseUrl: config.baseUrl,
1914
- environment: this.environment,
1915
- getAccessToken: () => this._accessToken,
1916
- getRefreshToken: () => this._refreshToken,
1917
- getApiKey: () => this._apiKey,
1918
- setTokens: (tokens) => {
1919
- this._accessToken = tokens.accessToken;
1920
- this._refreshToken = tokens.refreshToken;
1921
- },
1922
- autoRefresh: "autoRefresh" in config ? config.autoRefresh !== false : true,
1923
- onTokenRefresh: "onTokenRefresh" in config ? config.onTokenRefresh : void 0,
1924
- sessionHeaderName: config.sessionHeaderName,
1925
- sessionHeaderValue: config.sessionHeaderValue,
1926
- retry: config.retry
1927
- });
1928
- this.auth = new AuthModule(this.httpClient);
1929
- this.tokens = new TokensModule(config.baseUrl, {
1930
- issuer: config.verify?.issuer,
1931
- audience: config.verify?.audience,
1932
- clockTolerance: config.verify?.clockTolerance
1933
- });
1934
- this.sessions = new SessionsModule(this.httpClient);
1935
- this.users = new UsersModule(this.httpClient);
1936
- this.permissions = new PermissionsModule(() => this.getCurrentClaims());
1937
- this.oidc = new OidcModule(this.httpClient, config.baseUrl, { tokens: this.tokens });
1938
- this.tenants = new TenantsModule(this.httpClient);
1939
- this.apps = new AppsModule(this.httpClient);
1940
- this.roles = new RolesModule(this.httpClient);
1941
- this.permissionGroups = new PermissionGroupsModule(this.httpClient);
1942
- this.apiKeys = new ApiKeysModule(this.httpClient);
1943
- this.invites = new InvitesModule(this.httpClient);
1944
- this.webhooks = new WebhooksModule(this.httpClient);
1945
- this.entitlements = new EntitlementsModule(this.httpClient);
1946
- this.vendors = new VendorsModule(this.httpClient);
1947
- this.sources = new SourcesModule(this.httpClient);
1948
- this.clients = new ClientsModule(this.httpClient);
1949
- this.hierarchy = new HierarchyModule(this.httpClient);
1950
- this.memberships = new MembershipsModule(this.httpClient);
1951
- this.scope = new ScopeModule(this.httpClient);
1952
- this.gdpr = new GdprModule(this.httpClient);
1953
- this.pin = new PinModule(this.httpClient);
1954
- this.mfa = new MfaModule(this.httpClient);
1955
- this.branding = new BrandingModule(this.httpClient);
1956
- }
1957
- static forBrowserSession(config) {
1958
- return new _IQAuthClient({ ...config, environment: "browser_session" });
1959
- }
1960
- static forServer(config) {
1961
- return new _IQAuthClient({ ...config, environment: "server" });
1962
- }
1963
- static forMobile(config) {
1964
- return new _IQAuthClient({ ...config, environment: "mobile" });
1965
- }
1966
- static forService(config) {
1967
- return new _IQAuthClient({ ...config, environment: "service" });
1968
- }
1969
- setTokens(tokens) {
1970
- this._accessToken = tokens.accessToken;
1971
- this._refreshToken = tokens.refreshToken;
1972
- }
1973
- getAccessToken() {
1974
- return this._accessToken;
1975
- }
1976
- getRefreshToken() {
1977
- return this._refreshToken;
1978
- }
1979
- getCurrentClaims() {
1980
- if (!this._accessToken) return null;
1981
- return this.tokens.decode(this._accessToken);
1982
- }
1983
- static resolveEnvironment(config) {
1984
- if (config.environment) return config.environment;
1985
- if (config.apiKey) return "service";
1986
- return "server";
1987
- }
1988
- };
1989
-
1990
508
  // src/next.ts
1991
509
  function readCookieFromHeader(header, name) {
1992
510
  if (!header) return void 0;
@@ -2009,8 +527,7 @@ function toResponse(hr) {
2009
527
  return new Response(JSON.stringify(hr.body), { status: hr.status, headers });
2010
528
  }
2011
529
  function handler(options) {
2012
- const parsed = parsePublishableKey(options.publishableKey);
2013
- if (!parsed) throw new Error("@iqauth/sdk/next: invalid publishable key");
530
+ const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/next handler" });
2014
531
  const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
2015
532
  const helperConfig = { ...options, issuer };
2016
533
  const accessCookie = options.accessCookieName ?? "iqauth_at";
@@ -2034,7 +551,7 @@ function handler(options) {
2034
551
  if (action === "signout") {
2035
552
  const auth = req.headers.get("authorization");
2036
553
  const accessToken = auth && auth.replace(/^Bearer /i, "") || readCookieFromHeader(cookieHeader, accessCookie);
2037
- return toResponse(await handleSignout(helperConfig, { accessToken }));
554
+ return toResponse(await handleSignout(helperConfig, { accessToken, ssoCookieHeader: cookieHeader ?? void 0 }));
2038
555
  }
2039
556
  return new Response(JSON.stringify({ success: false, error: { code: "NOT_FOUND", message: `Unknown action: ${action}` } }), {
2040
557
  status: 404,
@@ -2043,11 +560,10 @@ function handler(options) {
2043
560
  };
2044
561
  }
2045
562
  function createMiddleware(options) {
2046
- const parsed = parsePublishableKey(options.publishableKey);
2047
- if (!parsed) throw new Error("@iqauth/sdk/next: invalid publishable key");
563
+ const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/next createMiddleware" });
2048
564
  const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
2049
565
  const accessCookie = options.accessCookieName ?? "iqauth_at";
2050
- const client = new IQAuthClient({ baseUrl: issuer, environment: "server" });
566
+ const tokens = new TokensModule(issuer);
2051
567
  return async (req) => {
2052
568
  const auth = req.headers.get("authorization");
2053
569
  let token;
@@ -2060,7 +576,7 @@ function createMiddleware(options) {
2060
576
  });
2061
577
  }
2062
578
  try {
2063
- await client.tokens.verify(token);
579
+ await tokens.verify(token);
2064
580
  return void 0;
2065
581
  } catch (err) {
2066
582
  const code = err.code || "TOKEN_INVALID";
@@ -2072,8 +588,7 @@ function createMiddleware(options) {
2072
588
  };
2073
589
  }
2074
590
  async function getAuth(options) {
2075
- const parsed = parsePublishableKey(options.publishableKey);
2076
- if (!parsed) throw new Error("@iqauth/sdk/next: invalid publishable key");
591
+ const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/next getAuth" });
2077
592
  const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
2078
593
  const accessCookie = options.accessCookieName ?? "iqauth_at";
2079
594
  let cookieJar = null;
@@ -2090,9 +605,9 @@ async function getAuth(options) {
2090
605
  }
2091
606
  const token = cookieJar?.get(accessCookie)?.value;
2092
607
  if (!token) return null;
2093
- const client = new IQAuthClient({ baseUrl: issuer, environment: "server" });
608
+ const tokens = new TokensModule(issuer);
2094
609
  try {
2095
- return await client.tokens.verify(token);
610
+ return await tokens.verify(token);
2096
611
  } catch {
2097
612
  return null;
2098
613
  }