@iqauth/sdk 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/dist/browser-session.d.mts +12 -0
  4. package/dist/browser-session.d.ts +12 -0
  5. package/dist/browser-session.js +1812 -0
  6. package/dist/browser-session.mjs +28 -0
  7. package/dist/browser.d.mts +46 -0
  8. package/dist/browser.d.ts +46 -0
  9. package/dist/browser.js +768 -0
  10. package/dist/browser.mjs +47 -0
  11. package/dist/chunk-5HF3OBNO.mjs +189 -0
  12. package/dist/chunk-5WFR6Y33.mjs +59 -0
  13. package/dist/chunk-6I6RM4MN.mjs +51 -0
  14. package/dist/chunk-73R6BEGO.mjs +176 -0
  15. package/dist/chunk-E46DKOVI.mjs +632 -0
  16. package/dist/chunk-JQWYIIIS.mjs +1740 -0
  17. package/dist/chunk-X3K3WOBR.mjs +64 -0
  18. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  19. package/dist/cli/index.d.mts +1 -0
  20. package/dist/cli/index.d.ts +1 -0
  21. package/dist/cli/index.js +581 -0
  22. package/dist/cli/index.mjs +57 -0
  23. package/dist/client-C1DXfB8Z.d.mts +911 -0
  24. package/dist/client-CggvJmmm.d.ts +911 -0
  25. package/dist/dev-FUTJZSWN.mjs +56 -0
  26. package/dist/doctor-OHJRZBBT.mjs +89 -0
  27. package/dist/errors-CDdl24MP.d.mts +52 -0
  28. package/dist/errors-CDdl24MP.d.ts +52 -0
  29. package/dist/express-BKAXB5Nl.d.ts +61 -0
  30. package/dist/express-CpfyYTmw.d.mts +61 -0
  31. package/dist/express.d.mts +45 -0
  32. package/dist/express.d.ts +45 -0
  33. package/dist/express.js +2252 -0
  34. package/dist/express.mjs +122 -0
  35. package/dist/fastify.d.mts +23 -0
  36. package/dist/fastify.d.ts +23 -0
  37. package/dist/fastify.js +2062 -0
  38. package/dist/fastify.mjs +118 -0
  39. package/dist/hono.d.mts +22 -0
  40. package/dist/hono.d.ts +22 -0
  41. package/dist/hono.js +2051 -0
  42. package/dist/hono.mjs +107 -0
  43. package/dist/index.d.mts +6 -0
  44. package/dist/index.d.ts +6 -0
  45. package/dist/index.js +2070 -0
  46. package/dist/index.mjs +83 -0
  47. package/dist/init-LLCSQGNL.mjs +198 -0
  48. package/dist/keys-NLWFAOEM.mjs +63 -0
  49. package/dist/mobile.d.mts +11 -0
  50. package/dist/mobile.d.ts +11 -0
  51. package/dist/mobile.js +1809 -0
  52. package/dist/mobile.mjs +25 -0
  53. package/dist/next.d.mts +37 -0
  54. package/dist/next.d.ts +37 -0
  55. package/dist/next.js +2078 -0
  56. package/dist/next.mjs +130 -0
  57. package/dist/publishableKey-B5DIK81A.d.mts +24 -0
  58. package/dist/publishableKey-B5DIK81A.d.ts +24 -0
  59. package/dist/react.d.mts +196 -0
  60. package/dist/react.d.ts +196 -0
  61. package/dist/react.js +1457 -0
  62. package/dist/react.mjs +787 -0
  63. package/dist/server/handlers.d.mts +96 -0
  64. package/dist/server/handlers.d.ts +96 -0
  65. package/dist/server/handlers.js +243 -0
  66. package/dist/server/handlers.mjs +14 -0
  67. package/dist/server.d.mts +14 -0
  68. package/dist/server.d.ts +14 -0
  69. package/dist/server.js +2195 -0
  70. package/dist/server.mjs +47 -0
  71. package/dist/service.d.mts +11 -0
  72. package/dist/service.d.ts +11 -0
  73. package/dist/service.js +1809 -0
  74. package/dist/service.mjs +25 -0
  75. package/dist/signIn-C8f6qVjD.d.mts +238 -0
  76. package/dist/signIn-Cy2lbEXb.d.ts +238 -0
  77. package/dist/types-Cxl3bQHt.d.mts +900 -0
  78. package/dist/types-Cxl3bQHt.d.ts +900 -0
  79. package/docs/APP_INTEGRATION_MATRIX.md +59 -0
  80. package/docs/BROWSER_SESSION_MIGRATION.md +69 -0
  81. package/docs/FRESH_IMPLEMENTATION_GUIDE.md +188 -0
  82. package/docs/TARBALL_RELEASE_WORKFLOW.md +98 -0
  83. package/docs/V1_TO_V2_UPGRADE_GUIDE.md +318 -0
  84. package/docs/guides/api-keys.md +130 -0
  85. package/docs/guides/app-registration.md +149 -0
  86. package/docs/guides/auth-flows.md +168 -0
  87. package/docs/guides/branding.md +160 -0
  88. package/docs/guides/entitlements.md +115 -0
  89. package/docs/guides/entity-hierarchy.md +200 -0
  90. package/docs/guides/error-handling.md +251 -0
  91. package/docs/guides/gdpr-compliance.md +123 -0
  92. package/docs/guides/invitations.md +143 -0
  93. package/docs/guides/mfa-enrollment.md +170 -0
  94. package/docs/guides/middleware-reference.md +205 -0
  95. package/docs/guides/mobile-native.md +110 -0
  96. package/docs/guides/roles-and-permissions.md +220 -0
  97. package/docs/guides/scoped-authorization.md +247 -0
  98. package/docs/guides/server-platform-integration.md +52 -0
  99. package/docs/guides/service-automation-integration.md +36 -0
  100. package/docs/guides/session-management.md +97 -0
  101. package/docs/guides/tenant-management.md +216 -0
  102. package/docs/guides/token-verification.md +178 -0
  103. package/docs/guides/user-management.md +184 -0
  104. package/docs/guides/webhooks.md +136 -0
  105. package/docs/integration-prompts/README.md +20 -0
  106. package/docs/integration-prompts/first-party-browser-app.md +29 -0
  107. package/docs/integration-prompts/install-from-tarball.md +41 -0
  108. package/docs/integration-prompts/migrate-from-local-packages-source.md +57 -0
  109. package/docs/integration-prompts/native-mobile-app.md +24 -0
  110. package/docs/integration-prompts/server-platform-app.md +20 -0
  111. package/docs/integration-prompts/service-automation-app.md +20 -0
  112. package/package.json +115 -0
package/dist/react.mjs ADDED
@@ -0,0 +1,787 @@
1
+ import {
2
+ SessionManager,
3
+ handleAuthCallback,
4
+ redirectToSignIn,
5
+ signIn,
6
+ signOut
7
+ } from "./chunk-E46DKOVI.mjs";
8
+ import "./chunk-5WFR6Y33.mjs";
9
+ import "./chunk-6I6RM4MN.mjs";
10
+ import "./chunk-Y6FXYEAI.mjs";
11
+
12
+ // src/react/index.tsx
13
+ import {
14
+ createContext,
15
+ createElement,
16
+ Fragment,
17
+ useCallback,
18
+ useContext,
19
+ useEffect,
20
+ useMemo,
21
+ useRef,
22
+ useState,
23
+ useSyncExternalStore
24
+ } from "react";
25
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
26
+ var IQAuthContext = createContext(null);
27
+ function IQAuthProvider({
28
+ publishableKey,
29
+ issuer,
30
+ channelName,
31
+ proactiveRefresh,
32
+ manager: externalManager,
33
+ children
34
+ }) {
35
+ const managerRef = useRef(null);
36
+ if (!managerRef.current) {
37
+ managerRef.current = externalManager ?? new SessionManager({
38
+ publishableKey,
39
+ issuer,
40
+ channelName,
41
+ proactiveRefresh
42
+ });
43
+ }
44
+ const manager = managerRef.current;
45
+ const subscribe = useCallback(
46
+ (cb) => manager.subscribe(() => cb()),
47
+ [manager]
48
+ );
49
+ const getSnapshot = useCallback(() => manager.getSnapshot(), [manager]);
50
+ const getServerSnapshot = useCallback(() => manager.getSnapshot(), [manager]);
51
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
52
+ useEffect(() => {
53
+ void manager.bootstrap();
54
+ const onVisible = () => {
55
+ if (typeof document !== "undefined" && document.visibilityState === "visible") {
56
+ void manager.refresh();
57
+ }
58
+ };
59
+ if (typeof document !== "undefined") {
60
+ document.addEventListener("visibilitychange", onVisible);
61
+ }
62
+ return () => {
63
+ if (typeof document !== "undefined") {
64
+ document.removeEventListener("visibilitychange", onVisible);
65
+ }
66
+ };
67
+ }, [manager]);
68
+ const value = useMemo(() => ({ manager, snapshot }), [manager, snapshot]);
69
+ return createElement(IQAuthContext.Provider, { value }, children);
70
+ }
71
+ function useCtx() {
72
+ const ctx = useContext(IQAuthContext);
73
+ if (!ctx) throw new Error("IQAuth hooks must be used inside <IQAuthProvider>");
74
+ return ctx;
75
+ }
76
+ function useUser() {
77
+ const { snapshot } = useCtx();
78
+ return useMemo(
79
+ () => ({
80
+ isLoaded: snapshot.status !== "loading",
81
+ isSignedIn: snapshot.status === "authenticated" && !!snapshot.user,
82
+ user: snapshot.user,
83
+ error: snapshot.error
84
+ }),
85
+ [snapshot.status, snapshot.user, snapshot.error, snapshot.version]
86
+ );
87
+ }
88
+ function useSession() {
89
+ const { snapshot } = useCtx();
90
+ return useMemo(
91
+ () => ({
92
+ isLoaded: snapshot.status !== "loading",
93
+ isSignedIn: snapshot.status === "authenticated",
94
+ claims: snapshot.claims,
95
+ accessToken: snapshot.accessToken,
96
+ error: snapshot.error
97
+ }),
98
+ [snapshot.status, snapshot.claims, snapshot.accessToken, snapshot.error, snapshot.version]
99
+ );
100
+ }
101
+ function useAuth() {
102
+ const { manager, snapshot } = useCtx();
103
+ return useMemo(
104
+ () => ({
105
+ isLoaded: snapshot.status !== "loading",
106
+ isSignedIn: snapshot.status === "authenticated",
107
+ userId: snapshot.user?.sub ?? null,
108
+ tenantId: snapshot.tenantId,
109
+ error: snapshot.error,
110
+ signIn: (opts) => signIn(manager, opts),
111
+ signOut: (opts) => signOut(manager, opts),
112
+ redirectToSignIn: (opts) => redirectToSignIn(manager, opts),
113
+ getToken: () => manager.getToken(),
114
+ fetch: (input, init) => manager.fetch(input, init)
115
+ }),
116
+ [manager, snapshot.status, snapshot.user, snapshot.tenantId, snapshot.error, snapshot.version]
117
+ );
118
+ }
119
+ function useOrganization() {
120
+ const { snapshot } = useCtx();
121
+ return useMemo(
122
+ () => ({
123
+ isLoaded: snapshot.status !== "loading",
124
+ organization: snapshot.tenantId ? { id: snapshot.tenantId, tenantId: snapshot.tenantId } : null,
125
+ error: snapshot.error
126
+ }),
127
+ [snapshot.status, snapshot.tenantId, snapshot.error, snapshot.version]
128
+ );
129
+ }
130
+ function useAuthFetch() {
131
+ const { manager } = useCtx();
132
+ return useCallback(
133
+ (input, init) => manager.fetch(input, init),
134
+ [manager]
135
+ );
136
+ }
137
+ function SignedIn({ children }) {
138
+ const { isSignedIn, isLoaded } = useUser();
139
+ if (!isLoaded || !isSignedIn) return null;
140
+ return createElement(Fragment, null, children);
141
+ }
142
+ function SignedOut({ children }) {
143
+ const { isSignedIn, isLoaded } = useUser();
144
+ if (!isLoaded || isSignedIn) return null;
145
+ return createElement(Fragment, null, children);
146
+ }
147
+ function RedirectToSignIn(props = {}) {
148
+ const { manager, snapshot } = useCtx();
149
+ useEffect(() => {
150
+ if (snapshot.status === "unauthenticated") {
151
+ void redirectToSignIn(manager, props);
152
+ }
153
+ }, [manager, snapshot.status]);
154
+ return null;
155
+ }
156
+ function AuthCallback({ onComplete, fallback } = {}) {
157
+ const { manager } = useCtx();
158
+ useEffect(() => {
159
+ let cancelled = false;
160
+ void handleAuthCallback(manager).then((result) => {
161
+ if (cancelled) return;
162
+ if (onComplete) onComplete(result);
163
+ else if (typeof window !== "undefined") {
164
+ window.location.replace(result.returnTo || "/");
165
+ }
166
+ });
167
+ return () => {
168
+ cancelled = true;
169
+ };
170
+ }, [manager, onComplete]);
171
+ return createElement(Fragment, null, fallback ?? null);
172
+ }
173
+ function brandStyle(branding) {
174
+ if (!branding) return {};
175
+ const s = {};
176
+ if (branding.primaryColor) s["--brand-primary"] = branding.primaryColor;
177
+ if (branding.accentColor) s["--brand-accent"] = branding.accentColor;
178
+ if (branding.backgroundColor) s["--brand-bg"] = branding.backgroundColor;
179
+ if (branding.surfaceColor) s["--brand-surface"] = branding.surfaceColor;
180
+ if (branding.textColor) s["--brand-text"] = branding.textColor;
181
+ return s;
182
+ }
183
+ async function jsonFetch(url, init) {
184
+ const res = await fetch(url, { ...init, credentials: init?.credentials || "include" });
185
+ const payload = await res.json().catch(() => ({}));
186
+ if (!res.ok && payload?.success !== true) {
187
+ const message = payload?.error?.message || payload?.error_description || payload?.error || `HTTP ${res.status}`;
188
+ throw new Error(typeof message === "string" ? message : "Request failed");
189
+ }
190
+ return payload;
191
+ }
192
+ function useIQAuthSignInContext(iqAuthBaseUrl, appKey, returnTo) {
193
+ const [ctx, setCtx] = useState(null);
194
+ const [loading, setLoading] = useState(true);
195
+ const [error, setError] = useState(null);
196
+ useEffect(() => {
197
+ if (!appKey) {
198
+ setLoading(false);
199
+ setError("appKey is required");
200
+ return;
201
+ }
202
+ let cancelled = false;
203
+ setLoading(true);
204
+ const url = `${iqAuthBaseUrl.replace(/\/$/, "")}/api/public/apps/${encodeURIComponent(appKey)}/sign-in-context?return_to=${encodeURIComponent(returnTo)}`;
205
+ fetch(url).then((r) => r.json()).then((payload) => {
206
+ if (cancelled) return;
207
+ if (payload?.success === false) throw new Error(payload?.error?.message || "Failed to load sign-in context");
208
+ setCtx(payload.data);
209
+ }).catch((err) => {
210
+ if (!cancelled) setError(err.message);
211
+ }).finally(() => {
212
+ if (!cancelled) setLoading(false);
213
+ });
214
+ return () => {
215
+ cancelled = true;
216
+ };
217
+ }, [iqAuthBaseUrl, appKey, returnTo]);
218
+ return { ctx, loading, error };
219
+ }
220
+ function Shell({
221
+ branding,
222
+ className,
223
+ children
224
+ }) {
225
+ return /* @__PURE__ */ jsxs(
226
+ "div",
227
+ {
228
+ className,
229
+ style: {
230
+ background: "var(--brand-surface, #ffffff)",
231
+ color: "var(--brand-text, #0f172a)",
232
+ border: "1px solid rgba(15,23,42,0.08)",
233
+ borderRadius: 12,
234
+ padding: 24,
235
+ ...brandStyle(branding)
236
+ },
237
+ children: [
238
+ branding?.logoUrl ? /* @__PURE__ */ jsx("img", { src: branding.logoUrl, alt: branding.brandName || "", style: { height: 28, width: "auto", marginBottom: 16 } }) : null,
239
+ children
240
+ ]
241
+ }
242
+ );
243
+ }
244
+ function Field({ label, children }) {
245
+ return /* @__PURE__ */ jsxs("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 13 }, children: [
246
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: label }),
247
+ children
248
+ ] });
249
+ }
250
+ function inputStyle() {
251
+ return {
252
+ padding: "8px 12px",
253
+ border: "1px solid rgba(15,23,42,0.15)",
254
+ borderRadius: 6,
255
+ fontSize: 14,
256
+ width: "100%",
257
+ background: "transparent",
258
+ color: "inherit"
259
+ };
260
+ }
261
+ function PrimaryButton(props) {
262
+ return /* @__PURE__ */ jsx(
263
+ "button",
264
+ {
265
+ ...props,
266
+ style: {
267
+ background: "var(--brand-primary, #3b82f6)",
268
+ color: "#fff",
269
+ border: "none",
270
+ padding: "10px 16px",
271
+ borderRadius: 8,
272
+ fontSize: 14,
273
+ fontWeight: 500,
274
+ cursor: props.disabled ? "not-allowed" : "pointer",
275
+ opacity: props.disabled ? 0.6 : 1,
276
+ width: "100%",
277
+ ...props.style
278
+ }
279
+ }
280
+ );
281
+ }
282
+ function GhostButton(props) {
283
+ return /* @__PURE__ */ jsx(
284
+ "button",
285
+ {
286
+ ...props,
287
+ style: {
288
+ background: "transparent",
289
+ color: "inherit",
290
+ border: "1px solid rgba(15,23,42,0.15)",
291
+ padding: "10px 16px",
292
+ borderRadius: 8,
293
+ fontSize: 14,
294
+ fontWeight: 500,
295
+ cursor: props.disabled ? "not-allowed" : "pointer",
296
+ width: "100%",
297
+ ...props.style
298
+ }
299
+ }
300
+ );
301
+ }
302
+ function ErrorBanner({ message }) {
303
+ return /* @__PURE__ */ jsx("div", { role: "alert", style: {
304
+ borderLeft: "3px solid #dc2626",
305
+ background: "rgba(220,38,38,0.08)",
306
+ padding: "10px 14px",
307
+ borderRadius: "0 6px 6px 0",
308
+ fontSize: 13,
309
+ color: "#b91c1c"
310
+ }, children: message });
311
+ }
312
+ function SignIn({ iqAuthBaseUrl, appKey, returnTo, onRedirect, className }) {
313
+ const { ctx, loading, error } = useIQAuthSignInContext(iqAuthBaseUrl, appKey, returnTo);
314
+ const [email, setEmail] = useState("");
315
+ const [password, setPassword] = useState("");
316
+ const [submitting, setSubmitting] = useState(false);
317
+ const [formError, setFormError] = useState("");
318
+ const [mfa, setMfa] = useState(null);
319
+ const [tenantSel, setTenantSel] = useState(null);
320
+ const [oauthExchanging, setOauthExchanging] = useState(false);
321
+ const oidcPayload = () => ({
322
+ client_id: ctx?.app.defaultClientId,
323
+ redirect_uri: returnTo,
324
+ scope: "openid"
325
+ });
326
+ const handlePayload = (payload) => {
327
+ if (payload.type === "redirect" && payload.redirectUrl) {
328
+ (onRedirect || ((u) => {
329
+ window.location.href = u;
330
+ }))(payload.redirectUrl);
331
+ return true;
332
+ }
333
+ if (payload.type === "tenant_selection") {
334
+ setTenantSel({ token: payload.tenantSelectionToken, tenants: payload.tenants || [] });
335
+ return true;
336
+ }
337
+ if (payload.type === "mfa_required") {
338
+ const methods = payload.availableMethods || ["totp"];
339
+ setMfa({ token: payload.mfaChallengeToken, methods, selected: methods[0], code: "", backup: false });
340
+ return true;
341
+ }
342
+ return false;
343
+ };
344
+ const submitLogin = async (e) => {
345
+ e.preventDefault();
346
+ if (!ctx?.app.defaultClientId) {
347
+ setFormError("Application is not configured for hosted sign-in.");
348
+ return;
349
+ }
350
+ setSubmitting(true);
351
+ setFormError("");
352
+ try {
353
+ const r = await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/oidc/sso-login`, {
354
+ method: "POST",
355
+ headers: { "Content-Type": "application/json" },
356
+ credentials: "include",
357
+ body: JSON.stringify({ email, password, ...oidcPayload() })
358
+ });
359
+ const payload = await r.json().catch(() => ({}));
360
+ if (!handlePayload(payload)) setFormError(payload.error_description || payload.error || "Sign-in failed");
361
+ } catch (err) {
362
+ setFormError(err.message || "Network error");
363
+ }
364
+ setSubmitting(false);
365
+ };
366
+ const submitMfa = async (e) => {
367
+ e.preventDefault();
368
+ if (!mfa) return;
369
+ setSubmitting(true);
370
+ setFormError("");
371
+ const r = await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/oidc/sso-mfa-complete`, {
372
+ method: "POST",
373
+ headers: { "Content-Type": "application/json" },
374
+ credentials: "include",
375
+ body: JSON.stringify({
376
+ mfaChallengeToken: mfa.token,
377
+ code: mfa.code,
378
+ method: mfa.selected,
379
+ useBackup: mfa.backup,
380
+ ...oidcPayload()
381
+ })
382
+ });
383
+ const payload = await r.json().catch(() => ({}));
384
+ if (!handlePayload(payload)) setFormError(payload.error_description || payload.error || "MFA verification failed");
385
+ setSubmitting(false);
386
+ };
387
+ const submitTenant = async (tenantId) => {
388
+ if (!tenantSel) return;
389
+ setSubmitting(true);
390
+ setFormError("");
391
+ const r = await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/oidc/sso-tenant-select`, {
392
+ method: "POST",
393
+ headers: { "Content-Type": "application/json" },
394
+ credentials: "include",
395
+ body: JSON.stringify({ tenantSelectionToken: tenantSel.token, tenantId, ...oidcPayload() })
396
+ });
397
+ const payload = await r.json().catch(() => ({}));
398
+ if (!handlePayload(payload)) setFormError(payload.error_description || payload.error || "Tenant selection failed");
399
+ setSubmitting(false);
400
+ };
401
+ const startGoogleLogin = () => {
402
+ if (!ctx?.app.defaultClientId) {
403
+ setFormError("Application is not configured for hosted sign-in.");
404
+ return;
405
+ }
406
+ const bridgeUrl = window.location.href;
407
+ const url = `${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/google?redirect_uri=${encodeURIComponent(bridgeUrl)}&client_id=${encodeURIComponent(ctx.app.defaultClientId)}`;
408
+ window.location.href = url;
409
+ };
410
+ useEffect(() => {
411
+ if (!ctx?.app.defaultClientId) return;
412
+ const params = new URLSearchParams(window.location.search);
413
+ const oauthCode = params.get("code");
414
+ if (!oauthCode) return;
415
+ const u = new URL(window.location.href);
416
+ u.searchParams.delete("code");
417
+ const codeRedirectUri = u.toString();
418
+ setOauthExchanging(true);
419
+ setFormError("");
420
+ (async () => {
421
+ try {
422
+ const r = await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/oidc/sso-complete-oauth`, {
423
+ method: "POST",
424
+ headers: { "Content-Type": "application/json" },
425
+ credentials: "include",
426
+ body: JSON.stringify({
427
+ authCode: oauthCode,
428
+ code_redirect_uri: codeRedirectUri,
429
+ ...oidcPayload()
430
+ })
431
+ });
432
+ const payload = await r.json().catch(() => ({}));
433
+ try {
434
+ window.history.replaceState({}, "", u.pathname + (u.search ? u.search : "") + u.hash);
435
+ } catch {
436
+ }
437
+ if (!handlePayload(payload)) setFormError(payload.error_description || payload.error || "Authorization code exchange failed");
438
+ } catch (err) {
439
+ setFormError(err.message || "Authorization code exchange failed");
440
+ }
441
+ setOauthExchanging(false);
442
+ })();
443
+ }, [ctx?.app.defaultClientId]);
444
+ if (loading || oauthExchanging) return /* @__PURE__ */ jsx(Shell, { branding: ctx?.branding || null, className, children: /* @__PURE__ */ jsx("p", { children: oauthExchanging ? "Completing sign-in\u2026" : "Loading\u2026" }) });
445
+ if (error || !ctx) return /* @__PURE__ */ jsx(Shell, { branding: null, className, children: /* @__PURE__ */ jsx(ErrorBanner, { message: error || "Failed to load app context" }) });
446
+ if (!ctx.returnAllowed) return /* @__PURE__ */ jsx(Shell, { branding: ctx.branding, className, children: /* @__PURE__ */ jsx(ErrorBanner, { message: `returnTo "${returnTo}" is not in this app's allowed origins.` }) });
447
+ return /* @__PURE__ */ jsxs(Shell, { branding: ctx.branding, className, children: [
448
+ /* @__PURE__ */ jsx("h2", { style: { fontSize: 20, fontWeight: 600, margin: "0 0 12px" }, children: ctx.branding?.loginHeadline || `Sign in to ${ctx.app.name}` }),
449
+ ctx.branding?.loginSubheadline ? /* @__PURE__ */ jsx("p", { style: { marginBottom: 16, fontSize: 13, opacity: 0.7 }, children: ctx.branding.loginSubheadline }) : null,
450
+ formError ? /* @__PURE__ */ jsx("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx(ErrorBanner, { message: formError }) }) : null,
451
+ tenantSel ? /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Choose tenant", style: { display: "flex", flexDirection: "column", gap: 8 }, children: tenantSel.tenants.map((t) => /* @__PURE__ */ jsxs(
452
+ "button",
453
+ {
454
+ type: "button",
455
+ "data-iqauth-tenant": t.tenantId,
456
+ onClick: () => submitTenant(t.tenantId),
457
+ style: { textAlign: "left", padding: "10px 14px", border: "1px solid rgba(15,23,42,0.15)", borderRadius: 8, background: "transparent", color: "inherit", cursor: "pointer" },
458
+ children: [
459
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontWeight: 500 }, children: t.tenantName || t.tenantSlug || t.tenantId }),
460
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: 12, opacity: 0.6 }, children: t.roles.join(", ") })
461
+ ]
462
+ },
463
+ t.tenantId
464
+ )) }) : mfa ? /* @__PURE__ */ jsxs("form", { onSubmit: submitMfa, style: { display: "flex", flexDirection: "column", gap: 12 }, "aria-label": "MFA verification", children: [
465
+ !mfa.backup && mfa.methods.length > 1 ? /* @__PURE__ */ jsx(Field, { label: "Method", children: /* @__PURE__ */ jsx("select", { style: inputStyle(), value: mfa.selected, onChange: (e) => setMfa({ ...mfa, selected: e.target.value }), children: mfa.methods.map((m) => /* @__PURE__ */ jsx("option", { value: m, children: m.toUpperCase() }, m)) }) }) : null,
466
+ /* @__PURE__ */ jsx(Field, { label: mfa.backup ? "Backup code" : "Verification code", children: /* @__PURE__ */ jsx(
467
+ "input",
468
+ {
469
+ style: { ...inputStyle(), fontFamily: "monospace", textAlign: mfa.backup ? "left" : "center", letterSpacing: mfa.backup ? "0.04em" : "0.3em" },
470
+ value: mfa.code,
471
+ onChange: (e) => setMfa({ ...mfa, code: e.target.value }),
472
+ autoComplete: "one-time-code",
473
+ inputMode: mfa.backup ? "text" : "numeric"
474
+ }
475
+ ) }),
476
+ /* @__PURE__ */ jsx(PrimaryButton, { type: "submit", disabled: submitting || !mfa.code, children: submitting ? "Verifying\u2026" : "Verify" }),
477
+ /* @__PURE__ */ jsx(GhostButton, { type: "button", onClick: () => setMfa({ ...mfa, backup: !mfa.backup, code: "" }), children: mfa.backup ? "Use verification code" : "Use backup code" })
478
+ ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
479
+ ctx.providers?.google ? /* @__PURE__ */ jsxs(Fragment2, { children: [
480
+ /* @__PURE__ */ jsxs(GhostButton, { type: "button", onClick: startGoogleLogin, disabled: submitting, "aria-label": "Continue with Google", style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 10 }, children: [
481
+ /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", "aria-hidden": "true", children: [
482
+ /* @__PURE__ */ jsx("path", { fill: "#4285F4", d: "M17.64 9.2c0-.64-.06-1.25-.17-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.71v2.26h2.92a8.78 8.78 0 0 0 2.68-6.61z" }),
483
+ /* @__PURE__ */ jsx("path", { fill: "#34A853", d: "M9 18c2.43 0 4.47-.81 5.96-2.18l-2.92-2.26a5.4 5.4 0 0 1-8.04-2.83H.96v2.33A9 9 0 0 0 9 18z" }),
484
+ /* @__PURE__ */ jsx("path", { fill: "#FBBC05", d: "M3.96 10.71A5.41 5.41 0 0 1 3.68 9c0-.59.1-1.17.29-1.71V4.96H.96a9 9 0 0 0 0 8.08l3-2.33z" }),
485
+ /* @__PURE__ */ jsx("path", { fill: "#EA4335", d: "M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.59A9 9 0 0 0 .96 4.96l3 2.33A5.4 5.4 0 0 1 9 3.58z" })
486
+ ] }),
487
+ "Continue with Google"
488
+ ] }),
489
+ /* @__PURE__ */ jsxs("div", { role: "separator", "aria-label": "or", style: { display: "flex", alignItems: "center", gap: 10, fontSize: 12, opacity: 0.6 }, children: [
490
+ /* @__PURE__ */ jsx("span", { style: { flex: 1, height: 1, background: "rgba(15,23,42,0.12)" } }),
491
+ "OR",
492
+ /* @__PURE__ */ jsx("span", { style: { flex: 1, height: 1, background: "rgba(15,23,42,0.12)" } })
493
+ ] })
494
+ ] }) : null,
495
+ /* @__PURE__ */ jsxs("form", { onSubmit: submitLogin, style: { display: "flex", flexDirection: "column", gap: 12 }, "aria-label": `Sign in to ${ctx.app.name}`, children: [
496
+ /* @__PURE__ */ jsx(Field, { label: "Email", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "email", autoComplete: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value) }) }),
497
+ /* @__PURE__ */ jsx(Field, { label: "Password", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "password", autoComplete: "current-password", required: true, value: password, onChange: (e) => setPassword(e.target.value) }) }),
498
+ /* @__PURE__ */ jsx(PrimaryButton, { type: "submit", disabled: submitting || !email || !password, children: submitting ? "Signing in\u2026" : "Sign in" })
499
+ ] })
500
+ ] })
501
+ ] });
502
+ }
503
+ function SignUp({ iqAuthBaseUrl, appKey, returnTo, onSuccess, className }) {
504
+ const { ctx, loading } = useIQAuthSignInContext(iqAuthBaseUrl, appKey, returnTo || "");
505
+ const [name, setName] = useState("");
506
+ const [email, setEmail] = useState("");
507
+ const [password, setPassword] = useState("");
508
+ const [organizationName, setOrganizationName] = useState("");
509
+ const [submitting, setSubmitting] = useState(false);
510
+ const [error, setError] = useState("");
511
+ const [done, setDone] = useState(false);
512
+ const submit = async (e) => {
513
+ e.preventDefault();
514
+ setSubmitting(true);
515
+ setError("");
516
+ try {
517
+ await jsonFetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/signup`, {
518
+ method: "POST",
519
+ headers: { "Content-Type": "application/json" },
520
+ body: JSON.stringify({ email, name, password, organizationName: organizationName || void 0 })
521
+ });
522
+ setDone(true);
523
+ onSuccess?.();
524
+ } catch (err) {
525
+ setError(err.message);
526
+ }
527
+ setSubmitting(false);
528
+ };
529
+ if (loading) return /* @__PURE__ */ jsx(Shell, { branding: null, className, children: /* @__PURE__ */ jsx("p", { children: "Loading\u2026" }) });
530
+ return /* @__PURE__ */ jsxs(Shell, { branding: ctx?.branding || null, className, children: [
531
+ /* @__PURE__ */ jsx("h2", { style: { fontSize: 20, fontWeight: 600, margin: "0 0 12px" }, children: "Create your account" }),
532
+ done ? /* @__PURE__ */ jsx("div", { role: "status", children: /* @__PURE__ */ jsx("p", { children: "Account created. Check your email for verification." }) }) : /* @__PURE__ */ jsxs("form", { onSubmit: submit, style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
533
+ error ? /* @__PURE__ */ jsx(ErrorBanner, { message: error }) : null,
534
+ /* @__PURE__ */ jsx(Field, { label: "Full name", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), value: name, onChange: (e) => setName(e.target.value), required: true }) }),
535
+ /* @__PURE__ */ jsx(Field, { label: "Email", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "email", autoComplete: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true }) }),
536
+ /* @__PURE__ */ jsx(Field, { label: "Organization (optional)", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), value: organizationName, onChange: (e) => setOrganizationName(e.target.value) }) }),
537
+ /* @__PURE__ */ jsx(Field, { label: "Password", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "password", autoComplete: "new-password", minLength: 8, value: password, onChange: (e) => setPassword(e.target.value), required: true }) }),
538
+ /* @__PURE__ */ jsx(PrimaryButton, { type: "submit", disabled: submitting || !email || !password || !name, children: submitting ? "Creating\u2026" : "Create account" })
539
+ ] })
540
+ ] });
541
+ }
542
+ function initialsOf(name, email) {
543
+ const src = name || email || "?";
544
+ const parts = src.split(/[\s@]+/).filter(Boolean);
545
+ if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
546
+ return src.substring(0, 2).toUpperCase();
547
+ }
548
+ function UserButton({ iqAuthBaseUrl, accountUrl, onSignOut, className }) {
549
+ const [user, setUser] = useState(null);
550
+ const [open, setOpen] = useState(false);
551
+ useEffect(() => {
552
+ let cancelled = false;
553
+ fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/me`, { credentials: "include" }).then((r) => r.json()).then((p) => {
554
+ if (!cancelled && p?.data) setUser(p.data);
555
+ }).catch(() => {
556
+ });
557
+ return () => {
558
+ cancelled = true;
559
+ };
560
+ }, [iqAuthBaseUrl]);
561
+ const signOut2 = async () => {
562
+ try {
563
+ await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/logout`, { method: "POST", credentials: "include" });
564
+ } catch {
565
+ }
566
+ if (onSignOut) onSignOut();
567
+ else window.location.reload();
568
+ };
569
+ if (!user) return null;
570
+ const target = accountUrl || `${iqAuthBaseUrl.replace(/\/$/, "")}/account`;
571
+ return /* @__PURE__ */ jsxs("div", { className, style: { position: "relative", display: "inline-block" }, children: [
572
+ /* @__PURE__ */ jsx(
573
+ "button",
574
+ {
575
+ type: "button",
576
+ "aria-haspopup": "menu",
577
+ "aria-expanded": open,
578
+ onClick: () => setOpen((o) => !o),
579
+ style: {
580
+ width: 32,
581
+ height: 32,
582
+ borderRadius: "50%",
583
+ background: "var(--brand-accent, #6366f1)",
584
+ color: "#fff",
585
+ border: "none",
586
+ cursor: "pointer",
587
+ fontSize: 12,
588
+ fontWeight: 600
589
+ },
590
+ children: user.picture ? /* @__PURE__ */ jsx("img", { src: user.picture, alt: user.name, style: { width: "100%", height: "100%", borderRadius: "50%" } }) : initialsOf(user.name, user.email)
591
+ }
592
+ ),
593
+ open ? /* @__PURE__ */ jsxs("div", { role: "menu", style: {
594
+ position: "absolute",
595
+ right: 0,
596
+ top: 40,
597
+ minWidth: 200,
598
+ background: "#fff",
599
+ border: "1px solid rgba(15,23,42,0.12)",
600
+ borderRadius: 8,
601
+ boxShadow: "0 4px 12px rgba(0,0,0,0.08)",
602
+ padding: 8,
603
+ zIndex: 100
604
+ }, children: [
605
+ /* @__PURE__ */ jsxs("div", { style: { padding: "8px 10px", fontSize: 12, opacity: 0.7, borderBottom: "1px solid rgba(15,23,42,0.06)" }, children: [
606
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 500, color: "#0f172a" }, children: user.name }),
607
+ /* @__PURE__ */ jsx("div", { children: user.email })
608
+ ] }),
609
+ /* @__PURE__ */ jsx("a", { href: target, role: "menuitem", style: { display: "block", padding: "8px 10px", fontSize: 13, color: "#0f172a", textDecoration: "none" }, children: "Account" }),
610
+ /* @__PURE__ */ jsx(
611
+ "button",
612
+ {
613
+ role: "menuitem",
614
+ type: "button",
615
+ onClick: signOut2,
616
+ style: { display: "block", width: "100%", textAlign: "left", padding: "8px 10px", fontSize: 13, background: "transparent", border: "none", cursor: "pointer", color: "#b91c1c" },
617
+ children: "Sign out"
618
+ }
619
+ )
620
+ ] }) : null
621
+ ] });
622
+ }
623
+ function UserProfile({ iqAuthBaseUrl, className }) {
624
+ const [user, setUser] = useState(null);
625
+ const [oldPassword, setOldPassword] = useState("");
626
+ const [newPassword, setNewPassword] = useState("");
627
+ const [pwState, setPwState] = useState({ submitting: false, message: "", error: "" });
628
+ const [sessions, setSessions] = useState([]);
629
+ useEffect(() => {
630
+ fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/me`, { credentials: "include" }).then((r) => r.json()).then((p) => {
631
+ if (p?.data) setUser(p.data);
632
+ }).catch(() => {
633
+ });
634
+ fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/sessions`, { credentials: "include" }).then((r) => r.json()).then((p) => setSessions(p?.data?.sessions || p?.data || [])).catch(() => {
635
+ });
636
+ }, [iqAuthBaseUrl]);
637
+ const changePassword = async (e) => {
638
+ e.preventDefault();
639
+ setPwState({ submitting: true, message: "", error: "" });
640
+ try {
641
+ await jsonFetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/password/change`, {
642
+ method: "POST",
643
+ headers: { "Content-Type": "application/json" },
644
+ body: JSON.stringify({ oldPassword, newPassword })
645
+ });
646
+ setPwState({ submitting: false, message: "Password updated.", error: "" });
647
+ setOldPassword("");
648
+ setNewPassword("");
649
+ } catch (err) {
650
+ setPwState({ submitting: false, message: "", error: err.message });
651
+ }
652
+ };
653
+ const revoke = async (sessionId) => {
654
+ await fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/sessions/${sessionId}`, { method: "DELETE", credentials: "include" });
655
+ setSessions((prev) => prev.filter((s) => s.id !== sessionId));
656
+ };
657
+ if (!user) return /* @__PURE__ */ jsx(Shell, { branding: null, className, children: /* @__PURE__ */ jsx("p", { children: "Loading account\u2026" }) });
658
+ return /* @__PURE__ */ jsxs(Shell, { branding: null, className, children: [
659
+ /* @__PURE__ */ jsx("h2", { style: { fontSize: 20, fontWeight: 600, margin: "0 0 12px" }, children: "Your account" }),
660
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "iqauth-profile", style: { marginBottom: 20 }, children: [
661
+ /* @__PURE__ */ jsx("h3", { id: "iqauth-profile", style: { fontSize: 14, fontWeight: 600 }, children: "Profile" }),
662
+ /* @__PURE__ */ jsxs("p", { style: { fontSize: 13, margin: "4px 0" }, children: [
663
+ /* @__PURE__ */ jsx("strong", { children: "Name:" }),
664
+ " ",
665
+ user.name
666
+ ] }),
667
+ /* @__PURE__ */ jsxs("p", { style: { fontSize: 13, margin: "4px 0" }, children: [
668
+ /* @__PURE__ */ jsx("strong", { children: "Email:" }),
669
+ " ",
670
+ user.email
671
+ ] })
672
+ ] }),
673
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "iqauth-pw", style: { marginBottom: 20 }, children: [
674
+ /* @__PURE__ */ jsx("h3", { id: "iqauth-pw", style: { fontSize: 14, fontWeight: 600 }, children: "Change password" }),
675
+ pwState.error ? /* @__PURE__ */ jsx(ErrorBanner, { message: pwState.error }) : null,
676
+ pwState.message ? /* @__PURE__ */ jsx("div", { role: "status", style: { fontSize: 13, color: "#047857" }, children: pwState.message }) : null,
677
+ /* @__PURE__ */ jsxs("form", { onSubmit: changePassword, style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
678
+ /* @__PURE__ */ jsx(Field, { label: "Current password", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "password", autoComplete: "current-password", value: oldPassword, onChange: (e) => setOldPassword(e.target.value), required: true }) }),
679
+ /* @__PURE__ */ jsx(Field, { label: "New password", children: /* @__PURE__ */ jsx("input", { style: inputStyle(), type: "password", autoComplete: "new-password", minLength: 8, value: newPassword, onChange: (e) => setNewPassword(e.target.value), required: true }) }),
680
+ /* @__PURE__ */ jsx(PrimaryButton, { type: "submit", disabled: pwState.submitting || !oldPassword || !newPassword, children: pwState.submitting ? "Updating\u2026" : "Change password" })
681
+ ] })
682
+ ] }),
683
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "iqauth-sessions", children: [
684
+ /* @__PURE__ */ jsx("h3", { id: "iqauth-sessions", style: { fontSize: 14, fontWeight: 600 }, children: "Sessions" }),
685
+ sessions.length === 0 ? /* @__PURE__ */ jsx("p", { style: { fontSize: 13, opacity: 0.6 }, children: "No active sessions." }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 6 }, children: sessions.map((s) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", justifyContent: "space-between", fontSize: 13, padding: "6px 10px", background: "#f8fafc", borderRadius: 6 }, children: [
686
+ /* @__PURE__ */ jsx("span", { children: s.userAgent || s.deviceName || "Unknown" }),
687
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => revoke(s.id), style: { fontSize: 12, color: "#b91c1c", background: "transparent", border: "1px solid #fecaca", borderRadius: 4, padding: "2px 8px", cursor: "pointer" }, children: "Revoke" })
688
+ ] }, s.id)) })
689
+ ] })
690
+ ] });
691
+ }
692
+ function OrganizationSwitcher({ iqAuthBaseUrl, onSwitched, className }) {
693
+ const [memberships, setMemberships] = useState([]);
694
+ const [activeTenantId, setActiveTenantId] = useState(null);
695
+ const [open, setOpen] = useState(false);
696
+ useEffect(() => {
697
+ fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/me`, { credentials: "include" }).then((r) => r.json()).then((p) => {
698
+ if (p?.data?.tenantId) setActiveTenantId(p.data.tenantId);
699
+ }).catch(() => {
700
+ });
701
+ fetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/tenants/memberships`, { credentials: "include" }).then((r) => r.json()).then((p) => setMemberships(p?.data?.memberships || p?.data || [])).catch(() => {
702
+ });
703
+ }, [iqAuthBaseUrl]);
704
+ const switchTo = async (tenantId) => {
705
+ try {
706
+ await jsonFetch(`${iqAuthBaseUrl.replace(/\/$/, "")}/api/v1/auth/select-tenant`, {
707
+ method: "POST",
708
+ headers: { "Content-Type": "application/json" },
709
+ body: JSON.stringify({ tenantId })
710
+ });
711
+ setActiveTenantId(tenantId);
712
+ setOpen(false);
713
+ onSwitched?.(tenantId);
714
+ } catch {
715
+ }
716
+ };
717
+ const active = memberships.find((m) => m.tenantId === activeTenantId);
718
+ return /* @__PURE__ */ jsxs("div", { className, style: { position: "relative", display: "inline-block" }, children: [
719
+ /* @__PURE__ */ jsx(
720
+ "button",
721
+ {
722
+ type: "button",
723
+ "aria-haspopup": "menu",
724
+ "aria-expanded": open,
725
+ onClick: () => setOpen((o) => !o),
726
+ style: { background: "transparent", border: "1px solid rgba(15,23,42,0.15)", padding: "6px 12px", borderRadius: 6, cursor: "pointer", fontSize: 13 },
727
+ children: active?.tenantName || active?.tenantSlug || "Select organization"
728
+ }
729
+ ),
730
+ open ? /* @__PURE__ */ jsx("div", { role: "menu", style: {
731
+ position: "absolute",
732
+ left: 0,
733
+ top: 36,
734
+ minWidth: 220,
735
+ background: "#fff",
736
+ border: "1px solid rgba(15,23,42,0.12)",
737
+ borderRadius: 8,
738
+ boxShadow: "0 4px 12px rgba(0,0,0,0.08)",
739
+ padding: 8,
740
+ zIndex: 100
741
+ }, children: memberships.length === 0 ? /* @__PURE__ */ jsx("p", { style: { fontSize: 13, opacity: 0.6, padding: "4px 6px" }, children: "No memberships" }) : memberships.map((m) => /* @__PURE__ */ jsxs(
742
+ "button",
743
+ {
744
+ role: "menuitem",
745
+ type: "button",
746
+ onClick: () => switchTo(m.tenantId),
747
+ style: {
748
+ display: "block",
749
+ width: "100%",
750
+ textAlign: "left",
751
+ padding: "8px 10px",
752
+ background: m.tenantId === activeTenantId ? "rgba(99,102,241,0.08)" : "transparent",
753
+ border: "none",
754
+ borderRadius: 4,
755
+ cursor: "pointer",
756
+ fontSize: 13,
757
+ color: "#0f172a"
758
+ },
759
+ children: [
760
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 500 }, children: m.tenantName || m.tenantSlug || m.tenantId }),
761
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, opacity: 0.6 }, children: m.roles.join(", ") })
762
+ ]
763
+ },
764
+ m.tenantId
765
+ )) }) : null
766
+ ] });
767
+ }
768
+ var __version__ = "phase-bc-1.0.0";
769
+ export {
770
+ AuthCallback,
771
+ IQAuthProvider,
772
+ OrganizationSwitcher,
773
+ RedirectToSignIn,
774
+ SignIn,
775
+ SignUp,
776
+ SignedIn,
777
+ SignedOut,
778
+ UserButton,
779
+ UserProfile,
780
+ __version__,
781
+ useAuth,
782
+ useAuthFetch,
783
+ useIQAuthSignInContext,
784
+ useOrganization,
785
+ useSession,
786
+ useUser
787
+ };