@ollaid/native-sso 2.7.8 → 2.7.9

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.
@@ -11,7 +11,8 @@ interface DebugPanelProps {
11
11
  onOpenLogin?: () => void;
12
12
  onOpenSignup?: () => void;
13
13
  onOpenOnboarding?: (preset: DebugOnboardingPreset) => void;
14
+ onOpenPassword?: () => void;
14
15
  onResetProfilePrompt?: () => void;
15
16
  }
16
- export declare function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }: DebugPanelProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onOpenPassword, onResetProfilePrompt }: DebugPanelProps): import("react/jsx-runtime").JSX.Element;
17
18
  export default DebugPanel;
@@ -0,0 +1,12 @@
1
+ interface PasswordRedirectModalProps {
2
+ open: boolean;
3
+ onOpenChange: (open: boolean) => void;
4
+ saasApiUrl?: string;
5
+ /**
6
+ * @deprecated Gardé pour compatibilité ascendante.
7
+ * Préférer `saasApiUrl` car le flux mot de passe passe désormais par le backend SaaS.
8
+ */
9
+ iamApiUrl?: string;
10
+ }
11
+ declare const PasswordRedirectModal: ({ open, onOpenChange, saasApiUrl, iamApiUrl }: PasswordRedirectModalProps) => import("react/jsx-runtime").JSX.Element;
12
+ export default PasswordRedirectModal;
package/dist/index.cjs CHANGED
@@ -11243,7 +11243,7 @@ function SuccessOrbit() {
11243
11243
  justifyContent: "center",
11244
11244
  zIndex: 2
11245
11245
  }, children: /* @__PURE__ */ jsxRuntime.jsx(IconLink, { style: { width: "1.7rem", height: "1.7rem", color: C$1.green } }) }),
11246
- orbitIcons.map((Icon, i) => {
11246
+ orbitIcons.map((Icon2, i) => {
11247
11247
  const angle = i * 360 / orbitIcons.length;
11248
11248
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
11249
11249
  position: "absolute",
@@ -11262,7 +11262,7 @@ function SuccessOrbit() {
11262
11262
  display: "flex",
11263
11263
  alignItems: "center",
11264
11264
  justifyContent: "center"
11265
- }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { style: { width: "1rem", height: "1rem", color: C$1.gray500 } }) }) }, i);
11265
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { style: { width: "1rem", height: "1rem", color: C$1.gray500 } }) }) }, i);
11266
11266
  })
11267
11267
  ] });
11268
11268
  }
@@ -13300,7 +13300,248 @@ function OnboardingModal({
13300
13300
  renderConfirmDialog()
13301
13301
  ] });
13302
13302
  }
13303
- function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }) {
13303
+ /**
13304
+ * @license lucide-react v0.462.0 - ISC
13305
+ *
13306
+ * This source code is licensed under the ISC license.
13307
+ * See the LICENSE file in the root directory of this source tree.
13308
+ */
13309
+ const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
13310
+ const mergeClasses = (...classes) => classes.filter((className, index, array) => {
13311
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
13312
+ }).join(" ").trim();
13313
+ /**
13314
+ * @license lucide-react v0.462.0 - ISC
13315
+ *
13316
+ * This source code is licensed under the ISC license.
13317
+ * See the LICENSE file in the root directory of this source tree.
13318
+ */
13319
+ var defaultAttributes = {
13320
+ xmlns: "http://www.w3.org/2000/svg",
13321
+ width: 24,
13322
+ height: 24,
13323
+ viewBox: "0 0 24 24",
13324
+ fill: "none",
13325
+ stroke: "currentColor",
13326
+ strokeWidth: 2,
13327
+ strokeLinecap: "round",
13328
+ strokeLinejoin: "round"
13329
+ };
13330
+ /**
13331
+ * @license lucide-react v0.462.0 - ISC
13332
+ *
13333
+ * This source code is licensed under the ISC license.
13334
+ * See the LICENSE file in the root directory of this source tree.
13335
+ */
13336
+ const Icon = react.forwardRef(
13337
+ ({
13338
+ color = "currentColor",
13339
+ size = 24,
13340
+ strokeWidth = 2,
13341
+ absoluteStrokeWidth,
13342
+ className = "",
13343
+ children,
13344
+ iconNode,
13345
+ ...rest
13346
+ }, ref) => {
13347
+ return react.createElement(
13348
+ "svg",
13349
+ {
13350
+ ref,
13351
+ ...defaultAttributes,
13352
+ width: size,
13353
+ height: size,
13354
+ stroke: color,
13355
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
13356
+ className: mergeClasses("lucide", className),
13357
+ ...rest
13358
+ },
13359
+ [
13360
+ ...iconNode.map(([tag, attrs]) => react.createElement(tag, attrs)),
13361
+ ...Array.isArray(children) ? children : [children]
13362
+ ]
13363
+ );
13364
+ }
13365
+ );
13366
+ /**
13367
+ * @license lucide-react v0.462.0 - ISC
13368
+ *
13369
+ * This source code is licensed under the ISC license.
13370
+ * See the LICENSE file in the root directory of this source tree.
13371
+ */
13372
+ const createLucideIcon = (iconName, iconNode) => {
13373
+ const Component = react.forwardRef(
13374
+ ({ className, ...props }, ref) => react.createElement(Icon, {
13375
+ ref,
13376
+ iconNode,
13377
+ className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
13378
+ ...props
13379
+ })
13380
+ );
13381
+ Component.displayName = `${iconName}`;
13382
+ return Component;
13383
+ };
13384
+ /**
13385
+ * @license lucide-react v0.462.0 - ISC
13386
+ *
13387
+ * This source code is licensed under the ISC license.
13388
+ * See the LICENSE file in the root directory of this source tree.
13389
+ */
13390
+ const ExternalLink = createLucideIcon("ExternalLink", [
13391
+ ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
13392
+ ["path", { d: "M10 14 21 3", key: "gplh6r" }],
13393
+ ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", key: "a6xqqp" }]
13394
+ ]);
13395
+ /**
13396
+ * @license lucide-react v0.462.0 - ISC
13397
+ *
13398
+ * This source code is licensed under the ISC license.
13399
+ * See the LICENSE file in the root directory of this source tree.
13400
+ */
13401
+ const LoaderCircle = createLucideIcon("LoaderCircle", [
13402
+ ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
13403
+ ]);
13404
+ /**
13405
+ * @license lucide-react v0.462.0 - ISC
13406
+ *
13407
+ * This source code is licensed under the ISC license.
13408
+ * See the LICENSE file in the root directory of this source tree.
13409
+ */
13410
+ const LockKeyhole = createLucideIcon("LockKeyhole", [
13411
+ ["circle", { cx: "12", cy: "16", r: "1", key: "1au0dj" }],
13412
+ ["rect", { x: "3", y: "10", width: "18", height: "12", rx: "2", key: "6s8ecr" }],
13413
+ ["path", { d: "M7 10V7a5 5 0 0 1 10 0v3", key: "1pqi11" }]
13414
+ ]);
13415
+ /**
13416
+ * @license lucide-react v0.462.0 - ISC
13417
+ *
13418
+ * This source code is licensed under the ISC license.
13419
+ * See the LICENSE file in the root directory of this source tree.
13420
+ */
13421
+ const ShieldCheck = createLucideIcon("ShieldCheck", [
13422
+ [
13423
+ "path",
13424
+ {
13425
+ d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
13426
+ key: "oel41y"
13427
+ }
13428
+ ],
13429
+ ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
13430
+ ]);
13431
+ const PasswordRedirectModal = ({ open, onOpenChange, saasApiUrl, iamApiUrl }) => {
13432
+ const [confirmOpen, setConfirmOpen] = react.useState(false);
13433
+ const [loading, setLoading] = react.useState(false);
13434
+ const [errorMessage, setErrorMessage] = react.useState(null);
13435
+ const apiBaseUrl = react.useMemo(() => {
13436
+ const source = saasApiUrl || iamApiUrl;
13437
+ if (!source) {
13438
+ return "";
13439
+ }
13440
+ const normalized = source.replace(/\/$/, "");
13441
+ return normalized.endsWith("/api") ? normalized : `${normalized}/api`;
13442
+ }, [saasApiUrl, iamApiUrl]);
13443
+ const buildUrl = (redirectUrl, magicToken) => {
13444
+ if (redirectUrl) {
13445
+ return redirectUrl;
13446
+ }
13447
+ if (magicToken) {
13448
+ const fallbackUrl = new URL("/auth/auto-connect", "https://iam.ollaid.com");
13449
+ fallbackUrl.searchParams.set("magic_token", magicToken);
13450
+ return fallbackUrl.toString();
13451
+ }
13452
+ return "";
13453
+ };
13454
+ const handleRedirect = async () => {
13455
+ if (loading) return;
13456
+ setLoading(true);
13457
+ setErrorMessage(null);
13458
+ try {
13459
+ const authToken = getAuthToken();
13460
+ const appAccessTokenRef = getNativeStorage().getItem(STORAGE.APP_ACCESS_TOKEN_REF);
13461
+ if (!authToken && !appAccessTokenRef) {
13462
+ throw new Error("Session Native SSO introuvable");
13463
+ }
13464
+ if (!apiBaseUrl) {
13465
+ throw new Error("saasApiUrl non configurée");
13466
+ }
13467
+ const result = await fetchWithTimeout(
13468
+ `${apiBaseUrl}/native/password-link`,
13469
+ {
13470
+ method: "POST",
13471
+ headers: getHeaders(authToken || void 0, true)
13472
+ },
13473
+ 2e4
13474
+ );
13475
+ if (!result.success) {
13476
+ throw new Error(result.message || "Impossible de générer le lien");
13477
+ }
13478
+ const redirectUrl = buildUrl(
13479
+ typeof result.redirect_url === "string" && result.redirect_url.length > 0 ? result.redirect_url : typeof result.sso_url === "string" && result.sso_url.length > 0 ? result.sso_url : "",
13480
+ result.magic_token
13481
+ );
13482
+ if (!redirectUrl) {
13483
+ throw new Error("URL de redirection manquante");
13484
+ }
13485
+ window.open(redirectUrl, "_blank", "noopener,noreferrer");
13486
+ onOpenChange(false);
13487
+ setConfirmOpen(false);
13488
+ } catch (error) {
13489
+ const message = error instanceof ApiError ? error.message : (error == null ? void 0 : error.message) || "Impossible de lancer le flux de mot de passe";
13490
+ setErrorMessage(message);
13491
+ } finally {
13492
+ setLoading(false);
13493
+ }
13494
+ };
13495
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13496
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { style: { maxWidth: "28rem", borderRadius: "16px", background: "#fff" }, children: [
13497
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
13498
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { margin: "0 auto 12px", width: 56, height: 56, borderRadius: "50%", background: "rgba(232, 67, 10, 0.1)", display: "flex", alignItems: "center", justifyContent: "center", color: "#e8430a" }, children: /* @__PURE__ */ jsxRuntime.jsx(LockKeyhole, { size: 28 }) }),
13499
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { style: { textAlign: "center" }, children: "Mot de passe géré par OLLAID SSO" }),
13500
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { style: { textAlign: "center" }, children: "Ce bouton sert à tester la redirection vers IAM et l'ouverture du modal de changement de mot de passe." })
13501
+ ] }),
13502
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid #e5e7eb", borderRadius: 12, padding: 16, background: "#f8fafc", color: "#475569", fontSize: 13, display: "grid", gap: 8 }, children: [
13503
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "flex-start" }, children: [
13504
+ /* @__PURE__ */ jsxRuntime.jsx(ShieldCheck, { size: 16, color: "#e8430a" }),
13505
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Le SaaS agit comme relais. Le backend SaaS appelle IAM côté serveur, puis renvoie l'URL de redirection." })
13506
+ ] }),
13507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "flex-start" }, children: [
13508
+ /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { size: 16, color: "#e8430a" }),
13509
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Une fenêtre IAM va s'ouvrir avec auto-connexion temporaire, sans appel direct du navigateur vers IAM." })
13510
+ ] })
13511
+ ] }),
13512
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { style: { gap: 12 }, children: [
13513
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), style: { flex: 1 }, disabled: loading, children: "Fermer" }),
13514
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => setConfirmOpen(true), disabled: loading, style: { flex: 1 }, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13515
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 16, style: { marginRight: 8, animation: "spin 1s linear infinite" } }),
13516
+ "Préparation..."
13517
+ ] }) : "Changer mon mot de passe" })
13518
+ ] })
13519
+ ] }) }),
13520
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: confirmOpen, onOpenChange: (nextOpen) => {
13521
+ if (!loading) {
13522
+ setConfirmOpen(nextOpen);
13523
+ }
13524
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { style: { maxWidth: "26rem", borderRadius: "16px", background: "#fff" }, children: [
13525
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
13526
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Êtes-vous sûr de rediriger vers le SSO ?" }),
13527
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Cette action ouvre IAM dans un nouvel onglet et prépare l'auto-connexion pour tester le changement de mot de passe." })
13528
+ ] }),
13529
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { border: "1px solid #fecaca", background: "#fef2f2", color: "#b91c1c", borderRadius: 12, padding: 12, fontSize: 13 }, children: errorMessage }),
13530
+ loading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 10, padding: "8px 0 4px" }, children: [
13531
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 26, style: { animation: "spin 1s linear infinite", color: "#e8430a" } }),
13532
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, color: "#475569", textAlign: "center" }, children: "Création de la session en cours..." })
13533
+ ] }),
13534
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { style: { gap: 12 }, children: [
13535
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: () => setConfirmOpen(false), style: { flex: 1 }, disabled: loading, children: "Annuler" }),
13536
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleRedirect, style: { flex: 1 }, disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13537
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { size: 16, style: { marginRight: 8, animation: "spin 1s linear infinite" } }),
13538
+ "Confirmer..."
13539
+ ] }) : "Confirmer" })
13540
+ ] })
13541
+ ] }) })
13542
+ ] });
13543
+ };
13544
+ function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onOpenPassword, onResetProfilePrompt }) {
13304
13545
  const [logs, setLogs] = react.useState([]);
13305
13546
  const [expanded, setExpanded] = react.useState(true);
13306
13547
  const [selectedLog, setSelectedLog] = react.useState(null);
@@ -13440,6 +13681,18 @@ function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOn
13440
13681
  children: "Infos profile"
13441
13682
  }
13442
13683
  ),
13684
+ /* @__PURE__ */ jsxRuntime.jsx(
13685
+ "button",
13686
+ {
13687
+ onClick: (e) => {
13688
+ e.stopPropagation();
13689
+ onOpenPassword == null ? void 0 : onOpenPassword();
13690
+ },
13691
+ disabled: !onOpenPassword,
13692
+ style: { background: "#334155", color: "#e2e8f0", border: "none", borderRadius: "4px", padding: "2px 8px", cursor: onOpenPassword ? "pointer" : "not-allowed", fontSize: "11px", opacity: onOpenPassword ? 1 : 0.5 },
13693
+ children: "Mot de passe"
13694
+ }
13695
+ ),
13443
13696
  /* @__PURE__ */ jsxRuntime.jsx(
13444
13697
  "button",
13445
13698
  {
@@ -13633,6 +13886,7 @@ function NativeSSOPage({
13633
13886
  const [showOnboarding, setShowOnboarding] = react.useState(false);
13634
13887
  const [pendingSession, setPendingSession] = react.useState(null);
13635
13888
  const [debugOnboardingState, setDebugOnboardingState] = react.useState(null);
13889
+ const [passwordRedirectOpen, setPasswordRedirectOpen] = react.useState(false);
13636
13890
  const [redirectingTarget, setRedirectingTarget] = react.useState(null);
13637
13891
  const [redirectingReason, setRedirectingReason] = react.useState(null);
13638
13892
  const [session, setSession] = react.useState(() => {
@@ -14005,6 +14259,9 @@ function NativeSSOPage({
14005
14259
  setShowOnboarding(true);
14006
14260
  }
14007
14261
  }, [session, clearOnboardingTimers]);
14262
+ const openDebugPassword = react.useCallback(() => {
14263
+ setPasswordRedirectOpen(true);
14264
+ }, []);
14008
14265
  react.useEffect(() => {
14009
14266
  syncProfilePrompt(session);
14010
14267
  return () => {
@@ -14152,8 +14409,18 @@ function NativeSSOPage({
14152
14409
  onOpenLogin: openDebugLogin,
14153
14410
  onOpenSignup: openDebugSignup,
14154
14411
  onOpenOnboarding: openDebugOnboarding,
14412
+ onOpenPassword: openDebugPassword,
14155
14413
  onResetProfilePrompt: resetDebugProfilePrompt
14156
14414
  }
14415
+ ),
14416
+ /* @__PURE__ */ jsxRuntime.jsx(
14417
+ PasswordRedirectModal,
14418
+ {
14419
+ open: passwordRedirectOpen,
14420
+ onOpenChange: setPasswordRedirectOpen,
14421
+ saasApiUrl,
14422
+ iamApiUrl
14423
+ }
14157
14424
  )
14158
14425
  ] });
14159
14426
  }
@@ -14233,8 +14500,18 @@ function NativeSSOPage({
14233
14500
  onOpenLogin: openDebugLogin,
14234
14501
  onOpenSignup: openDebugSignup,
14235
14502
  onOpenOnboarding: openDebugOnboarding,
14503
+ onOpenPassword: openDebugPassword,
14236
14504
  onResetProfilePrompt: resetDebugProfilePrompt
14237
14505
  }
14506
+ ),
14507
+ /* @__PURE__ */ jsxRuntime.jsx(
14508
+ PasswordRedirectModal,
14509
+ {
14510
+ open: passwordRedirectOpen,
14511
+ onOpenChange: setPasswordRedirectOpen,
14512
+ saasApiUrl,
14513
+ iamApiUrl
14514
+ }
14238
14515
  )
14239
14516
  ] });
14240
14517
  }