@ionite/react 0.0.24-beta.20260928.17 → 0.0.24-beta.20260930.19
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.
- package/dist/index.js +728 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1 +1,728 @@
|
|
|
1
|
-
import{defaultLogger as Ht,silentLogger as Xt}from"@ionite/core";import{silentLogger as me}from"@ionite/core";import{createContext as ve,useContext as X,useMemo as ye}from"react";import{JSON_MEDIA_TYPE as B,readJsonResponse as z,silentLogger as b}from"@ionite/core";import{useCallback as P,useEffect as q,useRef as J,useState as k}from"react";var ge=/^[a-zA-Z0-9._-]{1,1024}$/;function pe(e,t){if(!e.trim())throw Error(`${t} storageKey must be a non-empty string.`);if(!ge.test(e))throw Error(`${t} storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.`)}function A(e,t,r){if(t===void 0||t==="")return e;return pe(t,r),t}function G(e,t,r,o=b){if(typeof window>"u")return null;try{let n=e==="local"?localStorage:sessionStorage,s=n.getItem(t);if(!s)return null;let a=JSON.parse(s),c=r.validate(a);if(!c)return n.removeItem(t),null;let i=r.revive(c);if(r.isExpired(i))return n.removeItem(t),null;return i}catch(n){return o.error("Error loading auth record from storage:",n),null}}async function Y(e,t,r,o=b){try{let n=new Headers(r?.headers);if(!n.has("Accept"))n.set("Accept",B);let s=await fetch(e,{...r,headers:n});if(s.status===401)return{kind:"unauthenticated"};if(!s.ok){let i=Error(`Failed to fetch auth record: ${s.status} ${s.statusText}`);return o.error("Error fetching auth record from URL:",i),{kind:"error",error:i}}let a=await z(s,{log:o,operation:"react.auth_resource.fetch"}),c=t.validate(a);if(!c){let i=Error("Auth record from server failed validation.");return o.error("Error validating auth record from URL:",i),{kind:"error",error:i}}return{kind:"ok",value:t.revive(c)}}catch(n){let s=n instanceof Error?n:Error(String(n));return o.error("Error fetching auth record from URL:",s),{kind:"error",error:s}}}function R(e){let{url:t,storage:r,storageKey:o,disableListener:n,logoutEventName:s,codec:a}=e,[c,i]=k(null),[f,u]=k(!!t),[d,m]=k(null),x=J(e.fetchInit);x.current=e.fetchInit;let y=J(e.log);y.current=e.log;let p=P((l)=>{if(r==="memory"||typeof window>"u")return;try{let v=r==="local"?localStorage:sessionStorage;if(l===null)v.removeItem(o);else v.setItem(o,JSON.stringify(l))}catch(v){y.current.error("Error saving auth record to storage:",v)}},[r,o]),T=P((l)=>{i(l),p(l)},[p]),h=P(()=>{if(r==="memory")return null;return G(r,o,a,y.current)},[r,o,a]),g=P(async()=>{if(!t)return;u(!0);try{let l=await Y(t,a,x.current,y.current);if(l.kind==="ok")i(l.value),m(null),p(l.value);else if(l.kind==="unauthenticated")i(null),m(null),p(null);else m(l.error)}finally{u(!1)}},[t,a,p]);return q(()=>{let l=h();if(l)i(l);if(t)g();else u(!1)},[h,t,g]),q(()=>{if(n||r==="memory")return;let l=(I)=>{if(I.key!==o)return;if(I.newValue===null){i(null);return}try{let L=JSON.parse(I.newValue),F=a.validate(L);if(!F)return;let W=a.revive(F);if(a.isExpired(W)){i(null);return}i(W)}catch(L){y.current.error("Error parsing auth record from storage event:",L)}},v=()=>{i(null),m(null)};return window.addEventListener("storage",l),window.addEventListener(s,v),()=>{window.removeEventListener("storage",l),window.removeEventListener(s,v)}},[n,r,o,s,a]),{value:c,setValue:T,isLoading:f,error:d}}async function H(e,t,r){let{storageKey:o,storage:n,fetchInit:s,log:a=b}=r;if(n==="local"||n==="session"){let c=G(n,o,t,a);if(c)return c}if(e){let c=await Y(e,t,s,a);if(c.kind==="ok")return c.value}return}async function w(e,t,r){try{let o=new Headers(r?.fetchInit?.headers);if(!o.has("Accept"))o.set("Accept",B);let n=await fetch(e,{...r?.fetchInit,headers:o});if(!n.ok)return{success:!1,error:`HTTP ${n.status}`};let s=await z(n);if(!s.success)return{success:!1,error:s.message||"Logout failed"};let a=typeof s.redirect==="string"?s.redirect:void 0;if(typeof window<"u"){let c=A(t.defaultStorageKey,r?.storageKey,t.providerLabel);if(localStorage.removeItem(c),sessionStorage.removeItem(c),window.dispatchEvent(new CustomEvent(t.logoutEventName)),a&&r?.navigate!==!1)window.location.assign(a)}return{success:!0,redirect:a}}catch(o){return{success:!1,error:o instanceof Error?o.message:"Network error"}}}import{jsx as Te}from"react/jsx-runtime";var O="ion.ciam.customer",Z="ion-ciam-logout",M="CIAMProvider",Q={validate(e){if(!e||typeof e!=="object")return null;let t=e;if(typeof t.userName!=="string"||typeof t.name!=="string"||typeof t.email!=="string")return null;return e},revive(e){if(e.ciam?.expires)e.ciam.expires=new Date(e.ciam.expires);return e},isExpired(e){let t=e.ciam?.expires;if(!t)return!1;let r=t instanceof Date?t.getTime():new Date(t).getTime();return Number.isFinite(r)&&r<=Date.now()}},N=ve(void 0);function he({storageKey:e,customerUrl:t,storage:r="memory",disableListener:o=!1,fetchInit:n,log:s=me,children:a}){let c=A(O,e,M),{value:i,setValue:f,isLoading:u,error:d}=R({url:t,storage:r,storageKey:c,disableListener:o,logoutEventName:Z,codec:Q,fetchInit:n,log:s}),m=ye(()=>({customer:i,setCustomer:f,isLoading:u,error:d}),[i,f,u,d]);return Te(N.Provider,{value:m,children:a})}function Ce(){let e=X(N);if(e===void 0)throw Error("useCIAMContext must be used within a CIAMProvider");return e}function C(){return X(N)}async function Se(e,t){return H(e,Q,{storageKey:A(O,t?.storageKey,M),storage:t?.storage??"memory",fetchInit:t?.fetchInit,log:t?.log})}async function xe(e,t){return w(e,{defaultStorageKey:O,logoutEventName:Z,providerLabel:M},t)}import{ACCEPT_JSON as Ie,readJsonResponse as Ae,silentLogger as j}from"@ionite/core";import{useCallback as Pe,useEffect as Re,useState as E}from"react";import{jsx as D,jsxs as U}from"react/jsx-runtime";function ee(e,t=j){let[r,o]=E([]),[n,s]=E(Boolean(e)),[a,c]=E(null),i=Pe(()=>{if(!e)return;let f=!1;return s(!0),c(null),fetch(e,{headers:Ie}).then(async(u)=>{if(!u.ok)throw Error(`Failed to fetch CIAM providers: ${u.status} ${u.statusText}`);let d=await Ae(u,{log:t,operation:"react.ciam.providers"});if(!f)o(Array.isArray(d.providers)?d.providers:[])}).catch((u)=>{let d=u instanceof Error?u:Error(String(u));if(t.error("Error fetching CIAM providers:",d),!f)o([]),c(d)}).finally(()=>{if(!f)s(!1)}),()=>{f=!0}},[e,t]);return Re(()=>i(),[i]),{providers:r,isLoading:n,error:a,refresh:i}}function we(e,t){return{display:"flex",alignItems:"center",gap:"0.5rem",width:"100%",padding:"0.625rem 0.875rem",border:"1px solid rgba(0,0,0,0.15)",borderRadius:"0.5rem",background:(t==="dark"?e.colors?.dark:e.colors?.light)??(t==="dark"?"#1f1f1f":"#ffffff"),color:t==="dark"?"#ffffff":"#111111",textDecoration:"none",font:"inherit",cursor:"pointer"}}function Ee({providersUrl:e,limit:t,theme:r="light",renderProvider:o,loadingFallback:n,emptyFallback:s,errorFallback:a,moreLabel:c="Show more",className:i,log:f=j}){let{providers:u,isLoading:d,error:m,refresh:x}=ee(e,f),[y,p]=E(!1);if(d)return n??null;if(m)return a?a(m,x):s??null;if(u.length===0)return s??null;let T=!y&&typeof t==="number"&&t>0?u.slice(0,t):u,h=T.length<u.length;return U("div",{className:i,style:{display:"flex",flexDirection:"column",gap:"0.5rem"},"data-ion-ciam-providers":"",children:[T.map((g)=>o?D("span",{children:o(g)},g.name):U("a",{href:g.loginUrl,style:we(g,r),"data-ion-ciam-provider":g.name,children:[g.iconUrl?D("img",{src:g.iconUrl,alt:"",width:20,height:20,"aria-hidden":"true"}):null,U("span",{children:["Continue with ",g.displayName]})]},g.name)),h?D("button",{type:"button",onClick:()=>p(!0),"data-ion-ciam-show-more":"",children:c}):null]})}import{compareDisplayExpiryDesc as Ne,JSON_MEDIA_TYPE as De,readDisplayCookies as Ue,readJsonResponse as Ve}from"@ionite/core";import{createContext as Ke,useCallback as se,useContext as _e,useEffect as Fe,useMemo as We,useState as ie}from"react";import{buildTenantUrl as te,normalizeTenantRoutingStrategy as re}from"@ionite/core";import{createContext as Le,useContext as oe,useMemo as ke}from"react";import{jsx as Me}from"react/jsx-runtime";var V=Le(null);function ne(e,t){let r=re(t);return{ui:(o="/")=>te(e,o,r,"ui"),api:(o="/")=>te(e,o,r,"api")}}function be({id:e,strategy:t,children:r}){let o=ke(()=>{let n=re(t);return{id:e,strategy:n,urls:ne(e,n)}},[t,e]);return Me(V.Provider,{value:o,children:r})}function Oe(){let e=oe(V);if(!e)throw Error("useTenant() must be used within a TenantProvider");return e}function K(){return oe(V)??void 0}import{jsx as Be}from"react/jsx-runtime";var ae=Ke(void 0);function qe(){if(typeof document>"u")return[];return Ue(document.cookie).filter(({envelope:e})=>Date.parse(e.expiresAt)>Date.now()).sort((e,t)=>{let r=e.envelope.kind==="discovered",o=t.envelope.kind==="discovered";if(r!==o)return r?1:-1;return Ne(e.envelope.expiresAt,t.envelope.expiresAt)}).flatMap(({envelope:e})=>e.tenantId?[{tenantId:e.tenantId,authType:e.authType,source:e.kind==="discovered"?"discovered":"session",user:e.user,expiresAt:e.expiresAt,...e.tenantName?{tenantName:e.tenantName}:{}}]:[]).filter((e,t,r)=>r.findIndex((o)=>o.tenantId===e.tenantId)===t)}function Je({sessionsUrl:e,verify:t="auto",currentTenantId:r,fetchInit:o,children:n}){let s=K(),a=r??s?.id,[c,i]=ie("loading"),[f,u]=ie([]),d=se(async(y)=>{let p=qe(),T=Boolean(a)&&!p.some((g)=>g.tenantId===a),h=y||t==="always"||t==="auto"&&(p.length===0||T);if(p.length>0)u(p),i("cookies");else if(!h){u([]),i("unauthenticated");return}if(!h)return;try{let g=new Headers(o?.headers);if(!g.has("Accept"))g.set("Accept",De);let l=await fetch(e,{credentials:"same-origin",...o,headers:g});if(l.status===404){if(p.length===0)u([]),i("unavailable");return}if(l.status===401){if(p.length===0)u([]),i("unauthenticated");return}if(!l.ok){if(p.length===0)u([]),i("unavailable");return}let v=await Ve(l),I=Array.isArray(v.tenants)?v.tenants:[];u(I),i(I.length>0?"verified":"unauthenticated")}catch{if(p.length===0)u([]),i("unavailable")}},[a,o,e,t]);Fe(()=>{if(typeof document>"u")return;d(!1)},[d]);let m=se(()=>d(!0),[d]),x=We(()=>({status:c,tenants:f,refresh:m}),[c,f,m]);return Be(ae.Provider,{value:x,children:n})}function ue(){let e=_e(ae);if(!e)throw Error("useTenants() must be used within a SessionsProvider");return e}function $e(){return ue()}import{silentLogger as ze}from"@ionite/core";import{createContext as Ge,useContext as ce,useMemo as Ye}from"react";import{jsx as je}from"react/jsx-runtime";var le="ion.sso.user",de="ion-sso-logout",fe="SSOProvider",He={validate(e){if(!e||typeof e!=="object")return null;let t=e;if(typeof t.userName!=="string"||typeof t.name!=="string"||typeof t.email!=="string")return null;return e},revive(e){if(e.sso?.expires)e.sso.expires=new Date(e.sso.expires);return e},isExpired(e){let t=e.sso?.expires;if(!t)return!1;let r=t instanceof Date?t.getTime():new Date(t).getTime();return Number.isFinite(r)&&r<=Date.now()}},_=Ge(void 0);function Xe({storageKey:e,userUrl:t,storage:r="memory",disableListener:o=!1,fetchInit:n,log:s=ze,children:a}){let c=A(le,e,fe),{value:i,setValue:f,isLoading:u,error:d}=R({url:t,storage:r,storageKey:c,disableListener:o,logoutEventName:de,codec:He,fetchInit:n,log:s}),m=Ye(()=>({user:i,setUser:f,isLoading:u,error:d}),[i,f,u,d]);return je(_.Provider,{value:m,children:a})}function Ze(){let e=ce(_);if(e===void 0)throw Error("useSSOContext must be used within a SSOProvider");return e}function S(){return ce(_)}async function Qe(e,t){return w(e,{defaultStorageKey:le,logoutEventName:de,providerLabel:fe},t)}import{jsx as rt,Fragment as tt}from"react/jsx-runtime";function et({complete:e=!1,audience:t="workforce",children:r}){let o=S(),n=C();if((t==="customer"?!!n?.isLoading:!!o?.isLoading)&&!e)return rt(tt,{children:r});return null}import{jsx as st,Fragment as nt}from"react/jsx-runtime";function ot({audience:e="workforce",children:t}){let r=S(),o=C();if(e==="customer"?!!o?.customer:!!r?.user)return st(nt,{children:t});return null}import{jsx as ut,Fragment as at}from"react/jsx-runtime";function it({audience:e="workforce",children:t}){let r=S(),o=C(),n=e==="customer"?!!o?.customer:!!r?.user,s=e==="customer"?!!o?.isLoading:!!r?.isLoading;if(n||s)return null;return ut(at,{children:t})}export{he as CIAMProvider,Ee as CIAMProviderList,Xe as SSOProvider,Je as SessionsProvider,et as SignInLoading,ot as SignedIn,it as SignedOut,be as TenantProvider,ne as createTenantUrls,Ht as defaultLogger,Se as getCustomer,Qe as logout,xe as logoutCustomer,Xt as silentLogger,Ce as useCIAMContext,ee as useCIAMProviders,C as useOptionalCIAMContext,S as useOptionalSSOContext,K as useOptionalTenant,Ze as useSSOContext,$e as useSessions,Oe as useTenant,ue as useTenants};
|
|
1
|
+
// packages/react/src/index.ts
|
|
2
|
+
import { defaultLogger, silentLogger as silentLogger5 } from "@ionite/core";
|
|
3
|
+
|
|
4
|
+
// packages/react/src/ciam-provider.tsx
|
|
5
|
+
import { silentLogger as silentLogger2 } from "@ionite/core";
|
|
6
|
+
import { createContext, useContext, useMemo } from "react";
|
|
7
|
+
|
|
8
|
+
// packages/react/src/auth-resource.tsx
|
|
9
|
+
import { JSON_MEDIA_TYPE, readJsonResponse, silentLogger } from "@ionite/core";
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
var VALID_STORAGE_KEY = /^[a-zA-Z0-9._-]{1,1024}$/;
|
|
12
|
+
function validateStorageKey(key, providerLabel) {
|
|
13
|
+
if (!key.trim()) {
|
|
14
|
+
throw new Error(`${providerLabel} storageKey must be a non-empty string.`);
|
|
15
|
+
}
|
|
16
|
+
if (!VALID_STORAGE_KEY.test(key)) {
|
|
17
|
+
throw new Error(`${providerLabel} storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function resolveStorageKey(defaultKey, storageKey, providerLabel) {
|
|
21
|
+
if (storageKey === undefined || storageKey === "") {
|
|
22
|
+
return defaultKey;
|
|
23
|
+
}
|
|
24
|
+
validateStorageKey(storageKey, providerLabel);
|
|
25
|
+
return storageKey;
|
|
26
|
+
}
|
|
27
|
+
function loadFromStorageForKey(storage, storageKey, codec, log = silentLogger) {
|
|
28
|
+
if (typeof window === "undefined")
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const store = storage === "local" ? localStorage : sessionStorage;
|
|
32
|
+
const raw = store.getItem(storageKey);
|
|
33
|
+
if (!raw)
|
|
34
|
+
return null;
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
const validated = codec.validate(parsed);
|
|
37
|
+
if (!validated) {
|
|
38
|
+
store.removeItem(storageKey);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const revived = codec.revive(validated);
|
|
42
|
+
if (codec.isExpired(revived)) {
|
|
43
|
+
store.removeItem(storageKey);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return revived;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
log.error("Error loading auth record from storage:", error);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function fetchResourceFromServer(url, codec, fetchInit, log = silentLogger) {
|
|
53
|
+
try {
|
|
54
|
+
const headers = new Headers(fetchInit?.headers);
|
|
55
|
+
if (!headers.has("Accept"))
|
|
56
|
+
headers.set("Accept", JSON_MEDIA_TYPE);
|
|
57
|
+
const response = await fetch(url, { ...fetchInit, headers });
|
|
58
|
+
if (response.status === 401) {
|
|
59
|
+
return { kind: "unauthenticated" };
|
|
60
|
+
}
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const error = new Error(`Failed to fetch auth record: ${response.status} ${response.statusText}`);
|
|
63
|
+
log.error("Error fetching auth record from URL:", error);
|
|
64
|
+
return { kind: "error", error };
|
|
65
|
+
}
|
|
66
|
+
const data = await readJsonResponse(response, {
|
|
67
|
+
log,
|
|
68
|
+
operation: "react.auth_resource.fetch"
|
|
69
|
+
});
|
|
70
|
+
const validated = codec.validate(data);
|
|
71
|
+
if (!validated) {
|
|
72
|
+
const error = new Error("Auth record from server failed validation.");
|
|
73
|
+
log.error("Error validating auth record from URL:", error);
|
|
74
|
+
return { kind: "error", error };
|
|
75
|
+
}
|
|
76
|
+
return { kind: "ok", value: codec.revive(validated) };
|
|
77
|
+
} catch (err) {
|
|
78
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
79
|
+
log.error("Error fetching auth record from URL:", error);
|
|
80
|
+
return { kind: "error", error };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function useAuthResource(options) {
|
|
84
|
+
const { url, storage, storageKey, disableListener, logoutEventName, codec } = options;
|
|
85
|
+
const [value, setValueState] = useState(null);
|
|
86
|
+
const [isLoading, setIsLoading] = useState(!!url);
|
|
87
|
+
const [error, setError] = useState(null);
|
|
88
|
+
const fetchInitRef = useRef(options.fetchInit);
|
|
89
|
+
fetchInitRef.current = options.fetchInit;
|
|
90
|
+
const logRef = useRef(options.log);
|
|
91
|
+
logRef.current = options.log;
|
|
92
|
+
const saveToStorage = useCallback((next) => {
|
|
93
|
+
if (storage === "memory" || typeof window === "undefined")
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
const store = storage === "local" ? localStorage : sessionStorage;
|
|
97
|
+
if (next === null) {
|
|
98
|
+
store.removeItem(storageKey);
|
|
99
|
+
} else {
|
|
100
|
+
store.setItem(storageKey, JSON.stringify(next));
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
logRef.current.error("Error saving auth record to storage:", err);
|
|
104
|
+
}
|
|
105
|
+
}, [storage, storageKey]);
|
|
106
|
+
const setValue = useCallback((next) => {
|
|
107
|
+
setValueState(next);
|
|
108
|
+
saveToStorage(next);
|
|
109
|
+
}, [saveToStorage]);
|
|
110
|
+
const loadFromStorage = useCallback(() => {
|
|
111
|
+
if (storage === "memory")
|
|
112
|
+
return null;
|
|
113
|
+
return loadFromStorageForKey(storage, storageKey, codec, logRef.current);
|
|
114
|
+
}, [storage, storageKey, codec]);
|
|
115
|
+
const refresh = useCallback(async () => {
|
|
116
|
+
if (!url)
|
|
117
|
+
return;
|
|
118
|
+
setIsLoading(true);
|
|
119
|
+
try {
|
|
120
|
+
const outcome = await fetchResourceFromServer(url, codec, fetchInitRef.current, logRef.current);
|
|
121
|
+
if (outcome.kind === "ok") {
|
|
122
|
+
setValueState(outcome.value);
|
|
123
|
+
setError(null);
|
|
124
|
+
saveToStorage(outcome.value);
|
|
125
|
+
} else if (outcome.kind === "unauthenticated") {
|
|
126
|
+
setValueState(null);
|
|
127
|
+
setError(null);
|
|
128
|
+
saveToStorage(null);
|
|
129
|
+
} else {
|
|
130
|
+
setError(outcome.error);
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
setIsLoading(false);
|
|
134
|
+
}
|
|
135
|
+
}, [url, codec, saveToStorage]);
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
const stored = loadFromStorage();
|
|
138
|
+
if (stored)
|
|
139
|
+
setValueState(stored);
|
|
140
|
+
if (url) {
|
|
141
|
+
refresh();
|
|
142
|
+
} else {
|
|
143
|
+
setIsLoading(false);
|
|
144
|
+
}
|
|
145
|
+
}, [loadFromStorage, url, refresh]);
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (disableListener || storage === "memory")
|
|
148
|
+
return;
|
|
149
|
+
const handleStorageChange = (event) => {
|
|
150
|
+
if (event.key !== storageKey)
|
|
151
|
+
return;
|
|
152
|
+
if (event.newValue === null) {
|
|
153
|
+
setValueState(null);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(event.newValue);
|
|
158
|
+
const validated = codec.validate(parsed);
|
|
159
|
+
if (!validated)
|
|
160
|
+
return;
|
|
161
|
+
const revived = codec.revive(validated);
|
|
162
|
+
if (codec.isExpired(revived)) {
|
|
163
|
+
setValueState(null);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
setValueState(revived);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
logRef.current.error("Error parsing auth record from storage event:", err);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const handleLogout = () => {
|
|
172
|
+
setValueState(null);
|
|
173
|
+
setError(null);
|
|
174
|
+
};
|
|
175
|
+
window.addEventListener("storage", handleStorageChange);
|
|
176
|
+
window.addEventListener(logoutEventName, handleLogout);
|
|
177
|
+
return () => {
|
|
178
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
179
|
+
window.removeEventListener(logoutEventName, handleLogout);
|
|
180
|
+
};
|
|
181
|
+
}, [disableListener, storage, storageKey, logoutEventName, codec]);
|
|
182
|
+
return { value, setValue, isLoading, error };
|
|
183
|
+
}
|
|
184
|
+
async function getResource(url, codec, options) {
|
|
185
|
+
const { storageKey, storage, fetchInit, log = silentLogger } = options;
|
|
186
|
+
if (storage === "local" || storage === "session") {
|
|
187
|
+
const fromStorage = loadFromStorageForKey(storage, storageKey, codec, log);
|
|
188
|
+
if (fromStorage)
|
|
189
|
+
return fromStorage;
|
|
190
|
+
}
|
|
191
|
+
if (url) {
|
|
192
|
+
const outcome = await fetchResourceFromServer(url, codec, fetchInit, log);
|
|
193
|
+
if (outcome.kind === "ok")
|
|
194
|
+
return outcome.value;
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
async function logoutResource(logoutUrl, config, options) {
|
|
199
|
+
try {
|
|
200
|
+
const headers = new Headers(options?.fetchInit?.headers);
|
|
201
|
+
if (!headers.has("Accept"))
|
|
202
|
+
headers.set("Accept", JSON_MEDIA_TYPE);
|
|
203
|
+
const response = await fetch(logoutUrl, {
|
|
204
|
+
...options?.fetchInit,
|
|
205
|
+
headers
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
return { success: false, error: `HTTP ${response.status}` };
|
|
209
|
+
}
|
|
210
|
+
const data = await readJsonResponse(response);
|
|
211
|
+
if (!data.success) {
|
|
212
|
+
return { success: false, error: data.message || "Logout failed" };
|
|
213
|
+
}
|
|
214
|
+
const redirect = typeof data.redirect === "string" ? data.redirect : undefined;
|
|
215
|
+
if (typeof window !== "undefined") {
|
|
216
|
+
const key = resolveStorageKey(config.defaultStorageKey, options?.storageKey, config.providerLabel);
|
|
217
|
+
localStorage.removeItem(key);
|
|
218
|
+
sessionStorage.removeItem(key);
|
|
219
|
+
window.dispatchEvent(new CustomEvent(config.logoutEventName));
|
|
220
|
+
if (redirect && options?.navigate !== false) {
|
|
221
|
+
window.location.assign(redirect);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { success: true, redirect };
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: error instanceof Error ? error.message : "Network error"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// packages/react/src/ciam-provider.tsx
|
|
234
|
+
import { jsx } from "react/jsx-runtime";
|
|
235
|
+
var DEFAULT_STORAGE_KEY = "ion.ciam.customer";
|
|
236
|
+
var LOGOUT_EVENT = "ion-ciam-logout";
|
|
237
|
+
var PROVIDER_LABEL = "CIAMProvider";
|
|
238
|
+
var customerCodec = {
|
|
239
|
+
validate(raw) {
|
|
240
|
+
if (!raw || typeof raw !== "object")
|
|
241
|
+
return null;
|
|
242
|
+
const candidate = raw;
|
|
243
|
+
if (typeof candidate.userName !== "string" || typeof candidate.name !== "string" || typeof candidate.email !== "string") {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
return raw;
|
|
247
|
+
},
|
|
248
|
+
revive(customer) {
|
|
249
|
+
if (customer.ciam?.expires) {
|
|
250
|
+
customer.ciam.expires = new Date(customer.ciam.expires);
|
|
251
|
+
}
|
|
252
|
+
return customer;
|
|
253
|
+
},
|
|
254
|
+
isExpired(customer) {
|
|
255
|
+
const expires = customer.ciam?.expires;
|
|
256
|
+
if (!expires)
|
|
257
|
+
return false;
|
|
258
|
+
const time = expires instanceof Date ? expires.getTime() : new Date(expires).getTime();
|
|
259
|
+
return Number.isFinite(time) && time <= Date.now();
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var CTX = createContext(undefined);
|
|
263
|
+
function CIAMProvider({
|
|
264
|
+
storageKey: storageKeyProp,
|
|
265
|
+
customerUrl,
|
|
266
|
+
storage = "memory",
|
|
267
|
+
disableListener = false,
|
|
268
|
+
fetchInit,
|
|
269
|
+
log = silentLogger2,
|
|
270
|
+
children
|
|
271
|
+
}) {
|
|
272
|
+
const storageKey = resolveStorageKey(DEFAULT_STORAGE_KEY, storageKeyProp, PROVIDER_LABEL);
|
|
273
|
+
const { value, setValue, isLoading, error } = useAuthResource({
|
|
274
|
+
url: customerUrl,
|
|
275
|
+
storage,
|
|
276
|
+
storageKey,
|
|
277
|
+
disableListener,
|
|
278
|
+
logoutEventName: LOGOUT_EVENT,
|
|
279
|
+
codec: customerCodec,
|
|
280
|
+
fetchInit,
|
|
281
|
+
log
|
|
282
|
+
});
|
|
283
|
+
const contextValue = useMemo(() => ({ customer: value, setCustomer: setValue, isLoading, error }), [value, setValue, isLoading, error]);
|
|
284
|
+
return /* @__PURE__ */ jsx(CTX.Provider, {
|
|
285
|
+
value: contextValue,
|
|
286
|
+
children
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function useCIAMContext() {
|
|
290
|
+
const context = useContext(CTX);
|
|
291
|
+
if (context === undefined) {
|
|
292
|
+
throw new Error("useCIAMContext must be used within a CIAMProvider");
|
|
293
|
+
}
|
|
294
|
+
return context;
|
|
295
|
+
}
|
|
296
|
+
function useOptionalCIAMContext() {
|
|
297
|
+
return useContext(CTX);
|
|
298
|
+
}
|
|
299
|
+
async function getCustomer(customerUrl, options) {
|
|
300
|
+
return getResource(customerUrl, customerCodec, {
|
|
301
|
+
storageKey: resolveStorageKey(DEFAULT_STORAGE_KEY, options?.storageKey, PROVIDER_LABEL),
|
|
302
|
+
storage: options?.storage ?? "memory",
|
|
303
|
+
fetchInit: options?.fetchInit,
|
|
304
|
+
log: options?.log
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async function logoutCustomer(logoutUrl, options) {
|
|
308
|
+
return logoutResource(logoutUrl, { defaultStorageKey: DEFAULT_STORAGE_KEY, logoutEventName: LOGOUT_EVENT, providerLabel: PROVIDER_LABEL }, options);
|
|
309
|
+
}
|
|
310
|
+
// packages/react/src/ciam-providers.tsx
|
|
311
|
+
import { ACCEPT_JSON, readJsonResponse as readJsonResponse2, silentLogger as silentLogger3 } from "@ionite/core";
|
|
312
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
313
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
314
|
+
function useCIAMProviders(providersUrl, log = silentLogger3) {
|
|
315
|
+
const [providers, setProviders] = useState2([]);
|
|
316
|
+
const [isLoading, setIsLoading] = useState2(Boolean(providersUrl));
|
|
317
|
+
const [error, setError] = useState2(null);
|
|
318
|
+
const refresh = useCallback2(() => {
|
|
319
|
+
if (!providersUrl)
|
|
320
|
+
return;
|
|
321
|
+
let cancelled = false;
|
|
322
|
+
setIsLoading(true);
|
|
323
|
+
setError(null);
|
|
324
|
+
fetch(providersUrl, { headers: ACCEPT_JSON }).then(async (response) => {
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
throw new Error(`Failed to fetch CIAM providers: ${response.status} ${response.statusText}`);
|
|
327
|
+
}
|
|
328
|
+
const data = await readJsonResponse2(response, {
|
|
329
|
+
log,
|
|
330
|
+
operation: "react.ciam.providers"
|
|
331
|
+
});
|
|
332
|
+
if (!cancelled)
|
|
333
|
+
setProviders(Array.isArray(data.providers) ? data.providers : []);
|
|
334
|
+
}).catch((err) => {
|
|
335
|
+
const normalized = err instanceof Error ? err : new Error(String(err));
|
|
336
|
+
log.error("Error fetching CIAM providers:", normalized);
|
|
337
|
+
if (!cancelled) {
|
|
338
|
+
setProviders([]);
|
|
339
|
+
setError(normalized);
|
|
340
|
+
}
|
|
341
|
+
}).finally(() => {
|
|
342
|
+
if (!cancelled)
|
|
343
|
+
setIsLoading(false);
|
|
344
|
+
});
|
|
345
|
+
return () => {
|
|
346
|
+
cancelled = true;
|
|
347
|
+
};
|
|
348
|
+
}, [providersUrl, log]);
|
|
349
|
+
useEffect2(() => {
|
|
350
|
+
const cleanup = refresh();
|
|
351
|
+
return cleanup;
|
|
352
|
+
}, [refresh]);
|
|
353
|
+
return { providers, isLoading, error, refresh };
|
|
354
|
+
}
|
|
355
|
+
function providerButtonStyle(provider, theme) {
|
|
356
|
+
const background = theme === "dark" ? provider.colors?.dark : provider.colors?.light;
|
|
357
|
+
return {
|
|
358
|
+
display: "flex",
|
|
359
|
+
alignItems: "center",
|
|
360
|
+
gap: "0.5rem",
|
|
361
|
+
width: "100%",
|
|
362
|
+
padding: "0.625rem 0.875rem",
|
|
363
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
364
|
+
borderRadius: "0.5rem",
|
|
365
|
+
background: background ?? (theme === "dark" ? "#1f1f1f" : "#ffffff"),
|
|
366
|
+
color: theme === "dark" ? "#ffffff" : "#111111",
|
|
367
|
+
textDecoration: "none",
|
|
368
|
+
font: "inherit",
|
|
369
|
+
cursor: "pointer"
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function CIAMProviderList({
|
|
373
|
+
providersUrl,
|
|
374
|
+
limit,
|
|
375
|
+
theme = "light",
|
|
376
|
+
renderProvider,
|
|
377
|
+
loadingFallback,
|
|
378
|
+
emptyFallback,
|
|
379
|
+
errorFallback,
|
|
380
|
+
moreLabel = "Show more",
|
|
381
|
+
className,
|
|
382
|
+
log = silentLogger3
|
|
383
|
+
}) {
|
|
384
|
+
const { providers, isLoading, error, refresh } = useCIAMProviders(providersUrl, log);
|
|
385
|
+
const [expanded, setExpanded] = useState2(false);
|
|
386
|
+
if (isLoading) {
|
|
387
|
+
return loadingFallback ?? null;
|
|
388
|
+
}
|
|
389
|
+
if (error) {
|
|
390
|
+
return errorFallback ? errorFallback(error, refresh) : emptyFallback ?? null;
|
|
391
|
+
}
|
|
392
|
+
if (providers.length === 0) {
|
|
393
|
+
return emptyFallback ?? null;
|
|
394
|
+
}
|
|
395
|
+
const visible = !expanded && typeof limit === "number" && limit > 0 ? providers.slice(0, limit) : providers;
|
|
396
|
+
const hasMore = visible.length < providers.length;
|
|
397
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
398
|
+
className,
|
|
399
|
+
style: { display: "flex", flexDirection: "column", gap: "0.5rem" },
|
|
400
|
+
"data-ion-ciam-providers": "",
|
|
401
|
+
children: [
|
|
402
|
+
visible.map((provider) => renderProvider ? /* @__PURE__ */ jsx2("span", {
|
|
403
|
+
children: renderProvider(provider)
|
|
404
|
+
}, provider.name) : /* @__PURE__ */ jsxs("a", {
|
|
405
|
+
href: provider.loginUrl,
|
|
406
|
+
style: providerButtonStyle(provider, theme),
|
|
407
|
+
"data-ion-ciam-provider": provider.name,
|
|
408
|
+
children: [
|
|
409
|
+
provider.iconUrl ? /* @__PURE__ */ jsx2("img", {
|
|
410
|
+
src: provider.iconUrl,
|
|
411
|
+
alt: "",
|
|
412
|
+
width: 20,
|
|
413
|
+
height: 20,
|
|
414
|
+
"aria-hidden": "true"
|
|
415
|
+
}) : null,
|
|
416
|
+
/* @__PURE__ */ jsxs("span", {
|
|
417
|
+
children: [
|
|
418
|
+
"Continue with ",
|
|
419
|
+
provider.displayName
|
|
420
|
+
]
|
|
421
|
+
})
|
|
422
|
+
]
|
|
423
|
+
}, provider.name)),
|
|
424
|
+
hasMore ? /* @__PURE__ */ jsx2("button", {
|
|
425
|
+
type: "button",
|
|
426
|
+
onClick: () => setExpanded(true),
|
|
427
|
+
"data-ion-ciam-show-more": "",
|
|
428
|
+
children: moreLabel
|
|
429
|
+
}) : null
|
|
430
|
+
]
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
// packages/react/src/sessions-context.tsx
|
|
434
|
+
import {
|
|
435
|
+
compareDisplayExpiryDesc,
|
|
436
|
+
JSON_MEDIA_TYPE as JSON_MEDIA_TYPE2,
|
|
437
|
+
readDisplayCookies,
|
|
438
|
+
readJsonResponse as readJsonResponse3
|
|
439
|
+
} from "@ionite/core";
|
|
440
|
+
import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
441
|
+
|
|
442
|
+
// packages/react/src/tenant-context.tsx
|
|
443
|
+
import { buildTenantUrl, normalizeTenantRoutingStrategy } from "@ionite/core";
|
|
444
|
+
import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
|
|
445
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
446
|
+
var TenantContext = createContext2(null);
|
|
447
|
+
function createTenantUrls(id, strategy) {
|
|
448
|
+
const normalized = normalizeTenantRoutingStrategy(strategy);
|
|
449
|
+
return {
|
|
450
|
+
ui: (restPath = "/") => buildTenantUrl(id, restPath, normalized, "ui"),
|
|
451
|
+
api: (restPath = "/") => buildTenantUrl(id, restPath, normalized, "api")
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function TenantProvider({ id, strategy, children }) {
|
|
455
|
+
const value = useMemo2(() => {
|
|
456
|
+
const normalized = normalizeTenantRoutingStrategy(strategy);
|
|
457
|
+
return {
|
|
458
|
+
id,
|
|
459
|
+
strategy: normalized,
|
|
460
|
+
urls: createTenantUrls(id, normalized)
|
|
461
|
+
};
|
|
462
|
+
}, [strategy, id]);
|
|
463
|
+
return /* @__PURE__ */ jsx3(TenantContext.Provider, {
|
|
464
|
+
value,
|
|
465
|
+
children
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
function useTenant() {
|
|
469
|
+
const value = useContext2(TenantContext);
|
|
470
|
+
if (!value) {
|
|
471
|
+
throw new Error("useTenant() must be used within a TenantProvider");
|
|
472
|
+
}
|
|
473
|
+
return value;
|
|
474
|
+
}
|
|
475
|
+
function useOptionalTenant() {
|
|
476
|
+
return useContext2(TenantContext) ?? undefined;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// packages/react/src/sessions-context.tsx
|
|
480
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
481
|
+
var SessionsContext = createContext3(undefined);
|
|
482
|
+
function tenantsFromCookies() {
|
|
483
|
+
if (typeof document === "undefined")
|
|
484
|
+
return [];
|
|
485
|
+
return readDisplayCookies(document.cookie).filter(({ envelope }) => Date.parse(envelope.expiresAt) > Date.now()).sort((a, b) => {
|
|
486
|
+
const aDiscovered = a.envelope.kind === "discovered";
|
|
487
|
+
const bDiscovered = b.envelope.kind === "discovered";
|
|
488
|
+
if (aDiscovered !== bDiscovered)
|
|
489
|
+
return aDiscovered ? 1 : -1;
|
|
490
|
+
return compareDisplayExpiryDesc(a.envelope.expiresAt, b.envelope.expiresAt);
|
|
491
|
+
}).flatMap(({ envelope }) => envelope.tenantId ? [
|
|
492
|
+
{
|
|
493
|
+
tenantId: envelope.tenantId,
|
|
494
|
+
authType: envelope.authType,
|
|
495
|
+
source: envelope.kind === "discovered" ? "discovered" : "session",
|
|
496
|
+
user: envelope.user,
|
|
497
|
+
expiresAt: envelope.expiresAt,
|
|
498
|
+
...envelope.tenantName ? { tenantName: envelope.tenantName } : {}
|
|
499
|
+
}
|
|
500
|
+
] : []).filter((entry, index, entries) => entries.findIndex((candidate) => candidate.tenantId === entry.tenantId) === index);
|
|
501
|
+
}
|
|
502
|
+
function SessionsProvider({
|
|
503
|
+
sessionsUrl,
|
|
504
|
+
verify = "auto",
|
|
505
|
+
currentTenantId,
|
|
506
|
+
fetchInit,
|
|
507
|
+
children
|
|
508
|
+
}) {
|
|
509
|
+
const tenant = useOptionalTenant();
|
|
510
|
+
const current = currentTenantId ?? tenant?.id;
|
|
511
|
+
const [status, setStatus] = useState3("loading");
|
|
512
|
+
const [tenants, setTenants] = useState3([]);
|
|
513
|
+
const load = useCallback3(async (force) => {
|
|
514
|
+
const fromCookies = tenantsFromCookies();
|
|
515
|
+
const currentMissing = Boolean(current) && !fromCookies.some((entry) => entry.tenantId === current);
|
|
516
|
+
const shouldFetch = force || verify === "always" || verify === "auto" && (fromCookies.length === 0 || currentMissing);
|
|
517
|
+
if (fromCookies.length > 0) {
|
|
518
|
+
setTenants(fromCookies);
|
|
519
|
+
setStatus("cookies");
|
|
520
|
+
} else if (!shouldFetch) {
|
|
521
|
+
setTenants([]);
|
|
522
|
+
setStatus("unauthenticated");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (!shouldFetch)
|
|
526
|
+
return;
|
|
527
|
+
try {
|
|
528
|
+
const headers = new Headers(fetchInit?.headers);
|
|
529
|
+
if (!headers.has("Accept"))
|
|
530
|
+
headers.set("Accept", JSON_MEDIA_TYPE2);
|
|
531
|
+
const response = await fetch(sessionsUrl, { credentials: "same-origin", ...fetchInit, headers });
|
|
532
|
+
if (response.status === 404) {
|
|
533
|
+
if (fromCookies.length === 0) {
|
|
534
|
+
setTenants([]);
|
|
535
|
+
setStatus("unavailable");
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (response.status === 401) {
|
|
540
|
+
if (fromCookies.length === 0) {
|
|
541
|
+
setTenants([]);
|
|
542
|
+
setStatus("unauthenticated");
|
|
543
|
+
}
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (!response.ok) {
|
|
547
|
+
if (fromCookies.length === 0) {
|
|
548
|
+
setTenants([]);
|
|
549
|
+
setStatus("unavailable");
|
|
550
|
+
}
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const body = await readJsonResponse3(response);
|
|
554
|
+
const verified = Array.isArray(body.tenants) ? body.tenants : [];
|
|
555
|
+
setTenants(verified);
|
|
556
|
+
setStatus(verified.length > 0 ? "verified" : "unauthenticated");
|
|
557
|
+
} catch {
|
|
558
|
+
if (fromCookies.length === 0) {
|
|
559
|
+
setTenants([]);
|
|
560
|
+
setStatus("unavailable");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}, [current, fetchInit, sessionsUrl, verify]);
|
|
564
|
+
useEffect3(() => {
|
|
565
|
+
if (typeof document === "undefined")
|
|
566
|
+
return;
|
|
567
|
+
load(false);
|
|
568
|
+
}, [load]);
|
|
569
|
+
const refresh = useCallback3(() => load(true), [load]);
|
|
570
|
+
const value = useMemo3(() => ({ status, tenants, refresh }), [status, tenants, refresh]);
|
|
571
|
+
return /* @__PURE__ */ jsx4(SessionsContext.Provider, {
|
|
572
|
+
value,
|
|
573
|
+
children
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function useTenants() {
|
|
577
|
+
const value = useContext3(SessionsContext);
|
|
578
|
+
if (!value)
|
|
579
|
+
throw new Error("useTenants() must be used within a SessionsProvider");
|
|
580
|
+
return value;
|
|
581
|
+
}
|
|
582
|
+
function useSessions() {
|
|
583
|
+
return useTenants();
|
|
584
|
+
}
|
|
585
|
+
// packages/react/src/sso-provider.tsx
|
|
586
|
+
import { silentLogger as silentLogger4 } from "@ionite/core";
|
|
587
|
+
import { createContext as createContext4, useContext as useContext4, useMemo as useMemo4 } from "react";
|
|
588
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
589
|
+
var DEFAULT_STORAGE_KEY2 = "ion.sso.user";
|
|
590
|
+
var LOGOUT_EVENT2 = "ion-sso-logout";
|
|
591
|
+
var PROVIDER_LABEL2 = "SSOProvider";
|
|
592
|
+
var userCodec = {
|
|
593
|
+
validate(raw) {
|
|
594
|
+
if (!raw || typeof raw !== "object")
|
|
595
|
+
return null;
|
|
596
|
+
const candidate = raw;
|
|
597
|
+
if (typeof candidate.userName !== "string" || typeof candidate.name !== "string" || typeof candidate.email !== "string") {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
return raw;
|
|
601
|
+
},
|
|
602
|
+
revive(user) {
|
|
603
|
+
if (user.sso?.expires) {
|
|
604
|
+
user.sso.expires = new Date(user.sso.expires);
|
|
605
|
+
}
|
|
606
|
+
return user;
|
|
607
|
+
},
|
|
608
|
+
isExpired(user) {
|
|
609
|
+
const expires = user.sso?.expires;
|
|
610
|
+
if (!expires)
|
|
611
|
+
return false;
|
|
612
|
+
const time = expires instanceof Date ? expires.getTime() : new Date(expires).getTime();
|
|
613
|
+
return Number.isFinite(time) && time <= Date.now();
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
var CTX2 = createContext4(undefined);
|
|
617
|
+
function SSOProvider({
|
|
618
|
+
storageKey: storageKeyProp,
|
|
619
|
+
userUrl,
|
|
620
|
+
storage = "memory",
|
|
621
|
+
disableListener = false,
|
|
622
|
+
fetchInit,
|
|
623
|
+
log = silentLogger4,
|
|
624
|
+
children
|
|
625
|
+
}) {
|
|
626
|
+
const storageKey = resolveStorageKey(DEFAULT_STORAGE_KEY2, storageKeyProp, PROVIDER_LABEL2);
|
|
627
|
+
const { value, setValue, isLoading, error } = useAuthResource({
|
|
628
|
+
url: userUrl,
|
|
629
|
+
storage,
|
|
630
|
+
storageKey,
|
|
631
|
+
disableListener,
|
|
632
|
+
logoutEventName: LOGOUT_EVENT2,
|
|
633
|
+
codec: userCodec,
|
|
634
|
+
fetchInit,
|
|
635
|
+
log
|
|
636
|
+
});
|
|
637
|
+
const contextValue = useMemo4(() => ({ user: value, setUser: setValue, isLoading, error }), [value, setValue, isLoading, error]);
|
|
638
|
+
return /* @__PURE__ */ jsx5(CTX2.Provider, {
|
|
639
|
+
value: contextValue,
|
|
640
|
+
children
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
function useSSOContext() {
|
|
644
|
+
const context = useContext4(CTX2);
|
|
645
|
+
if (context === undefined) {
|
|
646
|
+
throw new Error("useSSOContext must be used within a SSOProvider");
|
|
647
|
+
}
|
|
648
|
+
return context;
|
|
649
|
+
}
|
|
650
|
+
function useOptionalSSOContext() {
|
|
651
|
+
return useContext4(CTX2);
|
|
652
|
+
}
|
|
653
|
+
async function logout(logoutUrl, options) {
|
|
654
|
+
return logoutResource(logoutUrl, { defaultStorageKey: DEFAULT_STORAGE_KEY2, logoutEventName: LOGOUT_EVENT2, providerLabel: PROVIDER_LABEL2 }, options);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// packages/react/src/sign-in-loading.tsx
|
|
658
|
+
import { jsx as jsx6, Fragment } from "react/jsx-runtime";
|
|
659
|
+
function SignInLoading({
|
|
660
|
+
complete = false,
|
|
661
|
+
audience = "workforce",
|
|
662
|
+
children
|
|
663
|
+
}) {
|
|
664
|
+
const sso = useOptionalSSOContext();
|
|
665
|
+
const ciam = useOptionalCIAMContext();
|
|
666
|
+
const isLoading = audience === "customer" ? !!ciam?.isLoading : !!sso?.isLoading;
|
|
667
|
+
if (isLoading && !complete)
|
|
668
|
+
return /* @__PURE__ */ jsx6(Fragment, {
|
|
669
|
+
children
|
|
670
|
+
});
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
// packages/react/src/signed-in.tsx
|
|
674
|
+
import { jsx as jsx7, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
675
|
+
function SignedIn({
|
|
676
|
+
audience = "workforce",
|
|
677
|
+
children
|
|
678
|
+
}) {
|
|
679
|
+
const sso = useOptionalSSOContext();
|
|
680
|
+
const ciam = useOptionalCIAMContext();
|
|
681
|
+
const present = audience === "customer" ? !!ciam?.customer : !!sso?.user;
|
|
682
|
+
if (present)
|
|
683
|
+
return /* @__PURE__ */ jsx7(Fragment2, {
|
|
684
|
+
children
|
|
685
|
+
});
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
// packages/react/src/signed-out.tsx
|
|
689
|
+
import { jsx as jsx8, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
690
|
+
function SignedOut({
|
|
691
|
+
audience = "workforce",
|
|
692
|
+
children
|
|
693
|
+
}) {
|
|
694
|
+
const sso = useOptionalSSOContext();
|
|
695
|
+
const ciam = useOptionalCIAMContext();
|
|
696
|
+
const present = audience === "customer" ? !!ciam?.customer : !!sso?.user;
|
|
697
|
+
const isLoading = audience === "customer" ? !!ciam?.isLoading : !!sso?.isLoading;
|
|
698
|
+
if (present || isLoading)
|
|
699
|
+
return null;
|
|
700
|
+
return /* @__PURE__ */ jsx8(Fragment3, {
|
|
701
|
+
children
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
export {
|
|
705
|
+
CIAMProvider,
|
|
706
|
+
CIAMProviderList,
|
|
707
|
+
SSOProvider,
|
|
708
|
+
SessionsProvider,
|
|
709
|
+
SignInLoading,
|
|
710
|
+
SignedIn,
|
|
711
|
+
SignedOut,
|
|
712
|
+
TenantProvider,
|
|
713
|
+
createTenantUrls,
|
|
714
|
+
defaultLogger,
|
|
715
|
+
getCustomer,
|
|
716
|
+
logout,
|
|
717
|
+
logoutCustomer,
|
|
718
|
+
silentLogger5 as silentLogger,
|
|
719
|
+
useCIAMContext,
|
|
720
|
+
useCIAMProviders,
|
|
721
|
+
useOptionalCIAMContext,
|
|
722
|
+
useOptionalSSOContext,
|
|
723
|
+
useOptionalTenant,
|
|
724
|
+
useSSOContext,
|
|
725
|
+
useSessions,
|
|
726
|
+
useTenant,
|
|
727
|
+
useTenants
|
|
728
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ionite/react",
|
|
3
|
-
"version": "0.0.24-beta.
|
|
3
|
+
"version": "0.0.24-beta.20260930.19",
|
|
4
4
|
"description": "ionite React Components",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "ionite",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"./package.json": "./package.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@ionite/core": "0.0.24-beta.
|
|
27
|
+
"@ionite/core": "0.0.24-beta.20260930.19"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0",
|