@athenaintel/react 0.10.3 → 0.10.5
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/auth.cjs +5 -3
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +5 -3
- package/dist/auth.js.map +1 -1
- package/dist/index.cjs +106 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +44 -0
- package/dist/index.js +106 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/auth.cjs
CHANGED
|
@@ -57,6 +57,8 @@ function AthenaAuthBridge({
|
|
|
57
57
|
}) {
|
|
58
58
|
const authInfo = react.useAuthInfo();
|
|
59
59
|
const logoutFn = react.useLogoutFunction();
|
|
60
|
+
const logoutFnRef = React.useRef(logoutFn);
|
|
61
|
+
logoutFnRef.current = logoutFn;
|
|
60
62
|
const prevAuthStateRef = React.useRef(null);
|
|
61
63
|
const authState = React.useMemo(() => {
|
|
62
64
|
var _a, _b;
|
|
@@ -80,7 +82,7 @@ function AthenaAuthBridge({
|
|
|
80
82
|
user: null,
|
|
81
83
|
accessToken: null,
|
|
82
84
|
orgs: [],
|
|
83
|
-
logout: async (redirect = true) =>
|
|
85
|
+
logout: async (redirect = true) => logoutFnRef.current(redirect)
|
|
84
86
|
};
|
|
85
87
|
}
|
|
86
88
|
const prev = prevAuthStateRef.current;
|
|
@@ -107,11 +109,11 @@ function AthenaAuthBridge({
|
|
|
107
109
|
user,
|
|
108
110
|
accessToken: authInfo.accessToken,
|
|
109
111
|
orgs,
|
|
110
|
-
logout: async (redirect = true) =>
|
|
112
|
+
logout: async (redirect = true) => logoutFnRef.current(redirect)
|
|
111
113
|
};
|
|
112
114
|
prevAuthStateRef.current = newState;
|
|
113
115
|
return newState;
|
|
114
|
-
}, [authInfo
|
|
116
|
+
}, [authInfo]);
|
|
115
117
|
if (authState.isLoading) {
|
|
116
118
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: displayWhileLoading ?? null });
|
|
117
119
|
}
|
package/dist/auth.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.cjs","sources":["../src/auth/AthenaAuthProvider.tsx","../src/auth/AthenaSSOProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFn(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFn(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n }, [authInfo, logoutFn]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type {\n AthenaAuthState,\n AthenaOrg,\n AthenaSSOUserInfo,\n AthenaUser,\n} from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaSSOProviderProps {\n children: ReactNode;\n\n /**\n * Base URL used to resolve the Athena SSO endpoints.\n *\n * When omitted, the provider defaults to same-origin relative paths:\n * `/api/sso/userinfo`, `/api/sso/initiate`, and `/api/sso/logout`.\n *\n * Use this when your frontend and backend live on different origins\n * (for example `https://app.example.com` + `https://api.example.com`).\n */\n ssoBaseUrl?: string;\n\n /**\n * Optional override for the SSO user-info endpoint that returns the\n * authenticated user's data and access token.\n *\n * The endpoint must return JSON matching `AthenaSSOUserInfo`:\n * ```json\n * {\n * \"user\": { \"user_id\": \"…\", \"email\": \"…\", … },\n * \"orgMemberInfos\": [{ \"orgId\": \"…\", \"orgName\": \"…\", \"urlSafeOrgName\": \"…\" }],\n * \"accessToken\": \"…\"\n * }\n * ```\n *\n * The request is sent with `credentials: 'include'` so that\n * session cookies are forwarded automatically.\n */\n ssoUserInfoUrl?: string;\n\n /**\n * Optional override for the URL to redirect the user to when no valid\n * SSO session exists.\n * This is typically the SSO initiation endpoint on your backend\n * (e.g. `https://api.yourdomain.com/api/sso/initiate`).\n */\n ssoLoginUrl?: string;\n\n /**\n * Optional override for the backend SSO logout endpoint.\n * Defaults to `/api/sso/logout` (or `${ssoBaseUrl}/api/sso/logout`).\n */\n ssoLogoutUrl?: string;\n\n /**\n * Custom element displayed while the SSO session is being verified.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n\n /**\n * Custom element displayed when SSO authentication fails.\n * If not provided, the user is automatically redirected to `ssoLoginUrl`.\n */\n displayOnError?: ReactNode;\n\n /**\n * Re-validate the SSO session when the window regains focus.\n * Enabled by default so token expiry and user switching are picked up\n * without requiring a full page reload.\n */\n revalidateOnWindowFocus?: boolean;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\ntype AthenaSSOError = 'unauthenticated' | 'invalid_response' | 'request_failed';\n\nconst DEFAULT_SSO_PATHS = {\n userInfo: '/api/sso/userinfo',\n login: '/api/sso/initiate',\n logout: '/api/sso/logout',\n} as const;\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '');\n\nfunction resolveSSOUrl({\n baseUrl,\n overrideUrl,\n defaultPath,\n}: {\n baseUrl?: string;\n overrideUrl?: string;\n defaultPath: string;\n}): string {\n if (overrideUrl) {\n return overrideUrl;\n }\n\n if (!baseUrl) {\n return defaultPath;\n }\n\n return `${trimTrailingSlash(baseUrl)}${defaultPath}`;\n}\n\nfunction isValidSSOUserInfo(data: unknown): data is AthenaSSOUserInfo {\n if (!data || typeof data !== 'object') {\n return false;\n }\n\n const candidate = data as {\n accessToken?: unknown;\n orgMemberInfos?: unknown;\n user?: {\n user_id?: unknown;\n email?: unknown;\n };\n };\n\n return (\n typeof candidate.accessToken === 'string' &&\n Array.isArray(candidate.orgMemberInfos) &&\n !!candidate.user &&\n typeof candidate.user.user_id === 'string' &&\n typeof candidate.user.email === 'string'\n );\n}\n\n/** Map the backend SSO response to the SDK's user/org types. */\nfunction mapSSOResponse(data: AthenaSSOUserInfo): {\n user: AthenaUser;\n orgs: AthenaOrg[];\n accessToken: string;\n} {\n const user: AthenaUser = {\n userId: data.user.user_id,\n email: data.user.email,\n firstName: data.user.first_name || undefined,\n lastName: data.user.last_name || undefined,\n username: data.user.username || undefined,\n pictureUrl: data.user.picture_url || undefined,\n properties: data.user.properties,\n };\n\n const orgs: AthenaOrg[] = data.orgMemberInfos.map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n }));\n\n return { user, orgs, accessToken: data.accessToken };\n}\n\n// ─── Provider ─────────────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by an external SSO\n * identity provider (e.g. Microsoft Entra / Azure AD, Okta, etc.).\n *\n * The SSO login flow is handled entirely by your backend. This provider\n * verifies the session by fetching `ssoUserInfoUrl` and exposes the\n * same `AthenaAuthState` context that `AthenaAuthProvider` (PropelAuth)\n * provides, so downstream components like `<AthenaProvider>` and\n * `useAthenaAuth()` work identically.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n *\n * @example\n * ```tsx\n * import { AthenaSSOProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaSSOProvider\n * ssoUserInfoUrl=\"https://api.yourdomain.com/api/sso/userinfo\"\n * ssoLoginUrl=\"https://api.yourdomain.com/api/sso/initiate\"\n * >\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaSSOProvider>\n * );\n * }\n * ```\n */\nexport function AthenaSSOProvider({\n children,\n ssoBaseUrl,\n ssoUserInfoUrl,\n ssoLoginUrl,\n ssoLogoutUrl,\n displayWhileLoading,\n displayOnError,\n revalidateOnWindowFocus = false,\n}: AthenaSSOProviderProps) {\n const [ssoData, setSSOData] = useState<AthenaSSOUserInfo | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<AthenaSSOError | null>(null);\n const activeRequestRef = useRef<AbortController | null>(null);\n\n const ssoUrls = useMemo(\n () => ({\n userInfo: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoUserInfoUrl,\n defaultPath: DEFAULT_SSO_PATHS.userInfo,\n }),\n login: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLoginUrl,\n defaultPath: DEFAULT_SSO_PATHS.login,\n }),\n logout: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLogoutUrl,\n defaultPath: DEFAULT_SSO_PATHS.logout,\n }),\n }),\n [ssoBaseUrl, ssoUserInfoUrl, ssoLoginUrl, ssoLogoutUrl],\n );\n\n const refreshSession = useCallback(\n async ({ showLoading = false }: { showLoading?: boolean } = {}): Promise<void> => {\n activeRequestRef.current?.abort();\n const abortController = new AbortController();\n activeRequestRef.current = abortController;\n\n if (showLoading) {\n setLoading(true);\n setError(null);\n setSSOData(null);\n }\n\n try {\n const response = await fetch(ssoUrls.userInfo, {\n credentials: 'include',\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n setSSOData(null);\n setError('unauthenticated');\n } else {\n setError('request_failed');\n }\n return;\n }\n\n const data: unknown = await response.json();\n\n if (!isValidSSOUserInfo(data)) {\n console.error(\n '[AthenaSDK] Invalid SSO userinfo response: expected \"user.user_id\", \"user.email\", \"orgMemberInfos\", and \"accessToken\"',\n );\n setSSOData(null);\n setError('invalid_response');\n return;\n }\n\n setSSOData((prev) => {\n if (\n prev &&\n prev.accessToken === data.accessToken &&\n prev.user.user_id === data.user.user_id\n ) {\n return prev;\n }\n return data;\n });\n setError(null);\n } catch (fetchError) {\n if (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n return;\n }\n\n setError('request_failed');\n } finally {\n if (activeRequestRef.current === abortController) {\n activeRequestRef.current = null;\n setLoading(false);\n }\n }\n },\n [ssoUrls.userInfo],\n );\n\n useEffect(() => {\n void refreshSession({ showLoading: true });\n\n return () => {\n activeRequestRef.current?.abort();\n };\n }, [refreshSession]);\n\n useEffect(() => {\n if (!revalidateOnWindowFocus || typeof window === 'undefined') {\n return;\n }\n\n const handleFocus = () => {\n void refreshSession();\n };\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n void refreshSession();\n }\n };\n\n window.addEventListener('focus', handleFocus);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [revalidateOnWindowFocus, refreshSession]);\n\n // Redirect to SSO login when session is invalid and no error UI provided.\n useEffect(() => {\n if (\n !loading &&\n error === 'unauthenticated' &&\n !displayOnError &&\n typeof window !== 'undefined'\n ) {\n window.location.assign(ssoUrls.login);\n }\n }, [loading, error, displayOnError, ssoUrls.login]);\n\n const logout = useCallback(async (redirectOnLogout = true) => {\n activeRequestRef.current?.abort();\n\n try {\n await fetch(ssoUrls.logout, {\n method: 'POST',\n credentials: 'include',\n });\n } catch (logoutError) {\n console.error('[AthenaSDK] Failed to invalidate SSO session during logout', logoutError);\n }\n\n setSSOData(null);\n setError(null);\n setLoading(false);\n\n if (redirectOnLogout && typeof window !== 'undefined') {\n window.location.assign(ssoUrls.login);\n }\n }, [ssoUrls.login, ssoUrls.logout]);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (loading) {\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (error === 'request_failed' && ssoData) {\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }\n\n if (error || !ssoData) {\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout,\n };\n }\n\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }, [loading, error, ssoData, logout]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n if (!authState.isLoggedIn) {\n return displayOnError ? <>{displayOnError}</> : null;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n"],"names":["useState","useEffect","useAuthInfo","useLogoutFunction","useRef","useMemo","jsx","Fragment","AthenaAuthContext","RequiredAuthProvider","RedirectToLogin","useCallback","user","orgs","accessToken"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.cjs","sources":["../src/auth/AthenaAuthProvider.tsx","../src/auth/AthenaSSOProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type {\n AthenaAuthState,\n AthenaOrg,\n AthenaSSOUserInfo,\n AthenaUser,\n} from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaSSOProviderProps {\n children: ReactNode;\n\n /**\n * Base URL used to resolve the Athena SSO endpoints.\n *\n * When omitted, the provider defaults to same-origin relative paths:\n * `/api/sso/userinfo`, `/api/sso/initiate`, and `/api/sso/logout`.\n *\n * Use this when your frontend and backend live on different origins\n * (for example `https://app.example.com` + `https://api.example.com`).\n */\n ssoBaseUrl?: string;\n\n /**\n * Optional override for the SSO user-info endpoint that returns the\n * authenticated user's data and access token.\n *\n * The endpoint must return JSON matching `AthenaSSOUserInfo`:\n * ```json\n * {\n * \"user\": { \"user_id\": \"…\", \"email\": \"…\", … },\n * \"orgMemberInfos\": [{ \"orgId\": \"…\", \"orgName\": \"…\", \"urlSafeOrgName\": \"…\" }],\n * \"accessToken\": \"…\"\n * }\n * ```\n *\n * The request is sent with `credentials: 'include'` so that\n * session cookies are forwarded automatically.\n */\n ssoUserInfoUrl?: string;\n\n /**\n * Optional override for the URL to redirect the user to when no valid\n * SSO session exists.\n * This is typically the SSO initiation endpoint on your backend\n * (e.g. `https://api.yourdomain.com/api/sso/initiate`).\n */\n ssoLoginUrl?: string;\n\n /**\n * Optional override for the backend SSO logout endpoint.\n * Defaults to `/api/sso/logout` (or `${ssoBaseUrl}/api/sso/logout`).\n */\n ssoLogoutUrl?: string;\n\n /**\n * Custom element displayed while the SSO session is being verified.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n\n /**\n * Custom element displayed when SSO authentication fails.\n * If not provided, the user is automatically redirected to `ssoLoginUrl`.\n */\n displayOnError?: ReactNode;\n\n /**\n * Re-validate the SSO session when the window regains focus.\n * Disabled by default. When enabled, token expiry and user switching\n * are picked up without requiring a full page reload.\n */\n revalidateOnWindowFocus?: boolean;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\ntype AthenaSSOError = 'unauthenticated' | 'invalid_response' | 'request_failed';\n\nconst DEFAULT_SSO_PATHS = {\n userInfo: '/api/sso/userinfo',\n login: '/api/sso/initiate',\n logout: '/api/sso/logout',\n} as const;\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '');\n\nfunction resolveSSOUrl({\n baseUrl,\n overrideUrl,\n defaultPath,\n}: {\n baseUrl?: string;\n overrideUrl?: string;\n defaultPath: string;\n}): string {\n if (overrideUrl) {\n return overrideUrl;\n }\n\n if (!baseUrl) {\n return defaultPath;\n }\n\n return `${trimTrailingSlash(baseUrl)}${defaultPath}`;\n}\n\nfunction isValidSSOUserInfo(data: unknown): data is AthenaSSOUserInfo {\n if (!data || typeof data !== 'object') {\n return false;\n }\n\n const candidate = data as {\n accessToken?: unknown;\n orgMemberInfos?: unknown;\n user?: {\n user_id?: unknown;\n email?: unknown;\n };\n };\n\n return (\n typeof candidate.accessToken === 'string' &&\n Array.isArray(candidate.orgMemberInfos) &&\n !!candidate.user &&\n typeof candidate.user.user_id === 'string' &&\n typeof candidate.user.email === 'string'\n );\n}\n\n/** Map the backend SSO response to the SDK's user/org types. */\nfunction mapSSOResponse(data: AthenaSSOUserInfo): {\n user: AthenaUser;\n orgs: AthenaOrg[];\n accessToken: string;\n} {\n const user: AthenaUser = {\n userId: data.user.user_id,\n email: data.user.email,\n firstName: data.user.first_name || undefined,\n lastName: data.user.last_name || undefined,\n username: data.user.username || undefined,\n pictureUrl: data.user.picture_url || undefined,\n properties: data.user.properties,\n };\n\n const orgs: AthenaOrg[] = data.orgMemberInfos.map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n }));\n\n return { user, orgs, accessToken: data.accessToken };\n}\n\n// ─── Provider ─────────────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by an external SSO\n * identity provider (e.g. Microsoft Entra / Azure AD, Okta, etc.).\n *\n * The SSO login flow is handled entirely by your backend. This provider\n * verifies the session by fetching `ssoUserInfoUrl` and exposes the\n * same `AthenaAuthState` context that `AthenaAuthProvider` (PropelAuth)\n * provides, so downstream components like `<AthenaProvider>` and\n * `useAthenaAuth()` work identically.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n *\n * @example\n * ```tsx\n * import { AthenaSSOProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaSSOProvider\n * ssoUserInfoUrl=\"https://api.yourdomain.com/api/sso/userinfo\"\n * ssoLoginUrl=\"https://api.yourdomain.com/api/sso/initiate\"\n * >\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaSSOProvider>\n * );\n * }\n * ```\n */\nexport function AthenaSSOProvider({\n children,\n ssoBaseUrl,\n ssoUserInfoUrl,\n ssoLoginUrl,\n ssoLogoutUrl,\n displayWhileLoading,\n displayOnError,\n revalidateOnWindowFocus = false,\n}: AthenaSSOProviderProps) {\n const [ssoData, setSSOData] = useState<AthenaSSOUserInfo | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<AthenaSSOError | null>(null);\n const activeRequestRef = useRef<AbortController | null>(null);\n\n const ssoUrls = useMemo(\n () => ({\n userInfo: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoUserInfoUrl,\n defaultPath: DEFAULT_SSO_PATHS.userInfo,\n }),\n login: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLoginUrl,\n defaultPath: DEFAULT_SSO_PATHS.login,\n }),\n logout: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLogoutUrl,\n defaultPath: DEFAULT_SSO_PATHS.logout,\n }),\n }),\n [ssoBaseUrl, ssoUserInfoUrl, ssoLoginUrl, ssoLogoutUrl],\n );\n\n const refreshSession = useCallback(\n async ({ showLoading = false }: { showLoading?: boolean } = {}): Promise<void> => {\n activeRequestRef.current?.abort();\n const abortController = new AbortController();\n activeRequestRef.current = abortController;\n\n if (showLoading) {\n setLoading(true);\n setError(null);\n setSSOData(null);\n }\n\n try {\n const response = await fetch(ssoUrls.userInfo, {\n credentials: 'include',\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n setSSOData(null);\n setError('unauthenticated');\n } else {\n setError('request_failed');\n }\n return;\n }\n\n const data: unknown = await response.json();\n\n if (!isValidSSOUserInfo(data)) {\n console.error(\n '[AthenaSDK] Invalid SSO userinfo response: expected \"user.user_id\", \"user.email\", \"orgMemberInfos\", and \"accessToken\"',\n );\n setSSOData(null);\n setError('invalid_response');\n return;\n }\n\n setSSOData((prev) => {\n if (\n prev &&\n prev.accessToken === data.accessToken &&\n prev.user.user_id === data.user.user_id\n ) {\n return prev;\n }\n return data;\n });\n setError(null);\n } catch (fetchError) {\n if (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n return;\n }\n\n setError('request_failed');\n } finally {\n if (activeRequestRef.current === abortController) {\n activeRequestRef.current = null;\n setLoading(false);\n }\n }\n },\n [ssoUrls.userInfo],\n );\n\n useEffect(() => {\n void refreshSession({ showLoading: true });\n\n return () => {\n activeRequestRef.current?.abort();\n };\n }, [refreshSession]);\n\n useEffect(() => {\n if (!revalidateOnWindowFocus || typeof window === 'undefined') {\n return;\n }\n\n const handleFocus = () => {\n void refreshSession();\n };\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n void refreshSession();\n }\n };\n\n window.addEventListener('focus', handleFocus);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [revalidateOnWindowFocus, refreshSession]);\n\n // Redirect to SSO login when session is invalid and no error UI provided.\n useEffect(() => {\n if (\n !loading &&\n error === 'unauthenticated' &&\n !displayOnError &&\n typeof window !== 'undefined'\n ) {\n window.location.assign(ssoUrls.login);\n }\n }, [loading, error, displayOnError, ssoUrls.login]);\n\n const logout = useCallback(async (redirectOnLogout = true) => {\n activeRequestRef.current?.abort();\n\n try {\n await fetch(ssoUrls.logout, {\n method: 'POST',\n credentials: 'include',\n });\n } catch (logoutError) {\n console.error('[AthenaSDK] Failed to invalidate SSO session during logout', logoutError);\n }\n\n setSSOData(null);\n setError(null);\n setLoading(false);\n\n if (redirectOnLogout && typeof window !== 'undefined') {\n window.location.assign(ssoUrls.login);\n }\n }, [ssoUrls.login, ssoUrls.logout]);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (loading) {\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (error === 'request_failed' && ssoData) {\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }\n\n if (error || !ssoData) {\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout,\n };\n }\n\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }, [loading, error, ssoData, logout]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n if (!authState.isLoggedIn) {\n return displayOnError ? <>{displayOnError}</> : null;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n"],"names":["useState","useEffect","useAuthInfo","useLogoutFunction","useRef","useMemo","jsx","Fragment","AthenaAuthContext","RequiredAuthProvider","RedirectToLogin","useCallback","user","orgs","accessToken"],"mappings":"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"}
|
package/dist/auth.d.ts
CHANGED
|
@@ -174,8 +174,8 @@ export declare interface AthenaSSOProviderProps {
|
|
|
174
174
|
displayOnError?: ReactNode;
|
|
175
175
|
/**
|
|
176
176
|
* Re-validate the SSO session when the window regains focus.
|
|
177
|
-
*
|
|
178
|
-
* without requiring a full page reload.
|
|
177
|
+
* Disabled by default. When enabled, token expiry and user switching
|
|
178
|
+
* are picked up without requiring a full page reload.
|
|
179
179
|
*/
|
|
180
180
|
revalidateOnWindowFocus?: boolean;
|
|
181
181
|
}
|
package/dist/auth.js
CHANGED
|
@@ -56,6 +56,8 @@ function AthenaAuthBridge({
|
|
|
56
56
|
}) {
|
|
57
57
|
const authInfo = useAuthInfo();
|
|
58
58
|
const logoutFn = useLogoutFunction();
|
|
59
|
+
const logoutFnRef = useRef(logoutFn);
|
|
60
|
+
logoutFnRef.current = logoutFn;
|
|
59
61
|
const prevAuthStateRef = useRef(null);
|
|
60
62
|
const authState = useMemo(() => {
|
|
61
63
|
var _a, _b;
|
|
@@ -79,7 +81,7 @@ function AthenaAuthBridge({
|
|
|
79
81
|
user: null,
|
|
80
82
|
accessToken: null,
|
|
81
83
|
orgs: [],
|
|
82
|
-
logout: async (redirect = true) =>
|
|
84
|
+
logout: async (redirect = true) => logoutFnRef.current(redirect)
|
|
83
85
|
};
|
|
84
86
|
}
|
|
85
87
|
const prev = prevAuthStateRef.current;
|
|
@@ -106,11 +108,11 @@ function AthenaAuthBridge({
|
|
|
106
108
|
user,
|
|
107
109
|
accessToken: authInfo.accessToken,
|
|
108
110
|
orgs,
|
|
109
|
-
logout: async (redirect = true) =>
|
|
111
|
+
logout: async (redirect = true) => logoutFnRef.current(redirect)
|
|
110
112
|
};
|
|
111
113
|
prevAuthStateRef.current = newState;
|
|
112
114
|
return newState;
|
|
113
|
-
}, [authInfo
|
|
115
|
+
}, [authInfo]);
|
|
114
116
|
if (authState.isLoading) {
|
|
115
117
|
return /* @__PURE__ */ jsx(Fragment, { children: displayWhileLoading ?? null });
|
|
116
118
|
}
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sources":["../src/auth/AthenaAuthProvider.tsx","../src/auth/AthenaSSOProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFn(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFn(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n }, [authInfo, logoutFn]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type {\n AthenaAuthState,\n AthenaOrg,\n AthenaSSOUserInfo,\n AthenaUser,\n} from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaSSOProviderProps {\n children: ReactNode;\n\n /**\n * Base URL used to resolve the Athena SSO endpoints.\n *\n * When omitted, the provider defaults to same-origin relative paths:\n * `/api/sso/userinfo`, `/api/sso/initiate`, and `/api/sso/logout`.\n *\n * Use this when your frontend and backend live on different origins\n * (for example `https://app.example.com` + `https://api.example.com`).\n */\n ssoBaseUrl?: string;\n\n /**\n * Optional override for the SSO user-info endpoint that returns the\n * authenticated user's data and access token.\n *\n * The endpoint must return JSON matching `AthenaSSOUserInfo`:\n * ```json\n * {\n * \"user\": { \"user_id\": \"…\", \"email\": \"…\", … },\n * \"orgMemberInfos\": [{ \"orgId\": \"…\", \"orgName\": \"…\", \"urlSafeOrgName\": \"…\" }],\n * \"accessToken\": \"…\"\n * }\n * ```\n *\n * The request is sent with `credentials: 'include'` so that\n * session cookies are forwarded automatically.\n */\n ssoUserInfoUrl?: string;\n\n /**\n * Optional override for the URL to redirect the user to when no valid\n * SSO session exists.\n * This is typically the SSO initiation endpoint on your backend\n * (e.g. `https://api.yourdomain.com/api/sso/initiate`).\n */\n ssoLoginUrl?: string;\n\n /**\n * Optional override for the backend SSO logout endpoint.\n * Defaults to `/api/sso/logout` (or `${ssoBaseUrl}/api/sso/logout`).\n */\n ssoLogoutUrl?: string;\n\n /**\n * Custom element displayed while the SSO session is being verified.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n\n /**\n * Custom element displayed when SSO authentication fails.\n * If not provided, the user is automatically redirected to `ssoLoginUrl`.\n */\n displayOnError?: ReactNode;\n\n /**\n * Re-validate the SSO session when the window regains focus.\n * Enabled by default so token expiry and user switching are picked up\n * without requiring a full page reload.\n */\n revalidateOnWindowFocus?: boolean;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\ntype AthenaSSOError = 'unauthenticated' | 'invalid_response' | 'request_failed';\n\nconst DEFAULT_SSO_PATHS = {\n userInfo: '/api/sso/userinfo',\n login: '/api/sso/initiate',\n logout: '/api/sso/logout',\n} as const;\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '');\n\nfunction resolveSSOUrl({\n baseUrl,\n overrideUrl,\n defaultPath,\n}: {\n baseUrl?: string;\n overrideUrl?: string;\n defaultPath: string;\n}): string {\n if (overrideUrl) {\n return overrideUrl;\n }\n\n if (!baseUrl) {\n return defaultPath;\n }\n\n return `${trimTrailingSlash(baseUrl)}${defaultPath}`;\n}\n\nfunction isValidSSOUserInfo(data: unknown): data is AthenaSSOUserInfo {\n if (!data || typeof data !== 'object') {\n return false;\n }\n\n const candidate = data as {\n accessToken?: unknown;\n orgMemberInfos?: unknown;\n user?: {\n user_id?: unknown;\n email?: unknown;\n };\n };\n\n return (\n typeof candidate.accessToken === 'string' &&\n Array.isArray(candidate.orgMemberInfos) &&\n !!candidate.user &&\n typeof candidate.user.user_id === 'string' &&\n typeof candidate.user.email === 'string'\n );\n}\n\n/** Map the backend SSO response to the SDK's user/org types. */\nfunction mapSSOResponse(data: AthenaSSOUserInfo): {\n user: AthenaUser;\n orgs: AthenaOrg[];\n accessToken: string;\n} {\n const user: AthenaUser = {\n userId: data.user.user_id,\n email: data.user.email,\n firstName: data.user.first_name || undefined,\n lastName: data.user.last_name || undefined,\n username: data.user.username || undefined,\n pictureUrl: data.user.picture_url || undefined,\n properties: data.user.properties,\n };\n\n const orgs: AthenaOrg[] = data.orgMemberInfos.map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n }));\n\n return { user, orgs, accessToken: data.accessToken };\n}\n\n// ─── Provider ─────────────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by an external SSO\n * identity provider (e.g. Microsoft Entra / Azure AD, Okta, etc.).\n *\n * The SSO login flow is handled entirely by your backend. This provider\n * verifies the session by fetching `ssoUserInfoUrl` and exposes the\n * same `AthenaAuthState` context that `AthenaAuthProvider` (PropelAuth)\n * provides, so downstream components like `<AthenaProvider>` and\n * `useAthenaAuth()` work identically.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n *\n * @example\n * ```tsx\n * import { AthenaSSOProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaSSOProvider\n * ssoUserInfoUrl=\"https://api.yourdomain.com/api/sso/userinfo\"\n * ssoLoginUrl=\"https://api.yourdomain.com/api/sso/initiate\"\n * >\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaSSOProvider>\n * );\n * }\n * ```\n */\nexport function AthenaSSOProvider({\n children,\n ssoBaseUrl,\n ssoUserInfoUrl,\n ssoLoginUrl,\n ssoLogoutUrl,\n displayWhileLoading,\n displayOnError,\n revalidateOnWindowFocus = false,\n}: AthenaSSOProviderProps) {\n const [ssoData, setSSOData] = useState<AthenaSSOUserInfo | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<AthenaSSOError | null>(null);\n const activeRequestRef = useRef<AbortController | null>(null);\n\n const ssoUrls = useMemo(\n () => ({\n userInfo: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoUserInfoUrl,\n defaultPath: DEFAULT_SSO_PATHS.userInfo,\n }),\n login: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLoginUrl,\n defaultPath: DEFAULT_SSO_PATHS.login,\n }),\n logout: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLogoutUrl,\n defaultPath: DEFAULT_SSO_PATHS.logout,\n }),\n }),\n [ssoBaseUrl, ssoUserInfoUrl, ssoLoginUrl, ssoLogoutUrl],\n );\n\n const refreshSession = useCallback(\n async ({ showLoading = false }: { showLoading?: boolean } = {}): Promise<void> => {\n activeRequestRef.current?.abort();\n const abortController = new AbortController();\n activeRequestRef.current = abortController;\n\n if (showLoading) {\n setLoading(true);\n setError(null);\n setSSOData(null);\n }\n\n try {\n const response = await fetch(ssoUrls.userInfo, {\n credentials: 'include',\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n setSSOData(null);\n setError('unauthenticated');\n } else {\n setError('request_failed');\n }\n return;\n }\n\n const data: unknown = await response.json();\n\n if (!isValidSSOUserInfo(data)) {\n console.error(\n '[AthenaSDK] Invalid SSO userinfo response: expected \"user.user_id\", \"user.email\", \"orgMemberInfos\", and \"accessToken\"',\n );\n setSSOData(null);\n setError('invalid_response');\n return;\n }\n\n setSSOData((prev) => {\n if (\n prev &&\n prev.accessToken === data.accessToken &&\n prev.user.user_id === data.user.user_id\n ) {\n return prev;\n }\n return data;\n });\n setError(null);\n } catch (fetchError) {\n if (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n return;\n }\n\n setError('request_failed');\n } finally {\n if (activeRequestRef.current === abortController) {\n activeRequestRef.current = null;\n setLoading(false);\n }\n }\n },\n [ssoUrls.userInfo],\n );\n\n useEffect(() => {\n void refreshSession({ showLoading: true });\n\n return () => {\n activeRequestRef.current?.abort();\n };\n }, [refreshSession]);\n\n useEffect(() => {\n if (!revalidateOnWindowFocus || typeof window === 'undefined') {\n return;\n }\n\n const handleFocus = () => {\n void refreshSession();\n };\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n void refreshSession();\n }\n };\n\n window.addEventListener('focus', handleFocus);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [revalidateOnWindowFocus, refreshSession]);\n\n // Redirect to SSO login when session is invalid and no error UI provided.\n useEffect(() => {\n if (\n !loading &&\n error === 'unauthenticated' &&\n !displayOnError &&\n typeof window !== 'undefined'\n ) {\n window.location.assign(ssoUrls.login);\n }\n }, [loading, error, displayOnError, ssoUrls.login]);\n\n const logout = useCallback(async (redirectOnLogout = true) => {\n activeRequestRef.current?.abort();\n\n try {\n await fetch(ssoUrls.logout, {\n method: 'POST',\n credentials: 'include',\n });\n } catch (logoutError) {\n console.error('[AthenaSDK] Failed to invalidate SSO session during logout', logoutError);\n }\n\n setSSOData(null);\n setError(null);\n setLoading(false);\n\n if (redirectOnLogout && typeof window !== 'undefined') {\n window.location.assign(ssoUrls.login);\n }\n }, [ssoUrls.login, ssoUrls.logout]);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (loading) {\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (error === 'request_failed' && ssoData) {\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }\n\n if (error || !ssoData) {\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout,\n };\n }\n\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }, [loading, error, ssoData, logout]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n if (!authState.isLoggedIn) {\n return displayOnError ? <>{displayOnError}</> : null;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n"],"names":["user","orgs","accessToken"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.js","sources":["../src/auth/AthenaAuthProvider.tsx","../src/auth/AthenaSSOProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type {\n AthenaAuthState,\n AthenaOrg,\n AthenaSSOUserInfo,\n AthenaUser,\n} from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaSSOProviderProps {\n children: ReactNode;\n\n /**\n * Base URL used to resolve the Athena SSO endpoints.\n *\n * When omitted, the provider defaults to same-origin relative paths:\n * `/api/sso/userinfo`, `/api/sso/initiate`, and `/api/sso/logout`.\n *\n * Use this when your frontend and backend live on different origins\n * (for example `https://app.example.com` + `https://api.example.com`).\n */\n ssoBaseUrl?: string;\n\n /**\n * Optional override for the SSO user-info endpoint that returns the\n * authenticated user's data and access token.\n *\n * The endpoint must return JSON matching `AthenaSSOUserInfo`:\n * ```json\n * {\n * \"user\": { \"user_id\": \"…\", \"email\": \"…\", … },\n * \"orgMemberInfos\": [{ \"orgId\": \"…\", \"orgName\": \"…\", \"urlSafeOrgName\": \"…\" }],\n * \"accessToken\": \"…\"\n * }\n * ```\n *\n * The request is sent with `credentials: 'include'` so that\n * session cookies are forwarded automatically.\n */\n ssoUserInfoUrl?: string;\n\n /**\n * Optional override for the URL to redirect the user to when no valid\n * SSO session exists.\n * This is typically the SSO initiation endpoint on your backend\n * (e.g. `https://api.yourdomain.com/api/sso/initiate`).\n */\n ssoLoginUrl?: string;\n\n /**\n * Optional override for the backend SSO logout endpoint.\n * Defaults to `/api/sso/logout` (or `${ssoBaseUrl}/api/sso/logout`).\n */\n ssoLogoutUrl?: string;\n\n /**\n * Custom element displayed while the SSO session is being verified.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n\n /**\n * Custom element displayed when SSO authentication fails.\n * If not provided, the user is automatically redirected to `ssoLoginUrl`.\n */\n displayOnError?: ReactNode;\n\n /**\n * Re-validate the SSO session when the window regains focus.\n * Disabled by default. When enabled, token expiry and user switching\n * are picked up without requiring a full page reload.\n */\n revalidateOnWindowFocus?: boolean;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\ntype AthenaSSOError = 'unauthenticated' | 'invalid_response' | 'request_failed';\n\nconst DEFAULT_SSO_PATHS = {\n userInfo: '/api/sso/userinfo',\n login: '/api/sso/initiate',\n logout: '/api/sso/logout',\n} as const;\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '');\n\nfunction resolveSSOUrl({\n baseUrl,\n overrideUrl,\n defaultPath,\n}: {\n baseUrl?: string;\n overrideUrl?: string;\n defaultPath: string;\n}): string {\n if (overrideUrl) {\n return overrideUrl;\n }\n\n if (!baseUrl) {\n return defaultPath;\n }\n\n return `${trimTrailingSlash(baseUrl)}${defaultPath}`;\n}\n\nfunction isValidSSOUserInfo(data: unknown): data is AthenaSSOUserInfo {\n if (!data || typeof data !== 'object') {\n return false;\n }\n\n const candidate = data as {\n accessToken?: unknown;\n orgMemberInfos?: unknown;\n user?: {\n user_id?: unknown;\n email?: unknown;\n };\n };\n\n return (\n typeof candidate.accessToken === 'string' &&\n Array.isArray(candidate.orgMemberInfos) &&\n !!candidate.user &&\n typeof candidate.user.user_id === 'string' &&\n typeof candidate.user.email === 'string'\n );\n}\n\n/** Map the backend SSO response to the SDK's user/org types. */\nfunction mapSSOResponse(data: AthenaSSOUserInfo): {\n user: AthenaUser;\n orgs: AthenaOrg[];\n accessToken: string;\n} {\n const user: AthenaUser = {\n userId: data.user.user_id,\n email: data.user.email,\n firstName: data.user.first_name || undefined,\n lastName: data.user.last_name || undefined,\n username: data.user.username || undefined,\n pictureUrl: data.user.picture_url || undefined,\n properties: data.user.properties,\n };\n\n const orgs: AthenaOrg[] = data.orgMemberInfos.map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n }));\n\n return { user, orgs, accessToken: data.accessToken };\n}\n\n// ─── Provider ─────────────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by an external SSO\n * identity provider (e.g. Microsoft Entra / Azure AD, Okta, etc.).\n *\n * The SSO login flow is handled entirely by your backend. This provider\n * verifies the session by fetching `ssoUserInfoUrl` and exposes the\n * same `AthenaAuthState` context that `AthenaAuthProvider` (PropelAuth)\n * provides, so downstream components like `<AthenaProvider>` and\n * `useAthenaAuth()` work identically.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n *\n * @example\n * ```tsx\n * import { AthenaSSOProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaSSOProvider\n * ssoUserInfoUrl=\"https://api.yourdomain.com/api/sso/userinfo\"\n * ssoLoginUrl=\"https://api.yourdomain.com/api/sso/initiate\"\n * >\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaSSOProvider>\n * );\n * }\n * ```\n */\nexport function AthenaSSOProvider({\n children,\n ssoBaseUrl,\n ssoUserInfoUrl,\n ssoLoginUrl,\n ssoLogoutUrl,\n displayWhileLoading,\n displayOnError,\n revalidateOnWindowFocus = false,\n}: AthenaSSOProviderProps) {\n const [ssoData, setSSOData] = useState<AthenaSSOUserInfo | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<AthenaSSOError | null>(null);\n const activeRequestRef = useRef<AbortController | null>(null);\n\n const ssoUrls = useMemo(\n () => ({\n userInfo: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoUserInfoUrl,\n defaultPath: DEFAULT_SSO_PATHS.userInfo,\n }),\n login: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLoginUrl,\n defaultPath: DEFAULT_SSO_PATHS.login,\n }),\n logout: resolveSSOUrl({\n baseUrl: ssoBaseUrl,\n overrideUrl: ssoLogoutUrl,\n defaultPath: DEFAULT_SSO_PATHS.logout,\n }),\n }),\n [ssoBaseUrl, ssoUserInfoUrl, ssoLoginUrl, ssoLogoutUrl],\n );\n\n const refreshSession = useCallback(\n async ({ showLoading = false }: { showLoading?: boolean } = {}): Promise<void> => {\n activeRequestRef.current?.abort();\n const abortController = new AbortController();\n activeRequestRef.current = abortController;\n\n if (showLoading) {\n setLoading(true);\n setError(null);\n setSSOData(null);\n }\n\n try {\n const response = await fetch(ssoUrls.userInfo, {\n credentials: 'include',\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n setSSOData(null);\n setError('unauthenticated');\n } else {\n setError('request_failed');\n }\n return;\n }\n\n const data: unknown = await response.json();\n\n if (!isValidSSOUserInfo(data)) {\n console.error(\n '[AthenaSDK] Invalid SSO userinfo response: expected \"user.user_id\", \"user.email\", \"orgMemberInfos\", and \"accessToken\"',\n );\n setSSOData(null);\n setError('invalid_response');\n return;\n }\n\n setSSOData((prev) => {\n if (\n prev &&\n prev.accessToken === data.accessToken &&\n prev.user.user_id === data.user.user_id\n ) {\n return prev;\n }\n return data;\n });\n setError(null);\n } catch (fetchError) {\n if (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n return;\n }\n\n setError('request_failed');\n } finally {\n if (activeRequestRef.current === abortController) {\n activeRequestRef.current = null;\n setLoading(false);\n }\n }\n },\n [ssoUrls.userInfo],\n );\n\n useEffect(() => {\n void refreshSession({ showLoading: true });\n\n return () => {\n activeRequestRef.current?.abort();\n };\n }, [refreshSession]);\n\n useEffect(() => {\n if (!revalidateOnWindowFocus || typeof window === 'undefined') {\n return;\n }\n\n const handleFocus = () => {\n void refreshSession();\n };\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n void refreshSession();\n }\n };\n\n window.addEventListener('focus', handleFocus);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [revalidateOnWindowFocus, refreshSession]);\n\n // Redirect to SSO login when session is invalid and no error UI provided.\n useEffect(() => {\n if (\n !loading &&\n error === 'unauthenticated' &&\n !displayOnError &&\n typeof window !== 'undefined'\n ) {\n window.location.assign(ssoUrls.login);\n }\n }, [loading, error, displayOnError, ssoUrls.login]);\n\n const logout = useCallback(async (redirectOnLogout = true) => {\n activeRequestRef.current?.abort();\n\n try {\n await fetch(ssoUrls.logout, {\n method: 'POST',\n credentials: 'include',\n });\n } catch (logoutError) {\n console.error('[AthenaSDK] Failed to invalidate SSO session during logout', logoutError);\n }\n\n setSSOData(null);\n setError(null);\n setLoading(false);\n\n if (redirectOnLogout && typeof window !== 'undefined') {\n window.location.assign(ssoUrls.login);\n }\n }, [ssoUrls.login, ssoUrls.logout]);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (loading) {\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (error === 'request_failed' && ssoData) {\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }\n\n if (error || !ssoData) {\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout,\n };\n }\n\n const { user, orgs, accessToken } = mapSSOResponse(ssoData);\n\n return {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken,\n orgs,\n logout,\n };\n }, [loading, error, ssoData, logout]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n if (!authState.isLoggedIn) {\n return displayOnError ? <>{displayOnError}</> : null;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n"],"names":["user","orgs","accessToken"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1399,8 +1399,8 @@ const createStoreImpl = (createState) => {
|
|
|
1399
1399
|
return api;
|
|
1400
1400
|
};
|
|
1401
1401
|
const createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
|
|
1402
|
-
const identity = (arg) => arg;
|
|
1403
|
-
function useStore$1(api, selector = identity) {
|
|
1402
|
+
const identity$1 = (arg) => arg;
|
|
1403
|
+
function useStore$1(api, selector = identity$1) {
|
|
1404
1404
|
const slice2 = React.useSyncExternalStore(
|
|
1405
1405
|
api.subscribe,
|
|
1406
1406
|
React.useCallback(() => selector(api.getState()), [api, selector]),
|
|
@@ -9610,6 +9610,72 @@ const AssistantRuntimeProviderImpl = ({ children, aui, runtime }) => {
|
|
|
9610
9610
|
return jsxRuntime.jsxs(AssistantProviderBase, { runtime, aui: aui ?? null, children: [jsxRuntime.jsx(DevToolsRegistration, {}), jsxRuntime.jsx(ThreadPrimitiveViewportProvider, { children })] });
|
|
9611
9611
|
};
|
|
9612
9612
|
const AssistantRuntimeProvider = React.memo(AssistantRuntimeProviderImpl);
|
|
9613
|
+
const debugVerifyPrototype = (runtime, prototype) => {
|
|
9614
|
+
const unboundMethods = Object.getOwnPropertyNames(prototype).filter((methodStr) => {
|
|
9615
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, methodStr);
|
|
9616
|
+
const isMethod = descriptor && typeof descriptor.value === "function";
|
|
9617
|
+
if (!isMethod)
|
|
9618
|
+
return false;
|
|
9619
|
+
const methodName = methodStr;
|
|
9620
|
+
return isMethod && !methodName.startsWith("_") && methodName !== "constructor" && prototype[methodName] === runtime[methodName];
|
|
9621
|
+
});
|
|
9622
|
+
if (unboundMethods.length > 0) {
|
|
9623
|
+
throw new Error(`The following methods are not bound: ${JSON.stringify(unboundMethods)}`);
|
|
9624
|
+
}
|
|
9625
|
+
const prototypePrototype = Object.getPrototypeOf(prototype);
|
|
9626
|
+
if (prototypePrototype && prototypePrototype !== Object.prototype) {
|
|
9627
|
+
debugVerifyPrototype(runtime, prototypePrototype);
|
|
9628
|
+
}
|
|
9629
|
+
};
|
|
9630
|
+
const ensureBinding = (r2) => {
|
|
9631
|
+
var _a2;
|
|
9632
|
+
const runtime = r2;
|
|
9633
|
+
if (runtime.__isBound)
|
|
9634
|
+
return;
|
|
9635
|
+
(_a2 = runtime.__internal_bindMethods) == null ? void 0 : _a2.call(runtime);
|
|
9636
|
+
runtime.__isBound = true;
|
|
9637
|
+
if (process.env.NODE_ENV !== "production") {
|
|
9638
|
+
debugVerifyPrototype(runtime, Object.getPrototypeOf(runtime));
|
|
9639
|
+
}
|
|
9640
|
+
};
|
|
9641
|
+
function useRuntimeStateInternal(runtime, selector = identity) {
|
|
9642
|
+
ensureBinding(runtime);
|
|
9643
|
+
const slice2 = React.useSyncExternalStore(runtime.subscribe, () => selector(runtime.getState()), () => selector(runtime.getState()));
|
|
9644
|
+
React.useDebugValue(slice2);
|
|
9645
|
+
return slice2;
|
|
9646
|
+
}
|
|
9647
|
+
const identity = (arg) => arg;
|
|
9648
|
+
function createStateHookForRuntime(useRuntime) {
|
|
9649
|
+
function useStoreHook(param) {
|
|
9650
|
+
let optional = false;
|
|
9651
|
+
let selector;
|
|
9652
|
+
if (typeof param === "function") {
|
|
9653
|
+
selector = param;
|
|
9654
|
+
} else if (param) {
|
|
9655
|
+
optional = !!param.optional;
|
|
9656
|
+
selector = param.selector;
|
|
9657
|
+
}
|
|
9658
|
+
const store = useRuntime({ optional });
|
|
9659
|
+
if (!store)
|
|
9660
|
+
return null;
|
|
9661
|
+
return useRuntimeStateInternal(store, selector);
|
|
9662
|
+
}
|
|
9663
|
+
return useStoreHook;
|
|
9664
|
+
}
|
|
9665
|
+
function useAssistantRuntime(options) {
|
|
9666
|
+
var _a2, _b;
|
|
9667
|
+
const aui = useAui();
|
|
9668
|
+
const runtime = ((_b = (_a2 = aui.threads()).__internal_getAssistantRuntime) == null ? void 0 : _b.call(_a2)) ?? null;
|
|
9669
|
+
if (!runtime && !(options == null ? void 0 : options.optional)) {
|
|
9670
|
+
throw new Error("AssistantRuntime is not available");
|
|
9671
|
+
}
|
|
9672
|
+
return runtime;
|
|
9673
|
+
}
|
|
9674
|
+
const useThreadListRuntime = (opt) => {
|
|
9675
|
+
var _a2;
|
|
9676
|
+
return ((_a2 = useAssistantRuntime(opt)) == null ? void 0 : _a2.threads) ?? null;
|
|
9677
|
+
};
|
|
9678
|
+
const useThreadList = createStateHookForRuntime(useThreadListRuntime);
|
|
9613
9679
|
function useComposerRuntime(options) {
|
|
9614
9680
|
const aui = useAui();
|
|
9615
9681
|
const runtime = useAuiState(() => {
|
|
@@ -9621,6 +9687,18 @@ function useComposerRuntime(options) {
|
|
|
9621
9687
|
}
|
|
9622
9688
|
return runtime;
|
|
9623
9689
|
}
|
|
9690
|
+
function useThreadRuntime(options) {
|
|
9691
|
+
const aui = useAui();
|
|
9692
|
+
const runtime = useAuiState(() => {
|
|
9693
|
+
var _a2, _b;
|
|
9694
|
+
return aui.thread.source ? ((_b = (_a2 = aui.thread()).__internal_getRuntime) == null ? void 0 : _b.call(_a2)) ?? null : null;
|
|
9695
|
+
});
|
|
9696
|
+
if (!runtime && !(options == null ? void 0 : options.optional)) {
|
|
9697
|
+
throw new Error("ThreadRuntime is not available");
|
|
9698
|
+
}
|
|
9699
|
+
return runtime;
|
|
9700
|
+
}
|
|
9701
|
+
const useThread = createStateHookForRuntime(useThreadRuntime);
|
|
9624
9702
|
function setRef(ref, value) {
|
|
9625
9703
|
if (typeof ref === "function") {
|
|
9626
9704
|
return ref(value);
|
|
@@ -66547,6 +66625,31 @@ function ThreadListItem() {
|
|
|
66547
66625
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate text-left", children: /* @__PURE__ */ jsxRuntime.jsx(ThreadListItemPrimitiveTitle, { fallback: "Untitled" }) })
|
|
66548
66626
|
] }) });
|
|
66549
66627
|
}
|
|
66628
|
+
function useAthenaThreadManager() {
|
|
66629
|
+
const runtime = useAssistantRuntime({ optional: true });
|
|
66630
|
+
const threadId = useThread({ optional: true, selector: (s) => s.threadId });
|
|
66631
|
+
const threadList = useThreadList({ optional: true });
|
|
66632
|
+
const switchToThread = React.useCallback(
|
|
66633
|
+
(id) => runtime.threads.switchToThread(id),
|
|
66634
|
+
[runtime]
|
|
66635
|
+
);
|
|
66636
|
+
const switchToNewThread = React.useCallback(
|
|
66637
|
+
() => runtime.threads.switchToNewThread(),
|
|
66638
|
+
[runtime]
|
|
66639
|
+
);
|
|
66640
|
+
return React.useMemo(() => {
|
|
66641
|
+
if (!runtime || !threadList) {
|
|
66642
|
+
return null;
|
|
66643
|
+
}
|
|
66644
|
+
return {
|
|
66645
|
+
activeThreadId: threadId ?? null,
|
|
66646
|
+
threadIds: threadList.threadIds,
|
|
66647
|
+
isLoading: threadList.isLoading,
|
|
66648
|
+
switchToThread,
|
|
66649
|
+
switchToNewThread
|
|
66650
|
+
};
|
|
66651
|
+
}, [runtime, threadId, threadList, switchToThread, switchToNewThread]);
|
|
66652
|
+
}
|
|
66550
66653
|
function useAppendToComposer() {
|
|
66551
66654
|
const aui = useAui();
|
|
66552
66655
|
return React.useCallback(
|
|
@@ -66724,6 +66827,7 @@ exports.useAthenaCitationLinkHandler = useAthenaCitationLinkHandler;
|
|
|
66724
66827
|
exports.useAthenaConfig = useAthenaConfig;
|
|
66725
66828
|
exports.useAthenaLinkClickHandler = useAthenaLinkClickHandler;
|
|
66726
66829
|
exports.useAthenaRuntime = useAthenaRuntime;
|
|
66830
|
+
exports.useAthenaThreadManager = useAthenaThreadManager;
|
|
66727
66831
|
exports.useComposerAttachment = useComposerAttachment;
|
|
66728
66832
|
exports.useFileUpload = useFileUpload;
|
|
66729
66833
|
exports.useMentionSuggestions = useMentionSuggestions;
|