@iqauth/sdk 2.6.4 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) 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 +212 -46
  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 +293 -34
  9. package/dist/browser.mjs +5 -5
  10. package/dist/{chunk-BVV54LPI.mjs → chunk-25SSYDIP.mjs} +10 -4
  11. package/dist/{chunk-XAWYUPMO.mjs → chunk-4V7FKOTG.mjs} +242 -22
  12. package/dist/{chunk-6I6RM4MN.mjs → chunk-6PJRLRB4.mjs} +33 -3
  13. package/dist/{chunk-SL3KRS4W.mjs → chunk-CIJORODR.mjs} +23 -1
  14. package/dist/{chunk-LIZYFXH7.mjs → chunk-DFWHSDYQ.mjs} +1 -1
  15. package/dist/chunk-GLXSIGVS.mjs +66 -0
  16. package/dist/{chunk-DJIBN2N7.mjs → chunk-GN37E64I.mjs} +29 -7
  17. package/dist/{chunk-WQWBJSSS.mjs → chunk-HVHNYPDC.mjs} +6 -6
  18. package/dist/chunk-JRDVUWAL.mjs +46 -0
  19. package/dist/{chunk-UNYDG2L4.mjs → chunk-NUO2I65G.mjs} +56 -23
  20. package/dist/{chunk-5T7GHBX6.mjs → chunk-TLET552H.mjs} +36 -0
  21. package/dist/chunk-VYQ3ETCK.mjs +244 -0
  22. package/dist/{chunk-3JULWS6F.mjs → chunk-WCELYTJ3.mjs} +3 -3
  23. package/dist/chunk-WHT6WKTY.mjs +3180 -0
  24. package/dist/{chunk-MKKZULZR.mjs → chunk-WIFG74IK.mjs} +1 -1
  25. package/dist/chunk-WSH4SW7F.mjs +490 -0
  26. package/dist/{chunk-W3F4JYGP.mjs → chunk-ZLJPABB7.mjs} +139 -23
  27. package/dist/cli/index.js +2 -2
  28. package/dist/cli/index.mjs +2 -2
  29. package/dist/{client-BNQe3AgF.d.ts → client-D8L-PaWr.d.mts} +59 -6
  30. package/dist/{client-kYlJFgPv.d.mts → client-DkPL0EPZ.d.ts} +59 -6
  31. package/dist/{doctor-YYNHNMLD.mjs → doctor-JAFXWU3X.mjs} +2 -2
  32. package/dist/errors-Jl1Jtm-6.d.mts +107 -0
  33. package/dist/errors-Jl1Jtm-6.d.ts +107 -0
  34. package/dist/{express-CHpfa7D_.d.ts → express-Budysq4h.d.ts} +2 -2
  35. package/dist/{express-B6_1vBYZ.d.mts → express-DDTA3qV1.d.mts} +2 -2
  36. package/dist/express.d.mts +7 -6
  37. package/dist/express.d.ts +7 -6
  38. package/dist/express.js +563 -85
  39. package/dist/express.mjs +73 -34
  40. package/dist/fastify.d.mts +10 -0
  41. package/dist/fastify.d.ts +10 -0
  42. package/dist/fastify.js +589 -65
  43. package/dist/fastify.mjs +101 -11
  44. package/dist/hono.d.mts +10 -0
  45. package/dist/hono.d.ts +10 -0
  46. package/dist/hono.js +566 -65
  47. package/dist/hono.mjs +78 -11
  48. package/dist/index-Cko-d5po.d.mts +1848 -0
  49. package/dist/index-RNqwEcmY.d.ts +1848 -0
  50. package/dist/index.d.mts +56 -8
  51. package/dist/index.d.ts +56 -8
  52. package/dist/index.js +694 -75
  53. package/dist/index.mjs +30 -10
  54. package/dist/{keys-NLWFAOEM.mjs → keys-6Y776TG2.mjs} +2 -2
  55. package/dist/locales.d.mts +1 -1
  56. package/dist/locales.d.ts +1 -1
  57. package/dist/locales.js +36 -0
  58. package/dist/locales.mjs +1 -1
  59. package/dist/mobile.d.mts +77 -7
  60. package/dist/mobile.d.ts +77 -7
  61. package/dist/mobile.js +307 -46
  62. package/dist/mobile.mjs +98 -3
  63. package/dist/next.d.mts +10 -1
  64. package/dist/next.d.ts +10 -1
  65. package/dist/next.js +596 -205
  66. package/dist/next.mjs +83 -10
  67. package/dist/{provisioningBridge-88xjOS2n.d.mts → provisioningBridge-BXPMZCLe.d.ts} +30 -2
  68. package/dist/{provisioningBridge-DnTfzdZK.d.ts → provisioningBridge-IEycmsgb.d.mts} +30 -2
  69. package/dist/{publishableKey-BaR0HoAH.d.ts → publishableKey-f2kq-rKw.d.mts} +1 -1
  70. package/dist/{publishableKey-BaR0HoAH.d.mts → publishableKey-f2kq-rKw.d.ts} +1 -1
  71. package/dist/react-permissions.d.mts +52 -0
  72. package/dist/react-permissions.d.ts +52 -0
  73. package/dist/react-permissions.js +239 -0
  74. package/dist/react-permissions.mjs +98 -0
  75. package/dist/react.d.mts +9 -1624
  76. package/dist/react.d.ts +9 -1624
  77. package/dist/react.js +882 -73
  78. package/dist/react.mjs +71 -2631
  79. package/dist/{reverify-4UEJXUS6.mjs → reverify-C64QXKJO.mjs} +2 -2
  80. package/dist/server/handlers.d.mts +200 -4
  81. package/dist/server/handlers.d.ts +200 -4
  82. package/dist/server/handlers.js +530 -16
  83. package/dist/server/handlers.mjs +14 -3
  84. package/dist/server.d.mts +171 -8
  85. package/dist/server.d.ts +171 -8
  86. package/dist/server.js +579 -61
  87. package/dist/server.mjs +99 -12
  88. package/dist/service.d.mts +4 -4
  89. package/dist/service.d.ts +4 -4
  90. package/dist/service.js +212 -46
  91. package/dist/service.mjs +3 -3
  92. package/dist/{signIn-CiIBTJIh.d.mts → signIn-CReqfXsh.d.mts} +95 -3
  93. package/dist/{signIn-OCr88Zf8.d.ts → signIn-Cfa1GTpO.d.ts} +95 -3
  94. package/dist/{signIn-4OKLDEIH.mjs → signIn-SHBW6Z4T.mjs} +1 -1
  95. package/dist/test.mjs +3 -3
  96. package/dist/{tokens-DCyzzn8L.d.mts → tokens-9F6ETrzk.d.ts} +9 -2
  97. package/dist/{tokens-aHiGFr_E.d.ts → tokens-B06VtvUi.d.mts} +9 -2
  98. package/dist/{types-DZAflmmq.d.mts → types-Bn8O-OEd.d.mts} +164 -11
  99. package/dist/{types-DZAflmmq.d.ts → types-Bn8O-OEd.d.ts} +164 -11
  100. package/dist/{types-6bNdxesb.d.ts → types-DnU2LhXR.d.mts} +7 -1
  101. package/dist/{types-6bNdxesb.d.mts → types-DnU2LhXR.d.ts} +7 -1
  102. package/dist/webhooks.d.mts +113 -17
  103. package/dist/webhooks.d.ts +113 -17
  104. package/dist/webhooks.js +179 -15
  105. package/dist/webhooks.mjs +7 -1
  106. package/dist/ws.d.mts +2 -2
  107. package/dist/ws.d.ts +2 -2
  108. package/dist/ws.js +80 -30
  109. package/dist/ws.mjs +4 -4
  110. package/docs/error-handling.md +101 -0
  111. package/docs/guides/effective-permissions.md +171 -0
  112. package/docs/guides/invitations.md +65 -0
  113. package/package.json +19 -4
  114. package/dist/chunk-6TDJJER7.mjs +0 -217
  115. package/dist/chunk-UKZLOHZG.mjs +0 -83
  116. package/dist/errors-CDdl24MP.d.mts +0 -52
  117. package/dist/errors-CDdl24MP.d.ts +0 -52
@@ -91,7 +91,7 @@ async function buildSignInUrl(manager, opts = {}) {
91
91
  returnTo,
92
92
  createdAt: Date.now()
93
93
  });
94
- const url = new URL(opts.signInPath ?? DEFAULT_SIGN_IN_PATH, manager.issuerUrl);
94
+ const url = new URL(opts.signInPath ?? DEFAULT_SIGN_IN_PATH, manager.hostedIssuerUrl);
95
95
  url.searchParams.set("response_type", "code");
96
96
  url.searchParams.set("app", manager.appKey);
97
97
  url.searchParams.set("publishable_key", manager.publishableKey.raw);
@@ -106,33 +106,50 @@ async function buildSignInUrl(manager, opts = {}) {
106
106
  return url.toString();
107
107
  }
108
108
  async function redirectToSignIn(manager, opts = {}) {
109
- const url = await buildSignInUrl(manager, opts);
110
- if (typeof window === "undefined") {
111
- throw new Error("redirectToSignIn requires a browser environment");
109
+ const t0 = Date.now();
110
+ let ok = false;
111
+ let code;
112
+ try {
113
+ const url = await buildSignInUrl(manager, opts);
114
+ if (typeof window === "undefined") {
115
+ code = "NO_WINDOW";
116
+ throw new Error("redirectToSignIn requires a browser environment");
117
+ }
118
+ ok = true;
119
+ manager.recordTiming("signIn", Date.now() - t0, true);
120
+ window.location.assign(url);
121
+ } catch (err) {
122
+ if (!ok) manager.recordTiming("signIn", Date.now() - t0, false, code ?? (err instanceof Error ? err.message : "ERROR"));
123
+ throw err;
112
124
  }
113
- window.location.assign(url);
114
125
  }
115
126
  async function signIn(manager, opts = {}) {
116
127
  return redirectToSignIn(manager, opts);
117
128
  }
118
129
  async function handleAuthCallback(manager, options = {}) {
130
+ const t0 = Date.now();
131
+ const emit = (ok, code2) => manager.recordTiming("signIn", Date.now() - t0, ok, code2);
119
132
  const url = new URL(options.url ?? (typeof window !== "undefined" ? window.location.href : ""));
120
133
  const code = url.searchParams.get("code");
121
134
  const state = url.searchParams.get("state");
122
135
  const errorParam = url.searchParams.get("error");
123
136
  if (errorParam) {
137
+ emit(false, errorParam);
124
138
  return { ok: false, returnTo: "/", error: errorParam };
125
139
  }
126
140
  if (!code || !state) {
141
+ emit(false, "missing_code_or_state");
127
142
  return { ok: false, returnTo: "/", error: "missing_code_or_state" };
128
143
  }
129
144
  const record = loadPkce(state);
130
145
  if (!record) {
146
+ emit(false, "unknown_state");
131
147
  return { ok: false, returnTo: "/", error: "unknown_state" };
132
148
  }
133
149
  clearPkce(state);
134
150
  const fetchImpl = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
135
151
  if (!fetchImpl) {
152
+ emit(false, "no_fetch");
136
153
  return { ok: false, returnTo: record.returnTo, error: "no_fetch" };
137
154
  }
138
155
  const tokenUrl = `${manager.issuerUrl}${options.tokenPath ?? DEFAULT_TOKEN_PATH}`;
@@ -151,10 +168,12 @@ async function handleAuthCallback(manager, options = {}) {
151
168
  const body = await res.json().catch(() => ({}));
152
169
  if (!res.ok) {
153
170
  const desc = body.error_description ?? body.error ?? "token_exchange_failed";
171
+ emit(false, desc);
154
172
  return { ok: false, returnTo: record.returnTo, error: desc };
155
173
  }
156
174
  const tokens = body;
157
175
  if (!tokens.access_token) {
176
+ emit(false, "missing_access_token");
158
177
  return { ok: false, returnTo: record.returnTo, error: "missing_access_token" };
159
178
  }
160
179
  if (tokens.refresh_token) {
@@ -162,21 +181,24 @@ async function handleAuthCallback(manager, options = {}) {
162
181
  setCookie(cookieName, tokens.refresh_token, { maxAgeSeconds: 60 * 60 * 24 * 30 });
163
182
  }
164
183
  manager.applyAccessToken(tokens.access_token, tokens.refresh_token);
184
+ emit(true);
165
185
  return { ok: true, returnTo: record.returnTo };
166
186
  }
167
187
  async function signOut(manager, opts = {}) {
168
188
  if (!opts.localOnly) {
169
189
  const issuer = manager.issuerUrl.replace(/\/$/, "");
190
+ const idempotency = manager.getIdempotencyToken();
170
191
  try {
171
192
  const url = `${issuer}${opts.logoutPath ?? DEFAULT_LOGOUT_PATH}`;
172
- await manager.fetch(url, { method: "POST" }).catch(() => void 0);
193
+ await manager.fetch(url, { method: "POST", headers: { "X-IQAuth-Idempotency": idempotency } }).catch(() => void 0);
173
194
  } catch {
174
195
  }
175
196
  if (opts.endSsoSession !== false) {
176
197
  try {
177
198
  await fetch(`${issuer}${DEFAULT_SSO_LOGOUT_PATH}`, {
178
199
  method: "POST",
179
- credentials: "include"
200
+ credentials: "include",
201
+ headers: { "X-IQAuth-Idempotency": idempotency }
180
202
  }).catch(() => void 0);
181
203
  } catch {
182
204
  }
@@ -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";
@@ -64,14 +64,14 @@ function assertPublishableKey(raw, opts) {
64
64
  const ctx = opts?.context ? `${opts.context}: ` : "";
65
65
  if (typeof raw !== "string" || raw.length === 0) {
66
66
  throw new IQAuthError(
67
- "CONFIG_INVALID",
67
+ "config_invalid",
68
68
  `${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.`
69
69
  );
70
70
  }
71
71
  const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
72
72
  if (!shapeMatch) {
73
73
  throw new IQAuthError(
74
- "CONFIG_INVALID",
74
+ "config_invalid",
75
75
  `${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.`
76
76
  );
77
77
  }
@@ -80,19 +80,19 @@ function assertPublishableKey(raw, opts) {
80
80
  decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
81
81
  } catch {
82
82
  throw new IQAuthError(
83
- "CONFIG_INVALID",
83
+ "config_invalid",
84
84
  `${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
85
85
  );
86
86
  }
87
87
  if (!isPublishableKeyPayload(decoded)) {
88
88
  throw new IQAuthError(
89
- "CONFIG_INVALID",
89
+ "config_invalid",
90
90
  `${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
91
91
  );
92
92
  }
93
93
  if (!isValidIssuerUrl(decoded.iss)) {
94
94
  throw new IQAuthError(
95
- "CONFIG_INVALID",
95
+ "config_invalid",
96
96
  `${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.`
97
97
  );
98
98
  }
@@ -0,0 +1,46 @@
1
+ // src/browser/returnTo.ts
2
+ function normalizeOrigin(o) {
3
+ try {
4
+ return new URL(o).origin;
5
+ } catch {
6
+ return o.replace(/\/+$/, "");
7
+ }
8
+ }
9
+ function sanitizeReturnTo(input, options = {}) {
10
+ const fallback = options.fallback ?? "/";
11
+ if (!input || typeof input !== "string") return fallback;
12
+ const trimmed = input.trim();
13
+ if (!trimmed) return fallback;
14
+ if (trimmed.includes("\\")) return fallback;
15
+ if (trimmed.startsWith("//")) return fallback;
16
+ if (trimmed.startsWith("/") || trimmed.startsWith("#") || trimmed.startsWith("?")) {
17
+ return trimmed;
18
+ }
19
+ if (!/^[a-z][a-z0-9+\-.]*:/i.test(trimmed)) {
20
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
21
+ }
22
+ let parsed;
23
+ try {
24
+ parsed = new URL(trimmed);
25
+ } catch {
26
+ return fallback;
27
+ }
28
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return fallback;
29
+ const currentOrigin = options.currentOrigin ?? (typeof window !== "undefined" ? window.location.origin : "");
30
+ const allowed = /* @__PURE__ */ new Set();
31
+ if (currentOrigin) allowed.add(normalizeOrigin(currentOrigin));
32
+ for (const o of options.allowedOrigins ?? []) allowed.add(normalizeOrigin(o));
33
+ if (allowed.has(parsed.origin)) return parsed.toString();
34
+ return fallback;
35
+ }
36
+ function isReturnToAllowed(input, options = {}) {
37
+ const fallback = options.fallback ?? "/";
38
+ const out = sanitizeReturnTo(input, options);
39
+ if (!input) return false;
40
+ return out !== fallback || input === fallback;
41
+ }
42
+
43
+ export {
44
+ sanitizeReturnTo,
45
+ isReturnToAllowed
46
+ };
@@ -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 {
@@ -37,6 +37,7 @@ var enUS = {
37
37
  "signIn.useDifferentAccount": "Use a different account",
38
38
  "signIn.selectTenant": "Choose an organization",
39
39
  "signIn.selectTenantSubtitle": "You belong to multiple organizations. Pick one to continue.",
40
+ "signIn.selectScope": "Choose scope",
40
41
  "signIn.dividerOr": "or",
41
42
  "signIn.preparingExperience": "Preparing your sign-in experience.",
42
43
  "signIn.applicationUnavailable": "Application unavailable",
@@ -125,6 +126,11 @@ var enUS = {
125
126
  "orgSwitcher.createNew": "Create organization",
126
127
  "orgSwitcher.manage": "Manage organization",
127
128
  "orgSwitcher.noOrgs": "You don't belong to any organizations yet.",
129
+ "orgSwitcher.mfaRequiredTitle": "Two-factor verification required",
130
+ "orgSwitcher.mfaRequiredBody": "This organization requires an extra verification step. Continue in the hosted sign-in to finish switching.",
131
+ "orgSwitcher.scopeSelectionRequiredTitle": "Choose what to access",
132
+ "orgSwitcher.scopeSelectionRequiredBody": "This organization needs you to pick which workspace to open. Continue in the hosted sign-in to choose.",
133
+ "orgSwitcher.continueInHostedSignIn": "Continue in hosted sign-in \u2192",
128
134
  "orgProfile.title": "Organization settings",
129
135
  "orgProfile.generalTab": "General",
130
136
  "orgProfile.membersTab": "Members",
@@ -217,6 +223,7 @@ var frFR = {
217
223
  "signIn.useDifferentAccount": "Utiliser un autre compte",
218
224
  "signIn.selectTenant": "Choisir une organisation",
219
225
  "signIn.selectTenantSubtitle": "Vous appartenez \xE0 plusieurs organisations. Choisissez-en une pour continuer.",
226
+ "signIn.selectScope": "Choisir une port\xE9e",
220
227
  "signIn.dividerOr": "ou",
221
228
  "signIn.preparingExperience": "Pr\xE9paration de votre exp\xE9rience de connexion.",
222
229
  "signIn.applicationUnavailable": "Application indisponible",
@@ -305,6 +312,11 @@ var frFR = {
305
312
  "orgSwitcher.createNew": "Cr\xE9er une organisation",
306
313
  "orgSwitcher.manage": "G\xE9rer l'organisation",
307
314
  "orgSwitcher.noOrgs": "Vous n'appartenez encore \xE0 aucune organisation.",
315
+ "orgSwitcher.mfaRequiredTitle": "V\xE9rification en deux \xE9tapes requise",
316
+ "orgSwitcher.mfaRequiredBody": "Cette organisation n\xE9cessite une \xE9tape de v\xE9rification suppl\xE9mentaire. Poursuivez sur la page de connexion h\xE9berg\xE9e pour terminer le changement.",
317
+ "orgSwitcher.scopeSelectionRequiredTitle": "Choisir l'espace \xE0 ouvrir",
318
+ "orgSwitcher.scopeSelectionRequiredBody": "Cette organisation n\xE9cessite que vous choisissiez un espace de travail. Poursuivez sur la page de connexion h\xE9berg\xE9e pour choisir.",
319
+ "orgSwitcher.continueInHostedSignIn": "Continuer sur la connexion h\xE9berg\xE9e \u2192",
308
320
  "orgProfile.title": "Param\xE8tres de l'organisation",
309
321
  "orgProfile.generalTab": "G\xE9n\xE9ral",
310
322
  "orgProfile.membersTab": "Membres",
@@ -397,6 +409,7 @@ var esES = {
397
409
  "signIn.useDifferentAccount": "Usar otra cuenta",
398
410
  "signIn.selectTenant": "Elige una organizaci\xF3n",
399
411
  "signIn.selectTenantSubtitle": "Perteneces a varias organizaciones. Selecciona una para continuar.",
412
+ "signIn.selectScope": "Elegir \xE1mbito",
400
413
  "signIn.dividerOr": "o",
401
414
  "signIn.preparingExperience": "Preparando tu experiencia de inicio de sesi\xF3n.",
402
415
  "signIn.applicationUnavailable": "Aplicaci\xF3n no disponible",
@@ -485,6 +498,11 @@ var esES = {
485
498
  "orgSwitcher.createNew": "Crear organizaci\xF3n",
486
499
  "orgSwitcher.manage": "Gestionar organizaci\xF3n",
487
500
  "orgSwitcher.noOrgs": "A\xFAn no perteneces a ninguna organizaci\xF3n.",
501
+ "orgSwitcher.mfaRequiredTitle": "Se requiere verificaci\xF3n en dos pasos",
502
+ "orgSwitcher.mfaRequiredBody": "Esta organizaci\xF3n requiere un paso de verificaci\xF3n adicional. Contin\xFAa en el inicio de sesi\xF3n alojado para completar el cambio.",
503
+ "orgSwitcher.scopeSelectionRequiredTitle": "Elige a qu\xE9 acceder",
504
+ "orgSwitcher.scopeSelectionRequiredBody": "Esta organizaci\xF3n requiere que elijas un espacio de trabajo. Contin\xFAa en el inicio de sesi\xF3n alojado para elegir.",
505
+ "orgSwitcher.continueInHostedSignIn": "Continuar en el inicio de sesi\xF3n alojado \u2192",
488
506
  "orgProfile.title": "Configuraci\xF3n de la organizaci\xF3n",
489
507
  "orgProfile.generalTab": "Generales",
490
508
  "orgProfile.membersTab": "Miembros",
@@ -577,6 +595,7 @@ var deDE = {
577
595
  "signIn.useDifferentAccount": "Anderes Konto verwenden",
578
596
  "signIn.selectTenant": "Organisation ausw\xE4hlen",
579
597
  "signIn.selectTenantSubtitle": "Sie geh\xF6ren zu mehreren Organisationen. Bitte w\xE4hlen Sie eine aus.",
598
+ "signIn.selectScope": "Bereich ausw\xE4hlen",
580
599
  "signIn.preparingExperience": "Anmeldung wird vorbereitet.",
581
600
  "signIn.applicationUnavailable": "Anwendung nicht verf\xFCgbar",
582
601
  "signIn.applicationNotFound": "Anwendung nicht gefunden",
@@ -665,6 +684,11 @@ var deDE = {
665
684
  "orgSwitcher.createNew": "Organisation erstellen",
666
685
  "orgSwitcher.manage": "Organisation verwalten",
667
686
  "orgSwitcher.noOrgs": "Sie geh\xF6ren noch keiner Organisation an.",
687
+ "orgSwitcher.mfaRequiredTitle": "Zwei-Faktor-Verifizierung erforderlich",
688
+ "orgSwitcher.mfaRequiredBody": "Diese Organisation erfordert einen zus\xE4tzlichen Verifizierungsschritt. Fahren Sie in der gehosteten Anmeldung fort, um den Wechsel abzuschlie\xDFen.",
689
+ "orgSwitcher.scopeSelectionRequiredTitle": "Zugriff ausw\xE4hlen",
690
+ "orgSwitcher.scopeSelectionRequiredBody": "F\xFCr diese Organisation m\xFCssen Sie einen Arbeitsbereich ausw\xE4hlen. Fahren Sie in der gehosteten Anmeldung fort, um auszuw\xE4hlen.",
691
+ "orgSwitcher.continueInHostedSignIn": "In der gehosteten Anmeldung fortfahren \u2192",
668
692
  "orgProfile.title": "Organisationseinstellungen",
669
693
  "orgProfile.generalTab": "Allgemein",
670
694
  "orgProfile.membersTab": "Mitglieder",
@@ -757,6 +781,7 @@ var jaJP = {
757
781
  "signIn.useDifferentAccount": "\u5225\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F7F\u7528",
758
782
  "signIn.selectTenant": "\u7D44\u7E54\u3092\u9078\u629E",
759
783
  "signIn.selectTenantSubtitle": "\u8907\u6570\u306E\u7D44\u7E54\u306B\u6240\u5C5E\u3057\u3066\u3044\u307E\u3059\u3002\u7D9A\u3051\u308B\u306B\u306F 1 \u3064\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
784
+ "signIn.selectScope": "\u30B9\u30B3\u30FC\u30D7\u3092\u9078\u629E",
760
785
  "signIn.preparingExperience": "\u30B5\u30A4\u30F3\u30A4\u30F3\u306E\u6E96\u5099\u3092\u3057\u3066\u3044\u307E\u3059\u3002",
761
786
  "signIn.applicationUnavailable": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
762
787
  "signIn.applicationNotFound": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
@@ -845,6 +870,11 @@ var jaJP = {
845
870
  "orgSwitcher.createNew": "\u7D44\u7E54\u3092\u4F5C\u6210",
846
871
  "orgSwitcher.manage": "\u7D44\u7E54\u3092\u7BA1\u7406",
847
872
  "orgSwitcher.noOrgs": "\u307E\u3060\u3069\u306E\u7D44\u7E54\u306B\u3082\u6240\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093\u3002",
873
+ "orgSwitcher.mfaRequiredTitle": "\u4E8C\u6BB5\u968E\u8A8D\u8A3C\u304C\u5FC5\u8981\u3067\u3059",
874
+ "orgSwitcher.mfaRequiredBody": "\u3053\u306E\u7D44\u7E54\u3067\u306F\u8FFD\u52A0\u306E\u672C\u4EBA\u78BA\u8A8D\u304C\u5FC5\u8981\u3067\u3059\u3002\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u7D9A\u3051\u3066\u5207\u308A\u66FF\u3048\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
875
+ "orgSwitcher.scopeSelectionRequiredTitle": "\u30A2\u30AF\u30BB\u30B9\u5148\u3092\u9078\u629E",
876
+ "orgSwitcher.scopeSelectionRequiredBody": "\u3053\u306E\u7D44\u7E54\u3067\u306F\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306E\u9078\u629E\u304C\u5FC5\u8981\u3067\u3059\u3002\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
877
+ "orgSwitcher.continueInHostedSignIn": "\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u306B\u9032\u3080 \u2192",
848
878
  "orgProfile.title": "\u7D44\u7E54\u306E\u8A2D\u5B9A",
849
879
  "orgProfile.generalTab": "\u4E00\u822C",
850
880
  "orgProfile.membersTab": "\u30E1\u30F3\u30D0\u30FC",
@@ -937,6 +967,7 @@ var ptBR = {
937
967
  "signIn.useDifferentAccount": "Usar outra conta",
938
968
  "signIn.selectTenant": "Escolher uma organiza\xE7\xE3o",
939
969
  "signIn.selectTenantSubtitle": "Voc\xEA pertence a v\xE1rias organiza\xE7\xF5es. Escolha uma para continuar.",
970
+ "signIn.selectScope": "Escolher escopo",
940
971
  "signIn.preparingExperience": "Preparando sua experi\xEAncia de login.",
941
972
  "signIn.applicationUnavailable": "Aplicativo indispon\xEDvel",
942
973
  "signIn.applicationNotFound": "Aplicativo n\xE3o encontrado",
@@ -1024,6 +1055,11 @@ var ptBR = {
1024
1055
  "orgSwitcher.personal": "Conta pessoal",
1025
1056
  "orgSwitcher.createNew": "Criar organiza\xE7\xE3o",
1026
1057
  "orgSwitcher.manage": "Gerenciar organiza\xE7\xE3o",
1058
+ "orgSwitcher.mfaRequiredTitle": "Verifica\xE7\xE3o em duas etapas necess\xE1ria",
1059
+ "orgSwitcher.mfaRequiredBody": "Esta organiza\xE7\xE3o requer uma etapa de verifica\xE7\xE3o adicional. Continue no login hospedado para concluir a troca.",
1060
+ "orgSwitcher.scopeSelectionRequiredTitle": "Escolha o que acessar",
1061
+ "orgSwitcher.scopeSelectionRequiredBody": "Esta organiza\xE7\xE3o exige que voc\xEA escolha um espa\xE7o de trabalho. Continue no login hospedado para escolher.",
1062
+ "orgSwitcher.continueInHostedSignIn": "Continuar no login hospedado \u2192",
1027
1063
  "orgSwitcher.noOrgs": "Voc\xEA ainda n\xE3o pertence a nenhuma organiza\xE7\xE3o.",
1028
1064
  "orgProfile.title": "Configura\xE7\xF5es da organiza\xE7\xE3o",
1029
1065
  "orgProfile.generalTab": "Geral",