@thunderid/react 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/auth/Callback/OAuthCallback.cjs +10 -4
- package/dist/cjs/components/auth/Callback/OAuthCallback.cjs.map +1 -1
- package/dist/cjs/components/auth/Callback/TokenCallback.cjs +8 -7
- package/dist/cjs/components/auth/Callback/TokenCallback.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/AcceptInvite/BaseAcceptInvite.cjs +4 -3
- package/dist/cjs/components/presentation/auth/AcceptInvite/BaseAcceptInvite.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/SignIn/SignIn.cjs +7 -6
- package/dist/cjs/components/presentation/auth/SignIn/SignIn.cjs.map +1 -1
- package/dist/cjs/contexts/I18n/I18nProvider.cjs +2 -3
- package/dist/cjs/contexts/I18n/I18nProvider.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDContext.cjs +4 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDContext.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs +5 -2
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/cjs/utils/oauth.cjs +3 -2
- package/dist/cjs/utils/oauth.cjs.map +1 -1
- package/dist/components/auth/Callback/OAuthCallback.d.ts.map +1 -1
- package/dist/components/auth/Callback/OAuthCallback.js +12 -6
- package/dist/components/auth/Callback/OAuthCallback.js.map +1 -1
- package/dist/components/auth/Callback/TokenCallback.d.ts.map +1 -1
- package/dist/components/auth/Callback/TokenCallback.js +8 -7
- package/dist/components/auth/Callback/TokenCallback.js.map +1 -1
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.d.ts.map +1 -1
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.js +4 -3
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.js.map +1 -1
- package/dist/components/presentation/auth/SignIn/SignIn.d.ts.map +1 -1
- package/dist/components/presentation/auth/SignIn/SignIn.js +7 -6
- package/dist/components/presentation/auth/SignIn/SignIn.js.map +1 -1
- package/dist/contexts/I18n/I18nProvider.d.ts +6 -0
- package/dist/contexts/I18n/I18nProvider.d.ts.map +1 -1
- package/dist/contexts/I18n/I18nProvider.js +3 -4
- package/dist/contexts/I18n/I18nProvider.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts +5 -0
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.js +3 -1
- package/dist/contexts/ThunderID/ThunderIDContext.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js +6 -3
- package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/dist/utils/oauth.d.ts +2 -1
- package/dist/utils/oauth.d.ts.map +1 -1
- package/dist/utils/oauth.js +4 -3
- package/dist/utils/oauth.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignIn.js","names":["SignIn: FC<SignInProps>","useThunderID","useTranslation","challengeTokenRef: any","initializationAttemptedRef: any","oauthCodeProcessedRef: any","passkeyProcessedRef: any","tempData: any","storageManager: any","urlParams: any","url: any","redirectURL: any","err: any","finalRedirectUrl: any","effectiveApplicationId: any","storedExecutionId: any","resumeExecutionId: any","error: any","response: EmbeddedSignInFlowResponse","currentUrlParams: any","timeoutMs: number","remaining: number","errorMessage: string","timerId: any","effectiveExecutionId: any","processedInputs: Record<string, any>","consentPromptRawData: any","purposes: any[]","found: any","submitAction: any","decisions: any","effectiveExecutionIdForPasskey: any","responseFieldErrors: FieldError[] | undefined","inputs: Record<string, string>","passkeyResponse: any","passkeyResponseObj: any","renderPropFieldErrors: Record<string, string>","BaseSignIn"],"sources":["../../../../../src/components/presentation/auth/SignIn/SignIn.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025-2026, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n ThunderIDRuntimeError,\n ThunderIDAPIError,\n EmbeddedFlowComponent,\n EmbeddedFlowType,\n EmbeddedSignInFlowResponse,\n EmbeddedSignInFlowRequest,\n EmbeddedSignInFlowStatus,\n EmbeddedSignInFlowType,\n FieldError,\n FlowMetadataResponse,\n Preferences,\n logger,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useState, useEffect, useRef, ReactNode} from 'react';\n// eslint-disable-next-line import/no-named-as-default\nimport BaseSignIn, {BaseSignInProps} from './BaseSignIn';\nimport useThunderID from '../../../../contexts/ThunderID/useThunderID';\nimport {useOAuthCallback} from '../../../../hooks/useOAuthCallback';\nimport useTranslation from '../../../../hooks/useTranslation';\nimport {extractErrorMessage, normalizeFlowResponse} from '../../../../utils/flowTransformer';\nimport {initiateOAuthRedirect} from '../../../../utils/oauth';\nimport {handlePasskeyAuthentication, handlePasskeyRegistration} from '../../../../utils/passkey';\n\n/**\n * Render props function parameters\n */\nexport interface SignInRenderProps {\n /**\n * Additional data from the flow response containing contextual information\n * like consent prompt details and session timeouts.\n */\n additionalData?: Record<string, any>;\n\n /**\n * Current flow components\n */\n components: EmbeddedFlowComponent[];\n\n /**\n * Current error if any\n */\n error: Error | null;\n\n /**\n * Server-side field-level validation errors from the most recent flow response,\n * collapsed to one message per field (first error wins). Empty when no validation\n * failures are active. Render-prop consumers should display these alongside their\n * own client-side validation errors.\n */\n fieldErrors: Record<string, string>;\n\n /**\n * Function to manually initialize the flow\n */\n initialize: () => Promise<void>;\n\n /**\n * Whether the flow has been initialized\n */\n isInitialized: boolean;\n\n /**\n * Loading state indicator\n */\n isLoading: boolean;\n\n /**\n * Flag indicating whether the flow step timeout has expired.\n * Consuming components can use this to disable submit buttons.\n */\n isTimeoutDisabled?: boolean;\n\n /**\n * Flow metadata returned by the platform (v2 only). `null` while loading or unavailable.\n */\n meta: FlowMetadataResponse | null;\n\n /**\n * Function to submit authentication data (primary)\n */\n onSubmit: (payload: EmbeddedSignInFlowRequest) => Promise<void>;\n}\n\n/**\n * Props for the SignIn component.\n * Matches the interface from the main SignIn component for consistency.\n */\nexport interface SignInProps {\n /**\n * Render props function for custom UI\n */\n children?: (props: SignInRenderProps) => ReactNode;\n\n /**\n * Custom CSS class name for the form container.\n */\n className?: string;\n\n /**\n * Callback function called when authentication fails.\n * @param error - The error that occurred during authentication.\n */\n onError?: (error: Error) => void;\n\n /**\n * Callback function called when authentication is successful.\n * @param authData - The authentication data returned upon successful completion.\n */\n onSuccess?: (authData: Record<string, any>) => void;\n\n /**\n * Component-level preferences to override global i18n and theme settings.\n * Preferences are deep-merged with global ones, with component preferences\n * taking precedence. Affects this component and all its descendants.\n */\n preferences?: Preferences;\n\n /**\n * Size variant for the component.\n */\n size?: 'small' | 'medium' | 'large';\n\n /**\n * Theme variant for the component.\n */\n variant?: BaseSignInProps['variant'];\n}\n\n/**\n * State for tracking passkey registration\n */\ninterface PasskeyState {\n actionId: string | null;\n challenge: string | null;\n creationOptions: string | null;\n error: Error | null;\n executionId: string | null;\n isActive: boolean;\n}\n\n/**\n * A component-driven SignIn component that provides authentication flow with pre-built styling.\n * This component handles the flow API calls for authentication and delegates UI logic to BaseSignIn.\n * It automatically transforms simple input-based responses into component-driven UI format.\n *\n * @example\n * // Default UI\n * ```tsx\n * import { SignIn } from '@thunderid/react/component-driven';\n *\n * const App = () => {\n * return (\n * <SignIn\n * onSuccess={(authData) => {\n * console.log('Authentication successful:', authData);\n * }}\n * onError={(error) => {\n * console.error('Authentication failed:', error);\n * }}\n * size=\"medium\"\n * variant=\"outlined\"\n * />\n * );\n * };\n * ```\n *\n * @example\n * // Custom UI with render props\n * ```tsx\n * import { SignIn } from '@thunderid/react/component-driven';\n *\n * const App = () => {\n * return (\n * <SignIn\n * onSuccess={(authData) => console.log('Success:', authData)}\n * onError={(error) => console.error('Error:', error)}\n * >\n * {({signIn, isLoading, components, error, isInitialized}) => (\n * <div className=\"custom-signin\">\n * <h1>Custom Sign In</h1>\n * {!isInitialized ? (\n * <p>Initializing...</p>\n * ) : error ? (\n * <div className=\"error\">{error.message}</div>\n * ) : (\n * <form onSubmit={(e) => {\n * e.preventDefault();\n * signIn({inputs: {username: 'user', password: 'pass'}});\n * }}>\n * <button type=\"submit\" disabled={isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign In'}\n * </button>\n * </form>\n * )}\n * </div>\n * )}\n * </SignIn>\n * );\n * };\n * ```\n */\nconst SignIn: FC<SignInProps> = ({\n className,\n preferences,\n size = 'medium',\n onSuccess,\n onError,\n variant,\n children,\n}: SignInProps): ReactElement => {\n const {applicationId, afterSignInUrl, signIn, isInitialized, isLoading, meta, getStorageManager, scopes} =\n useThunderID();\n const {t} = useTranslation(preferences?.i18n);\n\n // State management for the flow\n const [components, setComponents] = useState<EmbeddedFlowComponent[]>([]);\n const [additionalData, setAdditionalData] = useState<Record<string, any>>({});\n // Server-side validation errors from the most recent flow response. Updated on every\n // submission; cleared when the next submission begins so stale errors don't linger.\n const [serverFieldErrors, setServerFieldErrors] = useState<FieldError[] | null>(null);\n const [currentExecutionId, setCurrentExecutionId] = useState<string | null>(null);\n const challengeTokenRef: any = useRef<string | null>(null);\n const [isStorageReady, setIsStorageReady] = useState(false);\n const [isFlowInitialized, setIsFlowInitialized] = useState(false);\n const [flowError, setFlowError] = useState<Error | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isTimeoutDisabled, setIsTimeoutDisabled] = useState<boolean>(false);\n const [passkeyState, setPasskeyState] = useState<PasskeyState>({\n actionId: null,\n challenge: null,\n creationOptions: null,\n error: null,\n executionId: null,\n isActive: false,\n });\n const initializationAttemptedRef: any = useRef(false);\n const oauthCodeProcessedRef: any = useRef(false);\n const passkeyProcessedRef: any = useRef(false);\n /**\n * Sets executionId between sessionStorage and state.\n * This ensures both are always in sync.\n */\n const setExecutionId = (executionId: string | null): void => {\n setCurrentExecutionId(executionId);\n if (executionId) {\n sessionStorage.setItem('thunderid_execution_id', executionId);\n } else {\n sessionStorage.removeItem('thunderid_execution_id');\n }\n };\n\n /**\n * Restore any challenge token persisted before an OAuth redirect.\n * Waits for SDK initialization before reading from storage.\n */\n useEffect(() => {\n if (!isInitialized) return;\n\n (async (): Promise<void> => {\n try {\n const storageManager: any = await getStorageManager();\n const tempData: any = await storageManager?.getTemporaryData();\n if (tempData?.challengeToken) {\n challengeTokenRef.current = tempData.challengeToken as string;\n }\n } finally {\n setIsStorageReady(true);\n }\n })();\n }, [isInitialized]);\n\n /**\n * Updates challengeTokenRef immediately (stale-closure safe) and persists via\n * the provider's StorageManager so the token survives OAuth redirects.\n */\n const setChallengeToken = async (challengeToken: string | null): Promise<void> => {\n challengeTokenRef.current = challengeToken;\n try {\n const storageManager: any = await getStorageManager();\n if (storageManager) {\n if (challengeToken) {\n await storageManager.setTemporaryDataParameter('challengeToken', challengeToken);\n } else {\n await storageManager.removeTemporaryDataParameter('challengeToken');\n }\n }\n } catch {\n logger.warn('Failed to persist challenge token in storage.');\n }\n };\n\n /**\n * Clear all flow-related storage and state.\n */\n const clearFlowState = async (): Promise<void> => {\n setExecutionId(null);\n await setChallengeToken(null);\n setIsFlowInitialized(false);\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage.');\n }\n setIsTimeoutDisabled(false);\n // Reset refs to allow new flows to start properly\n oauthCodeProcessedRef.current = false;\n };\n\n /**\n * Parse URL parameters used in flows.\n */\n const getUrlParams = (): any => {\n const urlParams: any = new URL(window?.location?.href ?? '').searchParams;\n\n return {\n applicationId: urlParams.get('applicationId'),\n authId: urlParams.get('authId'),\n code: urlParams.get('code'),\n error: urlParams.get('error'),\n errorDescription: urlParams.get('error_description'),\n executionId: urlParams.get('executionId'),\n nonce: urlParams.get('nonce'),\n state: urlParams.get('state'),\n };\n };\n\n /**\n * Handle authId from URL and persist it via the storage manager so it survives URL cleanup.\n * ThunderIDReactClient.signIn() reads authId from storageManager.getHybridDataParameter('authId'),\n * not from raw sessionStorage, so we must use the same storage path here.\n */\n const handleAuthId = async (authId: string | null): Promise<void> => {\n if (authId) {\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.setHybridDataParameter?.('authId', authId);\n } catch {\n logger.warn('Failed to store authId in hybrid storage.');\n }\n }\n };\n\n /**\n * Clean up OAuth-related URL parameters from the browser URL.\n */\n const cleanupOAuthUrlParams = (includeNonce = false): void => {\n if (!window?.location?.href) return;\n const url: any = new URL(window.location.href);\n url.searchParams.delete('error');\n url.searchParams.delete('error_description');\n url.searchParams.delete('code');\n url.searchParams.delete('state');\n if (includeNonce) {\n url.searchParams.delete('nonce');\n }\n window?.history?.replaceState({}, '', url.toString());\n };\n\n /**\n * Clean up flow-related URL parameters (executionId, authId) from the browser URL.\n * Used after executionId is set in state to prevent using invalidated executionId from URL.\n */\n const cleanupFlowUrlParams = (): void => {\n if (!window?.location?.href) return;\n const url: any = new URL(window.location.href);\n url.searchParams.delete('executionId');\n url.searchParams.delete('authId');\n url.searchParams.delete('applicationId');\n window?.history?.replaceState({}, '', url.toString());\n };\n\n /**\n * Set error state and call onError callback.\n * Ensures isFlowInitialized is true so errors can be displayed in the UI.\n */\n const setError = (error: Error): void => {\n setFlowError(error);\n setIsFlowInitialized(true);\n onError?.(error);\n };\n\n /**\n * Handle OAuth error from URL parameters.\n * Clears flow state, creates error, and cleans up URL.\n */\n const handleOAuthError = (error: string, errorDescription: string | null): void => {\n clearFlowState();\n const errorMessage: any = errorDescription || `OAuth error: ${error}`;\n const err: any = new ThunderIDRuntimeError(errorMessage, 'SIGN_IN_ERROR', 'react');\n setError(err);\n cleanupOAuthUrlParams(true);\n };\n\n /**\n * Handle REDIRECTION response by storing flow state and redirecting to OAuth provider.\n */\n const handleRedirection = async (response: EmbeddedSignInFlowResponse): Promise<boolean> => {\n if (response.type === EmbeddedSignInFlowType.Redirection) {\n const redirectURL: any = (response.data as any)?.redirectURL || (response as any)?.redirectURL;\n\n if (redirectURL && window?.location) {\n if (response.executionId) {\n setExecutionId(response.executionId);\n }\n await setChallengeToken(response.challengeToken ?? null);\n\n const urlParams: any = getUrlParams();\n await handleAuthId(urlParams.authId);\n\n initiateOAuthRedirect(redirectURL);\n return true;\n }\n }\n return false;\n };\n\n /**\n * Handle terminal flow responses (Error and Complete) shared by initializeFlow and handleSubmit.\n * Throws on an Error status so the caller's catch block can propagate it to BaseSignIn.\n * Returns true when a Complete status was handled (caller should return), false otherwise.\n */\n const handleTerminalResponse = async (response: EmbeddedSignInFlowResponse): Promise<boolean> => {\n // Handle Error flow status - flow has failed and is invalidated\n if (response.flowStatus === EmbeddedSignInFlowStatus.Error) {\n await clearFlowState();\n const err: any = new Error(extractErrorMessage(response, t));\n setError(err);\n cleanupFlowUrlParams();\n // Throw the error so it's caught by the catch block and propagated to BaseSignIn\n throw err;\n }\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Complete) {\n // Get redirectUrl from response (from /oauth2/auth/callback) or fall back to afterSignInUrl\n const redirectUrl: any = (response as any)?.redirectUrl || (response as any)?.redirect_uri;\n const finalRedirectUrl: any = redirectUrl || afterSignInUrl;\n\n // Clear submitting state before redirect\n setIsSubmitting(false);\n\n // Clear all OAuth-related storage on successful completion\n setExecutionId(null);\n await setChallengeToken(null);\n setIsFlowInitialized(false);\n sessionStorage.removeItem('thunderid_execution_id');\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage after completion.');\n }\n\n // Clean up OAuth URL params before redirect\n cleanupOAuthUrlParams(true);\n\n if (onSuccess) {\n onSuccess({\n redirectUrl: finalRedirectUrl,\n ...(response.data || {}),\n });\n }\n\n if (finalRedirectUrl && window?.location) {\n window.location.href = finalRedirectUrl;\n }\n\n return true;\n }\n\n return false;\n };\n\n /**\n * Initialize the authentication flow.\n * Priority: executionId > applicationId (from context) > applicationId (from URL)\n */\n const initializeFlow = async (): Promise<void> => {\n const urlParams: any = getUrlParams();\n\n // Reset OAuth code processed ref when starting a new flow\n oauthCodeProcessedRef.current = false;\n // Clear stale serverFieldErrors so the new flow doesn't inherit them via render-prop.\n setServerFieldErrors(null);\n\n await handleAuthId(urlParams.authId);\n\n const effectiveApplicationId: any = applicationId || urlParams.applicationId;\n\n // On a page refresh the executionId is no longer in the URL (it is scrubbed by\n // cleanupFlowUrlParams after the first load). Fall back to the executionId persisted in\n // sessionStorage so the in-progress flow can be resumed instead of starting a new flow.\n // This is required for authorization_code apps where direct new-flow initiation is blocked\n // server-side and the flow must be initiated through the OAuth /authorize endpoint.\n const storedExecutionId: any = !urlParams.executionId ? sessionStorage.getItem('thunderid_execution_id') : null;\n const resumeExecutionId: any = urlParams.executionId || storedExecutionId;\n\n if (!resumeExecutionId && !effectiveApplicationId) {\n const error: any = new ThunderIDRuntimeError(\n 'Either executionId or applicationId is required for authentication',\n 'SIGN_IN_ERROR',\n 'react',\n );\n setError(error);\n throw error;\n }\n\n try {\n setFlowError(null);\n\n let response: EmbeddedSignInFlowResponse;\n\n if (resumeExecutionId) {\n try {\n response = (await signIn({\n executionId: resumeExecutionId,\n ...(challengeTokenRef.current ? {challengeToken: challengeTokenRef.current} : {}),\n })) as EmbeddedSignInFlowResponse;\n } catch (resumeError) {\n // Only treat a stale/expired session (HTTP 400 from the server, meaning the stored\n // executionId is no longer valid) as a recoverable condition. Transient failures\n // such as 5xx server errors or network errors are rethrown so they surface correctly.\n const isStaleSession: boolean =\n storedExecutionId &&\n resumeExecutionId === storedExecutionId &&\n resumeError instanceof ThunderIDAPIError &&\n resumeError.statusCode === 400;\n\n if (isStaleSession) {\n setExecutionId(null);\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage.');\n }\n if (!effectiveApplicationId) {\n const expiredError: any = new ThunderIDRuntimeError(\n t('errors.signin.session.expired') ||\n 'Your session has expired. Please return to the application and sign in again.',\n 'SIGN_IN_ERROR',\n 'react',\n );\n setError(expiredError);\n return;\n }\n response = (await signIn({\n applicationId: effectiveApplicationId,\n flowType: EmbeddedFlowType.Authentication,\n ...(scopes && {scopes}),\n })) as EmbeddedSignInFlowResponse;\n } else {\n throw resumeError;\n }\n }\n } else {\n response = (await signIn({\n applicationId: effectiveApplicationId,\n flowType: EmbeddedFlowType.Authentication,\n ...(scopes && {scopes}),\n })) as EmbeddedSignInFlowResponse;\n }\n\n if (await handleRedirection(response)) {\n return;\n }\n\n // Handle a flow that completes (or errors) on the very first step — e.g. a reused SSO\n // session lets the backend return COMPLETE immediately with no UI components. Without\n // this the UI would fall through with no components and spin forever.\n if (await handleTerminalResponse(response)) {\n return;\n }\n\n const {\n executionId: normalizedExecutionId,\n components: normalizedComponents,\n additionalData: normalizedAdditionalData,\n } = normalizeFlowResponse(\n response,\n t,\n {\n resolveTranslations: false,\n },\n meta,\n );\n\n await setChallengeToken(response.challengeToken ?? null);\n\n if (normalizedExecutionId && normalizedComponents) {\n setExecutionId(normalizedExecutionId);\n setComponents(normalizedComponents);\n setAdditionalData(normalizedAdditionalData ?? {});\n setIsFlowInitialized(true);\n setIsTimeoutDisabled(false);\n // Clean up executionId from URL after setting it in state\n cleanupFlowUrlParams();\n }\n } catch (error) {\n const err: any = error;\n await clearFlowState();\n\n setError(err instanceof ThunderIDRuntimeError ? err : new Error(extractErrorMessage(err, t)));\n initializationAttemptedRef.current = false;\n }\n };\n\n /**\n * Initialize the flow and handle cleanup of stale flow state.\n */\n useEffect(() => {\n const urlParams: any = getUrlParams();\n\n // Check for OAuth error in URL\n if (urlParams.error) {\n handleOAuthError(urlParams.error, urlParams.errorDescription);\n return;\n }\n\n handleAuthId(urlParams.authId);\n\n // Skip OAuth code processing - let the dedicated OAuth useEffect handle it\n // No action needed here as the dedicated useEffect will handle it\n }, []);\n\n useEffect(() => {\n // Only initialize if we're not processing an OAuth callback or submission.\n // Wait for isStorageReady so the challenge token is restored from storage before\n // we attempt to resume a persisted executionId — the server requires it.\n const currentUrlParams: any = getUrlParams();\n if (\n isInitialized &&\n isStorageReady &&\n !isLoading &&\n !isFlowInitialized &&\n !initializationAttemptedRef.current &&\n !currentExecutionId &&\n !currentUrlParams.code &&\n !currentUrlParams.state &&\n !isSubmitting &&\n !oauthCodeProcessedRef.current\n ) {\n initializationAttemptedRef.current = true;\n initializeFlow();\n }\n }, [isInitialized, isStorageReady, isLoading, isFlowInitialized, currentExecutionId]);\n\n /**\n * Handle step timeout if configured in additionalData.\n */\n useEffect(() => {\n const timeoutMs: number = Number(additionalData?.['stepTimeout']) || 0;\n if (timeoutMs <= 0 || !isFlowInitialized) {\n setIsTimeoutDisabled(false);\n return undefined;\n }\n\n const remaining: number = Math.max(0, Math.floor((timeoutMs - Date.now()) / 1000));\n\n const handleTimeout = (): void => {\n const errorMessage: string = t('errors.signin.timeout') || 'Time allowed to complete the step has expired.';\n setError(new Error(errorMessage));\n setIsTimeoutDisabled(true);\n };\n\n if (remaining <= 0) {\n handleTimeout();\n return undefined;\n }\n\n const timerId: any = setTimeout(() => {\n handleTimeout();\n }, remaining * 1000);\n\n return () => clearTimeout(timerId);\n }, [additionalData?.['stepTimeout'], isFlowInitialized, t]);\n\n /**\n * Handle form submission from BaseSignIn or render props.\n */\n const handleSubmit = async (payload: EmbeddedSignInFlowRequest): Promise<void> => {\n // Use executionId from payload if available, otherwise fall back to currentExecutionId\n const effectiveExecutionId: any = payload.executionId || currentExecutionId;\n\n if (!effectiveExecutionId) {\n throw new Error('No active flow ID');\n }\n\n const processedInputs: Record<string, any> = {...payload.inputs};\n\n // Auto-compile consent decisions if we are currently on a consent prompt step\n if (additionalData?.['consentPrompt']) {\n try {\n const consentPromptRawData: any = additionalData['consentPrompt'];\n const purposes: any[] =\n typeof consentPromptRawData === 'string'\n ? JSON.parse(consentPromptRawData)\n : consentPromptRawData.purposes || consentPromptRawData;\n\n // Find the action component to determine if it was a deny action\n let isDeny = false;\n if (payload.action) {\n // Flatten components to find the action\n const findAction = (comps: any[]): any => {\n if (!comps || comps.length === 0) return null;\n\n const found: any = comps.find((c: any) => c.id === payload.action);\n if (found) return found;\n\n return comps.reduce((acc: any, c: any) => {\n if (acc) return acc;\n if (c.components) return findAction(c.components);\n return null;\n }, null);\n };\n\n const submitAction: any = findAction(components);\n\n if (submitAction && submitAction.variant?.toLowerCase() !== 'primary') {\n isDeny = true;\n }\n }\n\n const decisions: any = {\n purposes: purposes.map((p: any) => ({\n approved: !isDeny,\n elements: [\n ...(p.essential || []).map((e: any) => ({\n approved: !isDeny,\n name: e.name,\n })),\n ...(p.optional || []).map((e: any) => {\n const key = `__consent_opt__${p.purposeId}__${e.name}`;\n return {\n approved: isDeny ? false : processedInputs[key] !== 'false',\n name: e.name,\n };\n }),\n ],\n purposeName: p.purposeName,\n })),\n };\n processedInputs['consent_decisions'] = JSON.stringify(decisions);\n\n // Cleanup temporary consent tracking fields from inputs\n Object.keys(processedInputs).forEach((key: string) => {\n if (key.startsWith('__consent_opt__')) {\n delete processedInputs[key];\n }\n });\n } catch (e) {\n // Failed to construct consent_decisions payload automatically\n }\n }\n\n try {\n setIsSubmitting(true);\n setFlowError(null);\n // Clear any field errors from the previous response before the new round-trip.\n setServerFieldErrors(null);\n\n const response: EmbeddedSignInFlowResponse = (await signIn({\n executionId: effectiveExecutionId,\n ...payload,\n inputs: processedInputs,\n ...(challengeTokenRef.current ? {challengeToken: challengeTokenRef.current} : {}),\n })) as EmbeddedSignInFlowResponse;\n\n if (await handleRedirection(response)) {\n return;\n }\n if (\n response.data?.additionalData?.['passkeyChallenge'] ||\n response.data?.additionalData?.['passkeyCreationOptions']\n ) {\n const {passkeyChallenge, passkeyCreationOptions}: any = response.data.additionalData;\n const effectiveExecutionIdForPasskey: any = response.executionId || effectiveExecutionId;\n\n // Reset passkey processed ref to allow processing\n passkeyProcessedRef.current = false;\n\n await setChallengeToken(response.challengeToken ?? null);\n\n // Set passkey state to trigger the passkey\n setPasskeyState({\n actionId: 'submit',\n challenge: passkeyChallenge,\n creationOptions: passkeyCreationOptions,\n error: null,\n executionId: effectiveExecutionIdForPasskey,\n isActive: true,\n });\n setIsSubmitting(false);\n\n return;\n }\n\n const {\n executionId: normalizedExecutionId,\n components: normalizedComponents,\n additionalData: normalizedAdditionalData,\n } = normalizeFlowResponse(\n response,\n t,\n {\n resolveTranslations: false,\n },\n meta,\n );\n\n // Handle terminal flow statuses (Error throws, Complete redirects and returns true).\n if (await handleTerminalResponse(response)) {\n return;\n }\n\n // Always update challenge token on any INCOMPLETE response — token rotates every step.\n await setChallengeToken(response.challengeToken ?? null);\n\n // Update executionId if response contains a new one\n if (normalizedExecutionId && normalizedComponents) {\n setExecutionId(normalizedExecutionId);\n setComponents(normalizedComponents);\n setAdditionalData(normalizedAdditionalData ?? {});\n setIsTimeoutDisabled(false);\n // Ensure flow is marked as initialized when we have components\n setIsFlowInitialized(true);\n // Clean up executionId from URL after setting it in state\n cleanupFlowUrlParams();\n\n // Surface server-side validation failures so BaseSignIn can inject them into\n // the form-level fieldErrors state used by the render-prop / default UI.\n const responseFieldErrors: FieldError[] | undefined = (response.data as any)?.fieldErrors;\n if (responseFieldErrors && responseFieldErrors.length > 0) {\n setServerFieldErrors(responseFieldErrors);\n }\n\n // Display error from INCOMPLETE response\n if ((response as any)?.error) {\n setFlowError(new Error(extractErrorMessage(response, t)));\n }\n }\n } catch (error) {\n const err: any = error;\n await clearFlowState();\n\n setError(err instanceof ThunderIDRuntimeError ? err : new Error(extractErrorMessage(err, t)));\n return;\n } finally {\n setIsSubmitting(false);\n }\n };\n\n /**\n * Handle authentication errors.\n */\n const handleError = (error: Error): void => {\n setError(error);\n };\n\n useOAuthCallback({\n currentExecutionId,\n isInitialized: isInitialized && !isLoading && isStorageReady,\n isSubmitting,\n onError: (err: any) => {\n clearFlowState();\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n onSubmit: async (payload: any) => handleSubmit({executionId: payload.executionId, inputs: payload.inputs}),\n processedRef: oauthCodeProcessedRef,\n setExecutionId,\n });\n\n /**\n * Handle passkey authentication/registration when passkey state becomes active.\n * This effect auto-triggers the browser passkey popup and submits the result.\n */\n useEffect(() => {\n if (\n !passkeyState.isActive ||\n (!passkeyState.challenge && !passkeyState.creationOptions) ||\n !passkeyState.executionId\n ) {\n return;\n }\n\n // Prevent re-processing\n if (passkeyProcessedRef.current) {\n return;\n }\n passkeyProcessedRef.current = true;\n\n const performPasskeyProcess = async (): Promise<void> => {\n let inputs: Record<string, string>;\n\n if (passkeyState.challenge) {\n const passkeyResponse: any = await handlePasskeyAuthentication(passkeyState.challenge);\n const passkeyResponseObj: any = JSON.parse(passkeyResponse);\n\n inputs = {\n authenticatorData: passkeyResponseObj.response.authenticatorData,\n clientDataJSON: passkeyResponseObj.response.clientDataJSON,\n credentialId: passkeyResponseObj.id,\n signature: passkeyResponseObj.response.signature,\n userHandle: passkeyResponseObj.response.userHandle,\n };\n } else if (passkeyState.creationOptions) {\n const passkeyResponse: any = await handlePasskeyRegistration(passkeyState.creationOptions);\n const passkeyResponseObj: any = JSON.parse(passkeyResponse);\n\n inputs = {\n attestationObject: passkeyResponseObj.response.attestationObject,\n clientDataJSON: passkeyResponseObj.response.clientDataJSON,\n credentialId: passkeyResponseObj.id,\n };\n } else {\n throw new Error('No passkey challenge or creation options available');\n }\n\n await handleSubmit({\n executionId: passkeyState.executionId ?? undefined,\n inputs,\n });\n };\n\n performPasskeyProcess()\n .then(() => {\n setPasskeyState({\n actionId: null,\n challenge: null,\n creationOptions: null,\n error: null,\n executionId: null,\n isActive: false,\n });\n })\n .catch((error: any) => {\n setPasskeyState((prev: any) => ({...prev, error: error as Error, isActive: false}));\n setFlowError(error as Error);\n onError?.(error as Error);\n });\n }, [passkeyState.isActive, passkeyState.challenge, passkeyState.creationOptions, passkeyState.executionId]);\n\n if (children) {\n // Collapse the server FieldError[] array to a single message per field map for\n // render-prop consumers. First error per field wins. Multi-error cases per\n // field are rare in practice (server typically returns one rule failure per\n // field in current flows) and consumers needing the full array can still read\n // it from the raw flow response.\n const renderPropFieldErrors: Record<string, string> = {};\n if (serverFieldErrors) {\n for (const fe of serverFieldErrors) {\n if (!(fe.identifier in renderPropFieldErrors)) {\n renderPropFieldErrors[fe.identifier] = fe.message;\n }\n }\n }\n\n const renderProps: SignInRenderProps = {\n additionalData,\n components,\n error: flowError,\n fieldErrors: renderPropFieldErrors,\n initialize: initializeFlow,\n isInitialized: isFlowInitialized,\n isLoading: isLoading || isSubmitting || !isInitialized,\n isTimeoutDisabled,\n meta,\n onSubmit: handleSubmit,\n };\n\n return <>{children(renderProps)}</>;\n }\n // Otherwise, render the default BaseSignIn component\n return (\n <BaseSignIn\n additionalData={additionalData}\n components={components}\n isLoading={isLoading || !isInitialized || !isFlowInitialized}\n isTimeoutDisabled={isTimeoutDisabled}\n onSubmit={handleSubmit}\n onError={handleError}\n error={flowError}\n className={className}\n size={size}\n variant={variant}\n preferences={preferences}\n serverFieldErrors={serverFieldErrors}\n />\n );\n};\n\nexport default SignIn;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SignIn.js","names":["SignIn: FC<SignInProps>","useThunderID","useTranslation","challengeTokenRef: any","initializationAttemptedRef: any","oauthCodeProcessedRef: any","passkeyProcessedRef: any","tempData: any","storageManager: any","urlParams: any","url: any","redirectURL: any","err: any","finalRedirectUrl: any","effectiveApplicationId: any","storedExecutionId: any","resumeExecutionId: any","error: any","response: EmbeddedSignInFlowResponse","currentUrlParams: any","timeoutMs: number","remaining: number","errorMessage: string","timerId: any","effectiveExecutionId: any","processedInputs: Record<string, any>","consentPromptRawData: any","purposes: any[]","found: any","submitAction: any","decisions: any","effectiveExecutionIdForPasskey: any","responseFieldErrors: FieldError[] | undefined","inputs: Record<string, string>","passkeyResponse: any","passkeyResponseObj: any","renderPropFieldErrors: Record<string, string>","BaseSignIn"],"sources":["../../../../../src/components/presentation/auth/SignIn/SignIn.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025-2026, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n ThunderIDRuntimeError,\n ThunderIDAPIError,\n EmbeddedFlowComponent,\n EmbeddedFlowType,\n EmbeddedSignInFlowResponse,\n EmbeddedSignInFlowRequest,\n EmbeddedSignInFlowStatus,\n EmbeddedSignInFlowType,\n FieldError,\n FlowMetadataResponse,\n Preferences,\n logger,\n} from '@thunderid/browser';\nimport {FC, ReactElement, useState, useEffect, useRef, ReactNode} from 'react';\n// eslint-disable-next-line import/no-named-as-default\nimport BaseSignIn, {BaseSignInProps} from './BaseSignIn';\nimport useThunderID from '../../../../contexts/ThunderID/useThunderID';\nimport {useOAuthCallback} from '../../../../hooks/useOAuthCallback';\nimport useTranslation from '../../../../hooks/useTranslation';\nimport {extractErrorMessage, normalizeFlowResponse} from '../../../../utils/flowTransformer';\nimport {initiateOAuthRedirect} from '../../../../utils/oauth';\nimport {handlePasskeyAuthentication, handlePasskeyRegistration} from '../../../../utils/passkey';\n\n/**\n * Render props function parameters\n */\nexport interface SignInRenderProps {\n /**\n * Additional data from the flow response containing contextual information\n * like consent prompt details and session timeouts.\n */\n additionalData?: Record<string, any>;\n\n /**\n * Current flow components\n */\n components: EmbeddedFlowComponent[];\n\n /**\n * Current error if any\n */\n error: Error | null;\n\n /**\n * Server-side field-level validation errors from the most recent flow response,\n * collapsed to one message per field (first error wins). Empty when no validation\n * failures are active. Render-prop consumers should display these alongside their\n * own client-side validation errors.\n */\n fieldErrors: Record<string, string>;\n\n /**\n * Function to manually initialize the flow\n */\n initialize: () => Promise<void>;\n\n /**\n * Whether the flow has been initialized\n */\n isInitialized: boolean;\n\n /**\n * Loading state indicator\n */\n isLoading: boolean;\n\n /**\n * Flag indicating whether the flow step timeout has expired.\n * Consuming components can use this to disable submit buttons.\n */\n isTimeoutDisabled?: boolean;\n\n /**\n * Flow metadata returned by the platform (v2 only). `null` while loading or unavailable.\n */\n meta: FlowMetadataResponse | null;\n\n /**\n * Function to submit authentication data (primary)\n */\n onSubmit: (payload: EmbeddedSignInFlowRequest) => Promise<void>;\n}\n\n/**\n * Props for the SignIn component.\n * Matches the interface from the main SignIn component for consistency.\n */\nexport interface SignInProps {\n /**\n * Render props function for custom UI\n */\n children?: (props: SignInRenderProps) => ReactNode;\n\n /**\n * Custom CSS class name for the form container.\n */\n className?: string;\n\n /**\n * Callback function called when authentication fails.\n * @param error - The error that occurred during authentication.\n */\n onError?: (error: Error) => void;\n\n /**\n * Callback function called when authentication is successful.\n * @param authData - The authentication data returned upon successful completion.\n */\n onSuccess?: (authData: Record<string, any>) => void;\n\n /**\n * Component-level preferences to override global i18n and theme settings.\n * Preferences are deep-merged with global ones, with component preferences\n * taking precedence. Affects this component and all its descendants.\n */\n preferences?: Preferences;\n\n /**\n * Size variant for the component.\n */\n size?: 'small' | 'medium' | 'large';\n\n /**\n * Theme variant for the component.\n */\n variant?: BaseSignInProps['variant'];\n}\n\n/**\n * State for tracking passkey registration\n */\ninterface PasskeyState {\n actionId: string | null;\n challenge: string | null;\n creationOptions: string | null;\n error: Error | null;\n executionId: string | null;\n isActive: boolean;\n}\n\n/**\n * A component-driven SignIn component that provides authentication flow with pre-built styling.\n * This component handles the flow API calls for authentication and delegates UI logic to BaseSignIn.\n * It automatically transforms simple input-based responses into component-driven UI format.\n *\n * @example\n * // Default UI\n * ```tsx\n * import { SignIn } from '@thunderid/react/component-driven';\n *\n * const App = () => {\n * return (\n * <SignIn\n * onSuccess={(authData) => {\n * console.log('Authentication successful:', authData);\n * }}\n * onError={(error) => {\n * console.error('Authentication failed:', error);\n * }}\n * size=\"medium\"\n * variant=\"outlined\"\n * />\n * );\n * };\n * ```\n *\n * @example\n * // Custom UI with render props\n * ```tsx\n * import { SignIn } from '@thunderid/react/component-driven';\n *\n * const App = () => {\n * return (\n * <SignIn\n * onSuccess={(authData) => console.log('Success:', authData)}\n * onError={(error) => console.error('Error:', error)}\n * >\n * {({signIn, isLoading, components, error, isInitialized}) => (\n * <div className=\"custom-signin\">\n * <h1>Custom Sign In</h1>\n * {!isInitialized ? (\n * <p>Initializing...</p>\n * ) : error ? (\n * <div className=\"error\">{error.message}</div>\n * ) : (\n * <form onSubmit={(e) => {\n * e.preventDefault();\n * signIn({inputs: {username: 'user', password: 'pass'}});\n * }}>\n * <button type=\"submit\" disabled={isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign In'}\n * </button>\n * </form>\n * )}\n * </div>\n * )}\n * </SignIn>\n * );\n * };\n * ```\n */\nconst SignIn: FC<SignInProps> = ({\n className,\n preferences,\n size = 'medium',\n onSuccess,\n onError,\n variant,\n children,\n}: SignInProps): ReactElement => {\n const {applicationId, afterSignInUrl, signIn, isInitialized, isLoading, meta, getStorageManager, scopes, vendor} =\n useThunderID();\n const {t} = useTranslation(preferences?.i18n);\n\n // State management for the flow\n const [components, setComponents] = useState<EmbeddedFlowComponent[]>([]);\n const [additionalData, setAdditionalData] = useState<Record<string, any>>({});\n // Server-side validation errors from the most recent flow response. Updated on every\n // submission; cleared when the next submission begins so stale errors don't linger.\n const [serverFieldErrors, setServerFieldErrors] = useState<FieldError[] | null>(null);\n const [currentExecutionId, setCurrentExecutionId] = useState<string | null>(null);\n const challengeTokenRef: any = useRef<string | null>(null);\n const [isStorageReady, setIsStorageReady] = useState(false);\n const [isFlowInitialized, setIsFlowInitialized] = useState(false);\n const [flowError, setFlowError] = useState<Error | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isTimeoutDisabled, setIsTimeoutDisabled] = useState<boolean>(false);\n const [passkeyState, setPasskeyState] = useState<PasskeyState>({\n actionId: null,\n challenge: null,\n creationOptions: null,\n error: null,\n executionId: null,\n isActive: false,\n });\n const initializationAttemptedRef: any = useRef(false);\n const oauthCodeProcessedRef: any = useRef(false);\n const passkeyProcessedRef: any = useRef(false);\n /**\n * Sets executionId between sessionStorage and state.\n * This ensures both are always in sync.\n */\n const setExecutionId = (executionId: string | null): void => {\n setCurrentExecutionId(executionId);\n if (executionId) {\n sessionStorage.setItem(`${vendor}_execution_id`, executionId);\n } else {\n sessionStorage.removeItem(`${vendor}_execution_id`);\n }\n };\n\n /**\n * Restore any challenge token persisted before an OAuth redirect.\n * Waits for SDK initialization before reading from storage.\n */\n useEffect(() => {\n if (!isInitialized) return;\n\n (async (): Promise<void> => {\n try {\n const storageManager: any = await getStorageManager();\n const tempData: any = await storageManager?.getTemporaryData();\n if (tempData?.challengeToken) {\n challengeTokenRef.current = tempData.challengeToken as string;\n }\n } finally {\n setIsStorageReady(true);\n }\n })();\n }, [isInitialized]);\n\n /**\n * Updates challengeTokenRef immediately (stale-closure safe) and persists via\n * the provider's StorageManager so the token survives OAuth redirects.\n */\n const setChallengeToken = async (challengeToken: string | null): Promise<void> => {\n challengeTokenRef.current = challengeToken;\n try {\n const storageManager: any = await getStorageManager();\n if (storageManager) {\n if (challengeToken) {\n await storageManager.setTemporaryDataParameter('challengeToken', challengeToken);\n } else {\n await storageManager.removeTemporaryDataParameter('challengeToken');\n }\n }\n } catch {\n logger.warn('Failed to persist challenge token in storage.');\n }\n };\n\n /**\n * Clear all flow-related storage and state.\n */\n const clearFlowState = async (): Promise<void> => {\n setExecutionId(null);\n await setChallengeToken(null);\n setIsFlowInitialized(false);\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage.');\n }\n setIsTimeoutDisabled(false);\n // Reset refs to allow new flows to start properly\n oauthCodeProcessedRef.current = false;\n };\n\n /**\n * Parse URL parameters used in flows.\n */\n const getUrlParams = (): any => {\n const urlParams: any = new URL(window?.location?.href ?? '').searchParams;\n\n return {\n applicationId: urlParams.get('applicationId'),\n authId: urlParams.get('authId'),\n code: urlParams.get('code'),\n error: urlParams.get('error'),\n errorDescription: urlParams.get('error_description'),\n executionId: urlParams.get('executionId'),\n nonce: urlParams.get('nonce'),\n state: urlParams.get('state'),\n };\n };\n\n /**\n * Handle authId from URL and persist it via the storage manager so it survives URL cleanup.\n * ThunderIDReactClient.signIn() reads authId from storageManager.getHybridDataParameter('authId'),\n * not from raw sessionStorage, so we must use the same storage path here.\n */\n const handleAuthId = async (authId: string | null): Promise<void> => {\n if (authId) {\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.setHybridDataParameter?.('authId', authId);\n } catch {\n logger.warn('Failed to store authId in hybrid storage.');\n }\n }\n };\n\n /**\n * Clean up OAuth-related URL parameters from the browser URL.\n */\n const cleanupOAuthUrlParams = (includeNonce = false): void => {\n if (!window?.location?.href) return;\n const url: any = new URL(window.location.href);\n url.searchParams.delete('error');\n url.searchParams.delete('error_description');\n url.searchParams.delete('code');\n url.searchParams.delete('state');\n if (includeNonce) {\n url.searchParams.delete('nonce');\n }\n window?.history?.replaceState({}, '', url.toString());\n };\n\n /**\n * Clean up flow-related URL parameters (executionId, authId) from the browser URL.\n * Used after executionId is set in state to prevent using invalidated executionId from URL.\n */\n const cleanupFlowUrlParams = (): void => {\n if (!window?.location?.href) return;\n const url: any = new URL(window.location.href);\n url.searchParams.delete('executionId');\n url.searchParams.delete('authId');\n url.searchParams.delete('applicationId');\n window?.history?.replaceState({}, '', url.toString());\n };\n\n /**\n * Set error state and call onError callback.\n * Ensures isFlowInitialized is true so errors can be displayed in the UI.\n */\n const setError = (error: Error): void => {\n setFlowError(error);\n setIsFlowInitialized(true);\n onError?.(error);\n };\n\n /**\n * Handle OAuth error from URL parameters.\n * Clears flow state, creates error, and cleans up URL.\n */\n const handleOAuthError = (error: string, errorDescription: string | null): void => {\n clearFlowState();\n const errorMessage: any = errorDescription || `OAuth error: ${error}`;\n const err: any = new ThunderIDRuntimeError(errorMessage, 'SIGN_IN_ERROR', 'react');\n setError(err);\n cleanupOAuthUrlParams(true);\n };\n\n /**\n * Handle REDIRECTION response by storing flow state and redirecting to OAuth provider.\n */\n const handleRedirection = async (response: EmbeddedSignInFlowResponse): Promise<boolean> => {\n if (response.type === EmbeddedSignInFlowType.Redirection) {\n const redirectURL: any = (response.data as any)?.redirectURL || (response as any)?.redirectURL;\n\n if (redirectURL && window?.location) {\n if (response.executionId) {\n setExecutionId(response.executionId);\n }\n await setChallengeToken(response.challengeToken ?? null);\n\n const urlParams: any = getUrlParams();\n await handleAuthId(urlParams.authId);\n\n initiateOAuthRedirect(redirectURL, vendor);\n return true;\n }\n }\n return false;\n };\n\n /**\n * Handle terminal flow responses (Error and Complete) shared by initializeFlow and handleSubmit.\n * Throws on an Error status so the caller's catch block can propagate it to BaseSignIn.\n * Returns true when a Complete status was handled (caller should return), false otherwise.\n */\n const handleTerminalResponse = async (response: EmbeddedSignInFlowResponse): Promise<boolean> => {\n // Handle Error flow status - flow has failed and is invalidated\n if (response.flowStatus === EmbeddedSignInFlowStatus.Error) {\n await clearFlowState();\n const err: any = new Error(extractErrorMessage(response, t));\n setError(err);\n cleanupFlowUrlParams();\n // Throw the error so it's caught by the catch block and propagated to BaseSignIn\n throw err;\n }\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Complete) {\n // Get redirectUrl from response (from /oauth2/auth/callback) or fall back to afterSignInUrl\n const redirectUrl: any = (response as any)?.redirectUrl || (response as any)?.redirect_uri;\n const finalRedirectUrl: any = redirectUrl || afterSignInUrl;\n\n // Clear submitting state before redirect\n setIsSubmitting(false);\n\n // Clear all OAuth-related storage on successful completion\n setExecutionId(null);\n await setChallengeToken(null);\n setIsFlowInitialized(false);\n sessionStorage.removeItem(`${vendor}_execution_id`);\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage after completion.');\n }\n\n // Clean up OAuth URL params before redirect\n cleanupOAuthUrlParams(true);\n\n if (onSuccess) {\n onSuccess({\n redirectUrl: finalRedirectUrl,\n ...(response.data || {}),\n });\n }\n\n if (finalRedirectUrl && window?.location) {\n window.location.href = finalRedirectUrl;\n }\n\n return true;\n }\n\n return false;\n };\n\n /**\n * Initialize the authentication flow.\n * Priority: executionId > applicationId (from context) > applicationId (from URL)\n */\n const initializeFlow = async (): Promise<void> => {\n const urlParams: any = getUrlParams();\n\n // Reset OAuth code processed ref when starting a new flow\n oauthCodeProcessedRef.current = false;\n // Clear stale serverFieldErrors so the new flow doesn't inherit them via render-prop.\n setServerFieldErrors(null);\n\n await handleAuthId(urlParams.authId);\n\n const effectiveApplicationId: any = applicationId || urlParams.applicationId;\n\n // On a page refresh the executionId is no longer in the URL (it is scrubbed by\n // cleanupFlowUrlParams after the first load). Fall back to the executionId persisted in\n // sessionStorage so the in-progress flow can be resumed instead of starting a new flow.\n // This is required for authorization_code apps where direct new-flow initiation is blocked\n // server-side and the flow must be initiated through the OAuth /authorize endpoint.\n const storedExecutionId: any = !urlParams.executionId ? sessionStorage.getItem(`${vendor}_execution_id`) : null;\n const resumeExecutionId: any = urlParams.executionId || storedExecutionId;\n\n if (!resumeExecutionId && !effectiveApplicationId) {\n const error: any = new ThunderIDRuntimeError(\n 'Either executionId or applicationId is required for authentication',\n 'SIGN_IN_ERROR',\n 'react',\n );\n setError(error);\n throw error;\n }\n\n try {\n setFlowError(null);\n\n let response: EmbeddedSignInFlowResponse;\n\n if (resumeExecutionId) {\n try {\n response = (await signIn({\n executionId: resumeExecutionId,\n ...(challengeTokenRef.current ? {challengeToken: challengeTokenRef.current} : {}),\n })) as EmbeddedSignInFlowResponse;\n } catch (resumeError) {\n // Only treat a stale/expired session (HTTP 400 from the server, meaning the stored\n // executionId is no longer valid) as a recoverable condition. Transient failures\n // such as 5xx server errors or network errors are rethrown so they surface correctly.\n const isStaleSession: boolean =\n storedExecutionId &&\n resumeExecutionId === storedExecutionId &&\n resumeError instanceof ThunderIDAPIError &&\n resumeError.statusCode === 400;\n\n if (isStaleSession) {\n setExecutionId(null);\n try {\n const storageManager: any = await getStorageManager();\n await storageManager?.removeHybridDataParameter?.('authId');\n } catch {\n logger.warn('Failed to clear authId from hybrid storage.');\n }\n if (!effectiveApplicationId) {\n const expiredError: any = new ThunderIDRuntimeError(\n t('errors.signin.session.expired') ||\n 'Your session has expired. Please return to the application and sign in again.',\n 'SIGN_IN_ERROR',\n 'react',\n );\n setError(expiredError);\n return;\n }\n response = (await signIn({\n applicationId: effectiveApplicationId,\n flowType: EmbeddedFlowType.Authentication,\n ...(scopes && {scopes}),\n })) as EmbeddedSignInFlowResponse;\n } else {\n throw resumeError;\n }\n }\n } else {\n response = (await signIn({\n applicationId: effectiveApplicationId,\n flowType: EmbeddedFlowType.Authentication,\n ...(scopes && {scopes}),\n })) as EmbeddedSignInFlowResponse;\n }\n\n if (await handleRedirection(response)) {\n return;\n }\n\n // Handle a flow that completes (or errors) on the very first step — e.g. a reused SSO\n // session lets the backend return COMPLETE immediately with no UI components. Without\n // this the UI would fall through with no components and spin forever.\n if (await handleTerminalResponse(response)) {\n return;\n }\n\n const {\n executionId: normalizedExecutionId,\n components: normalizedComponents,\n additionalData: normalizedAdditionalData,\n } = normalizeFlowResponse(\n response,\n t,\n {\n resolveTranslations: false,\n },\n meta,\n );\n\n await setChallengeToken(response.challengeToken ?? null);\n\n if (normalizedExecutionId && normalizedComponents) {\n setExecutionId(normalizedExecutionId);\n setComponents(normalizedComponents);\n setAdditionalData(normalizedAdditionalData ?? {});\n setIsFlowInitialized(true);\n setIsTimeoutDisabled(false);\n // Clean up executionId from URL after setting it in state\n cleanupFlowUrlParams();\n }\n } catch (error) {\n const err: any = error;\n await clearFlowState();\n\n setError(err instanceof ThunderIDRuntimeError ? err : new Error(extractErrorMessage(err, t)));\n initializationAttemptedRef.current = false;\n }\n };\n\n /**\n * Initialize the flow and handle cleanup of stale flow state.\n */\n useEffect(() => {\n const urlParams: any = getUrlParams();\n\n // Check for OAuth error in URL\n if (urlParams.error) {\n handleOAuthError(urlParams.error, urlParams.errorDescription);\n return;\n }\n\n handleAuthId(urlParams.authId);\n\n // Skip OAuth code processing - let the dedicated OAuth useEffect handle it\n // No action needed here as the dedicated useEffect will handle it\n }, []);\n\n useEffect(() => {\n // Only initialize if we're not processing an OAuth callback or submission.\n // Wait for isStorageReady so the challenge token is restored from storage before\n // we attempt to resume a persisted executionId — the server requires it.\n const currentUrlParams: any = getUrlParams();\n if (\n isInitialized &&\n isStorageReady &&\n !isLoading &&\n !isFlowInitialized &&\n !initializationAttemptedRef.current &&\n !currentExecutionId &&\n !currentUrlParams.code &&\n !currentUrlParams.state &&\n !isSubmitting &&\n !oauthCodeProcessedRef.current\n ) {\n initializationAttemptedRef.current = true;\n initializeFlow();\n }\n }, [isInitialized, isStorageReady, isLoading, isFlowInitialized, currentExecutionId]);\n\n /**\n * Handle step timeout if configured in additionalData.\n */\n useEffect(() => {\n const timeoutMs: number = Number(additionalData?.['stepTimeout']) || 0;\n if (timeoutMs <= 0 || !isFlowInitialized) {\n setIsTimeoutDisabled(false);\n return undefined;\n }\n\n const remaining: number = Math.max(0, Math.floor((timeoutMs - Date.now()) / 1000));\n\n const handleTimeout = (): void => {\n const errorMessage: string = t('errors.signin.timeout') || 'Time allowed to complete the step has expired.';\n setError(new Error(errorMessage));\n setIsTimeoutDisabled(true);\n };\n\n if (remaining <= 0) {\n handleTimeout();\n return undefined;\n }\n\n const timerId: any = setTimeout(() => {\n handleTimeout();\n }, remaining * 1000);\n\n return () => clearTimeout(timerId);\n }, [additionalData?.['stepTimeout'], isFlowInitialized, t]);\n\n /**\n * Handle form submission from BaseSignIn or render props.\n */\n const handleSubmit = async (payload: EmbeddedSignInFlowRequest): Promise<void> => {\n // Use executionId from payload if available, otherwise fall back to currentExecutionId\n const effectiveExecutionId: any = payload.executionId || currentExecutionId;\n\n if (!effectiveExecutionId) {\n throw new Error('No active flow ID');\n }\n\n const processedInputs: Record<string, any> = {...payload.inputs};\n\n // Auto-compile consent decisions if we are currently on a consent prompt step\n if (additionalData?.['consentPrompt']) {\n try {\n const consentPromptRawData: any = additionalData['consentPrompt'];\n const purposes: any[] =\n typeof consentPromptRawData === 'string'\n ? JSON.parse(consentPromptRawData)\n : consentPromptRawData.purposes || consentPromptRawData;\n\n // Find the action component to determine if it was a deny action\n let isDeny = false;\n if (payload.action) {\n // Flatten components to find the action\n const findAction = (comps: any[]): any => {\n if (!comps || comps.length === 0) return null;\n\n const found: any = comps.find((c: any) => c.id === payload.action);\n if (found) return found;\n\n return comps.reduce((acc: any, c: any) => {\n if (acc) return acc;\n if (c.components) return findAction(c.components);\n return null;\n }, null);\n };\n\n const submitAction: any = findAction(components);\n\n if (submitAction && submitAction.variant?.toLowerCase() !== 'primary') {\n isDeny = true;\n }\n }\n\n const decisions: any = {\n purposes: purposes.map((p: any) => ({\n approved: !isDeny,\n elements: [\n ...(p.essential || []).map((e: any) => ({\n approved: !isDeny,\n name: e.name,\n })),\n ...(p.optional || []).map((e: any) => {\n const key = `__consent_opt__${p.purposeId}__${e.name}`;\n return {\n approved: isDeny ? false : processedInputs[key] !== 'false',\n name: e.name,\n };\n }),\n ],\n purposeName: p.purposeName,\n })),\n };\n processedInputs['consent_decisions'] = JSON.stringify(decisions);\n\n // Cleanup temporary consent tracking fields from inputs\n Object.keys(processedInputs).forEach((key: string) => {\n if (key.startsWith('__consent_opt__')) {\n delete processedInputs[key];\n }\n });\n } catch (e) {\n // Failed to construct consent_decisions payload automatically\n }\n }\n\n try {\n setIsSubmitting(true);\n setFlowError(null);\n // Clear any field errors from the previous response before the new round-trip.\n setServerFieldErrors(null);\n\n const response: EmbeddedSignInFlowResponse = (await signIn({\n executionId: effectiveExecutionId,\n ...payload,\n inputs: processedInputs,\n ...(challengeTokenRef.current ? {challengeToken: challengeTokenRef.current} : {}),\n })) as EmbeddedSignInFlowResponse;\n\n if (await handleRedirection(response)) {\n return;\n }\n if (\n response.data?.additionalData?.['passkeyChallenge'] ||\n response.data?.additionalData?.['passkeyCreationOptions']\n ) {\n const {passkeyChallenge, passkeyCreationOptions}: any = response.data.additionalData;\n const effectiveExecutionIdForPasskey: any = response.executionId || effectiveExecutionId;\n\n // Reset passkey processed ref to allow processing\n passkeyProcessedRef.current = false;\n\n await setChallengeToken(response.challengeToken ?? null);\n\n // Set passkey state to trigger the passkey\n setPasskeyState({\n actionId: 'submit',\n challenge: passkeyChallenge,\n creationOptions: passkeyCreationOptions,\n error: null,\n executionId: effectiveExecutionIdForPasskey,\n isActive: true,\n });\n setIsSubmitting(false);\n\n return;\n }\n\n const {\n executionId: normalizedExecutionId,\n components: normalizedComponents,\n additionalData: normalizedAdditionalData,\n } = normalizeFlowResponse(\n response,\n t,\n {\n resolveTranslations: false,\n },\n meta,\n );\n\n // Handle terminal flow statuses (Error throws, Complete redirects and returns true).\n if (await handleTerminalResponse(response)) {\n return;\n }\n\n // Always update challenge token on any INCOMPLETE response — token rotates every step.\n await setChallengeToken(response.challengeToken ?? null);\n\n // Update executionId if response contains a new one\n if (normalizedExecutionId && normalizedComponents) {\n setExecutionId(normalizedExecutionId);\n setComponents(normalizedComponents);\n setAdditionalData(normalizedAdditionalData ?? {});\n setIsTimeoutDisabled(false);\n // Ensure flow is marked as initialized when we have components\n setIsFlowInitialized(true);\n // Clean up executionId from URL after setting it in state\n cleanupFlowUrlParams();\n\n // Surface server-side validation failures so BaseSignIn can inject them into\n // the form-level fieldErrors state used by the render-prop / default UI.\n const responseFieldErrors: FieldError[] | undefined = (response.data as any)?.fieldErrors;\n if (responseFieldErrors && responseFieldErrors.length > 0) {\n setServerFieldErrors(responseFieldErrors);\n }\n\n // Display error from INCOMPLETE response\n if ((response as any)?.error) {\n setFlowError(new Error(extractErrorMessage(response, t)));\n }\n }\n } catch (error) {\n const err: any = error;\n await clearFlowState();\n\n setError(err instanceof ThunderIDRuntimeError ? err : new Error(extractErrorMessage(err, t)));\n return;\n } finally {\n setIsSubmitting(false);\n }\n };\n\n /**\n * Handle authentication errors.\n */\n const handleError = (error: Error): void => {\n setError(error);\n };\n\n useOAuthCallback({\n currentExecutionId,\n executionIdStorageKey: `${vendor}_execution_id`,\n isInitialized: isInitialized && !isLoading && isStorageReady,\n isSubmitting,\n onError: (err: any) => {\n clearFlowState();\n setError(err instanceof Error ? err : new Error(String(err)));\n },\n onSubmit: async (payload: any) => handleSubmit({executionId: payload.executionId, inputs: payload.inputs}),\n processedRef: oauthCodeProcessedRef,\n setExecutionId,\n });\n\n /**\n * Handle passkey authentication/registration when passkey state becomes active.\n * This effect auto-triggers the browser passkey popup and submits the result.\n */\n useEffect(() => {\n if (\n !passkeyState.isActive ||\n (!passkeyState.challenge && !passkeyState.creationOptions) ||\n !passkeyState.executionId\n ) {\n return;\n }\n\n // Prevent re-processing\n if (passkeyProcessedRef.current) {\n return;\n }\n passkeyProcessedRef.current = true;\n\n const performPasskeyProcess = async (): Promise<void> => {\n let inputs: Record<string, string>;\n\n if (passkeyState.challenge) {\n const passkeyResponse: any = await handlePasskeyAuthentication(passkeyState.challenge);\n const passkeyResponseObj: any = JSON.parse(passkeyResponse);\n\n inputs = {\n authenticatorData: passkeyResponseObj.response.authenticatorData,\n clientDataJSON: passkeyResponseObj.response.clientDataJSON,\n credentialId: passkeyResponseObj.id,\n signature: passkeyResponseObj.response.signature,\n userHandle: passkeyResponseObj.response.userHandle,\n };\n } else if (passkeyState.creationOptions) {\n const passkeyResponse: any = await handlePasskeyRegistration(passkeyState.creationOptions);\n const passkeyResponseObj: any = JSON.parse(passkeyResponse);\n\n inputs = {\n attestationObject: passkeyResponseObj.response.attestationObject,\n clientDataJSON: passkeyResponseObj.response.clientDataJSON,\n credentialId: passkeyResponseObj.id,\n };\n } else {\n throw new Error('No passkey challenge or creation options available');\n }\n\n await handleSubmit({\n executionId: passkeyState.executionId ?? undefined,\n inputs,\n });\n };\n\n performPasskeyProcess()\n .then(() => {\n setPasskeyState({\n actionId: null,\n challenge: null,\n creationOptions: null,\n error: null,\n executionId: null,\n isActive: false,\n });\n })\n .catch((error: any) => {\n setPasskeyState((prev: any) => ({...prev, error: error as Error, isActive: false}));\n setFlowError(error as Error);\n onError?.(error as Error);\n });\n }, [passkeyState.isActive, passkeyState.challenge, passkeyState.creationOptions, passkeyState.executionId]);\n\n if (children) {\n // Collapse the server FieldError[] array to a single message per field map for\n // render-prop consumers. First error per field wins. Multi-error cases per\n // field are rare in practice (server typically returns one rule failure per\n // field in current flows) and consumers needing the full array can still read\n // it from the raw flow response.\n const renderPropFieldErrors: Record<string, string> = {};\n if (serverFieldErrors) {\n for (const fe of serverFieldErrors) {\n if (!(fe.identifier in renderPropFieldErrors)) {\n renderPropFieldErrors[fe.identifier] = fe.message;\n }\n }\n }\n\n const renderProps: SignInRenderProps = {\n additionalData,\n components,\n error: flowError,\n fieldErrors: renderPropFieldErrors,\n initialize: initializeFlow,\n isInitialized: isFlowInitialized,\n isLoading: isLoading || isSubmitting || !isInitialized,\n isTimeoutDisabled,\n meta,\n onSubmit: handleSubmit,\n };\n\n return <>{children(renderProps)}</>;\n }\n // Otherwise, render the default BaseSignIn component\n return (\n <BaseSignIn\n additionalData={additionalData}\n components={components}\n isLoading={isLoading || !isInitialized || !isFlowInitialized}\n isTimeoutDisabled={isTimeoutDisabled}\n onSubmit={handleSubmit}\n onError={handleError}\n error={flowError}\n className={className}\n size={size}\n variant={variant}\n preferences={preferences}\n serverFieldErrors={serverFieldErrors}\n />\n );\n};\n\nexport default SignIn;\n"],"mappings":"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"}
|
|
@@ -22,6 +22,12 @@ export interface I18nProviderProps {
|
|
|
22
22
|
* The i18n preferences from the ThunderIDProvider
|
|
23
23
|
*/
|
|
24
24
|
preferences?: I18nPreferences;
|
|
25
|
+
/**
|
|
26
|
+
* Vendor/brand namespace used to derive the default language storage key
|
|
27
|
+
* (e.g. `${vendor}-i18n-language`). Resolved from the SDK config's `vendor` option.
|
|
28
|
+
* @default 'thunderid'
|
|
29
|
+
*/
|
|
30
|
+
vendor?: string;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* I18nProvider component that manages internationalization state and provides
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"I18nProvider.d.ts","sourceRoot":"","sources":["../../../src/contexts/I18n/I18nProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,eAAe,
|
|
1
|
+
{"version":3,"file":"I18nProvider.d.ts","sourceRoot":"","sources":["../../../src/contexts/I18n/I18nProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,eAAe,EAShB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAAE,iBAAiB,EAA0D,MAAM,OAAO,CAAC;AAUrG,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAE9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqFD;;;GAGG;AACH,QAAA,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CA6L1D,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import I18nContext_default from "./I18nContext.js";
|
|
2
|
-
import { TranslationBundleConstants, createPackageComponentLogger, deepMerge, getDefaultI18nBundles, normalizeTranslations } from "@thunderid/browser";
|
|
2
|
+
import { TranslationBundleConstants, createPackageComponentLogger, deepMerge, getDefaultI18nBundles, getVendorPrefix, normalizeTranslations } from "@thunderid/browser";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
|
|
6
6
|
//#region src/contexts/I18n/I18nProvider.tsx
|
|
7
7
|
const logger$1 = createPackageComponentLogger("@thunderid/react", "I18nProvider");
|
|
8
|
-
const DEFAULT_STORAGE_KEY = "thunderid-i18n-language";
|
|
9
8
|
const DEFAULT_URL_PARAM = "lang";
|
|
10
9
|
const detectBrowserLanguage = () => {
|
|
11
10
|
if (typeof window !== "undefined" && window.navigator) return window.navigator.language || TranslationBundleConstants.FALLBACK_LOCALE;
|
|
@@ -72,10 +71,10 @@ const detectUrlParamLanguage = (paramName) => {
|
|
|
72
71
|
* I18nProvider component that manages internationalization state and provides
|
|
73
72
|
* translation functions to child components.
|
|
74
73
|
*/
|
|
75
|
-
const I18nProvider = ({ children, preferences }) => {
|
|
74
|
+
const I18nProvider = ({ children, preferences, vendor }) => {
|
|
76
75
|
const defaultBundles = getDefaultI18nBundles();
|
|
77
76
|
const storageStrategy = preferences?.storageStrategy ?? "cookie";
|
|
78
|
-
const storageKey = preferences?.storageKey ??
|
|
77
|
+
const storageKey = preferences?.storageKey ?? `${getVendorPrefix(vendor)}-i18n-language`;
|
|
79
78
|
const urlParamConfig = preferences?.urlParam === void 0 ? DEFAULT_URL_PARAM : preferences.urlParam;
|
|
80
79
|
const resolvedCookieDomain = useMemo(() => {
|
|
81
80
|
if (storageStrategy !== "cookie") return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"I18nProvider.js","names":["logger: ReturnType<typeof createPackageComponentLogger>","parts: string[]","match: RegExpMatchArray | null","maxAge: number","secure: string","domain: string","I18nProvider: FC<PropsWithChildren<I18nProviderProps>>","defaultBundles: Record<string, I18nBundle>","storageStrategy: I18nStorageStrategy","storageKey: string","urlParamConfig: string | false","resolvedCookieDomain: string | undefined","storage: StorageAdapter","urlLanguage: string | null","storedLanguage: string | null","browserLanguage: string","injectBundles: (newBundles: Record<string, I18nBundle>) => void","merged: Record<string, I18nBundle>","normalizedTranslations: I18nTranslations","mergedBundles: Record<string, I18nBundle>","languageKey: string","fallbackLanguage: string","t: (key: string, params?: Record<string, string | number>) => string","translation: string | undefined","currentBundle: I18nBundle | undefined","fallbackBundle: I18nBundle | undefined","setLanguage: (language: string) => void","contextValue: I18nContextValue","I18nContext"],"sources":["../../../src/contexts/I18n/I18nProvider.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n deepMerge,\n I18nPreferences,\n I18nStorageStrategy,\n createPackageComponentLogger,\n I18nBundle,\n I18nTranslations,\n TranslationBundleConstants,\n getDefaultI18nBundles,\n normalizeTranslations,\n} from '@thunderid/browser';\nimport {FC, PropsWithChildren, ReactElement, useCallback, useEffect, useMemo, useState} from 'react';\nimport I18nContext, {I18nContextValue} from './I18nContext';\n\nconst logger: ReturnType<typeof createPackageComponentLogger> = createPackageComponentLogger(\n '@thunderid/react',\n 'I18nProvider',\n);\n\nconst DEFAULT_STORAGE_KEY = 'thunderid-i18n-language';\nconst DEFAULT_URL_PARAM = 'lang';\n\nexport interface I18nProviderProps {\n /**\n * The i18n preferences from the ThunderIDProvider\n */\n preferences?: I18nPreferences;\n}\n\nconst detectBrowserLanguage = (): string => {\n if (typeof window !== 'undefined' && window.navigator) {\n return window.navigator.language || TranslationBundleConstants.FALLBACK_LOCALE;\n }\n\n return TranslationBundleConstants.FALLBACK_LOCALE;\n};\n\nconst deriveRootDomain = (hostname: string): string => {\n const parts: string[] = hostname.split('.');\n return parts.length > 1 ? parts.slice(-2).join('.') : hostname;\n};\n\nconst getCookie = (name: string): string | null => {\n if (typeof document === 'undefined') return null;\n const match: RegExpMatchArray | null = new RegExp(\n `(?:^|; )${name.replace(/([.*+?^${}()|[\\]\\\\])/g, '\\\\$1')}=([^;]*)`,\n ).exec(document.cookie);\n return match ? decodeURIComponent(match[1]) : null;\n};\n\nconst setCookie = (name: string, value: string, domain: string): void => {\n if (typeof document === 'undefined') return;\n const maxAge: number = 365 * 24 * 60 * 60;\n const secure: string = typeof window !== 'undefined' && window.location.protocol === 'https:' ? '; Secure' : '';\n document.cookie =\n `${encodeURIComponent(name)}=${encodeURIComponent(value)}` +\n `; Max-Age=${maxAge}` +\n `; Path=/` +\n `; Domain=${domain}` +\n `; SameSite=Lax${secure}`;\n};\n\ninterface StorageAdapter {\n read: () => string | null;\n write: (language: string) => void;\n}\n\nconst createStorageAdapter = (strategy: I18nStorageStrategy, key: string, cookieDomain?: string): StorageAdapter => {\n switch (strategy) {\n case 'cookie':\n return {\n read: (): string | null => getCookie(key),\n write: (language: string): void => {\n const domain: string =\n cookieDomain ?? (typeof window !== 'undefined' ? deriveRootDomain(window.location.hostname) : '');\n if (domain) setCookie(key, language, domain);\n },\n };\n case 'localStorage':\n return {\n read: (): string | null => {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n write: (language: string): void => {\n if (typeof window === 'undefined' || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, language);\n } catch {\n logger.warn('Failed to persist language preference to localStorage.');\n }\n },\n };\n case 'none':\n default:\n return {read: (): null => null, write: (): void => {}};\n }\n};\n\nconst detectUrlParamLanguage = (paramName: string): string | null => {\n if (typeof window === 'undefined') return null;\n try {\n return new URLSearchParams(window.location.search).get(paramName);\n } catch {\n return null;\n }\n};\n\n/**\n * I18nProvider component that manages internationalization state and provides\n * translation functions to child components.\n */\nconst I18nProvider: FC<PropsWithChildren<I18nProviderProps>> = ({\n children,\n preferences,\n}: PropsWithChildren<I18nProviderProps>): ReactElement => {\n // Get default bundles from the browser package\n const defaultBundles: Record<string, I18nBundle> = getDefaultI18nBundles();\n\n const storageStrategy: I18nStorageStrategy = preferences?.storageStrategy ?? 'cookie';\n const storageKey: string = preferences?.storageKey ?? DEFAULT_STORAGE_KEY;\n const urlParamConfig: string | false = preferences?.urlParam === undefined ? DEFAULT_URL_PARAM : preferences.urlParam;\n\n const resolvedCookieDomain: string | undefined = useMemo((): string | undefined => {\n if (storageStrategy !== 'cookie') return undefined;\n if (preferences?.cookieDomain) return preferences.cookieDomain;\n return typeof window !== 'undefined' ? deriveRootDomain(window.location.hostname) : undefined;\n }, [storageStrategy, preferences?.cookieDomain]);\n\n const storage: StorageAdapter = useMemo(\n () => createStorageAdapter(storageStrategy, storageKey, resolvedCookieDomain),\n [storageStrategy, storageKey, resolvedCookieDomain],\n );\n\n const determineInitialLanguage = (): string => {\n if (preferences?.language) return preferences.language;\n if (urlParamConfig !== false) {\n const urlLanguage: string | null = detectUrlParamLanguage(urlParamConfig);\n if (urlLanguage) {\n storage.write(urlLanguage);\n return urlLanguage;\n }\n }\n const storedLanguage: string | null = storage.read();\n if (storedLanguage) return storedLanguage;\n const browserLanguage: string = detectBrowserLanguage();\n if (browserLanguage) return browserLanguage;\n return preferences?.fallbackLanguage || TranslationBundleConstants.FALLBACK_LOCALE;\n };\n\n const [currentLanguage, setCurrentLanguage] = useState<string>(determineInitialLanguage);\n\n // Bundles injected at runtime (e.g., from flow metadata i18n translations).\n // These take precedence over defaults but are overridden by prop-provided bundles.\n const [injectedBundles, setInjectedBundles] = useState<Record<string, I18nBundle>>({});\n\n const injectBundles: (newBundles: Record<string, I18nBundle>) => void = useCallback(\n (newBundles: Record<string, I18nBundle>): void => {\n setInjectedBundles((prev: Record<string, I18nBundle>) => {\n const merged: Record<string, I18nBundle> = {...prev};\n Object.entries(newBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n bundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...bundle, translations: normalizedTranslations};\n }\n });\n return merged;\n });\n },\n [],\n );\n\n /**\n * Merge bundles in priority order: defaults → injected (meta) → prop-provided\n */\n const mergedBundles: Record<string, I18nBundle> = useMemo(() => {\n const merged: Record<string, I18nBundle> = {};\n\n // 1. Default bundles\n Object.entries(defaultBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n // Convert key format (e.g., 'en_US' to 'en-US')\n const languageKey: string = key.replace('_', '-');\n merged[languageKey] = bundle;\n });\n\n // 2. Injected bundles (e.g., from flow metadata) — override defaults\n Object.entries(injectedBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n bundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...bundle, translations: normalizedTranslations};\n }\n });\n\n // 3. User-provided bundles (from props) — highest priority, override everything\n if (preferences?.bundles) {\n Object.entries(preferences.bundles).forEach(([key, userBundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n userBundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n metadata: userBundle.metadata ? {...merged[key].metadata, ...userBundle.metadata} : merged[key].metadata,\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...userBundle, translations: normalizedTranslations};\n }\n });\n }\n\n return merged;\n }, [defaultBundles, injectedBundles, preferences?.bundles]);\n\n const fallbackLanguage: string = preferences?.fallbackLanguage || TranslationBundleConstants.FALLBACK_LOCALE;\n\n // Persist language changes to the configured storage.\n useEffect(() => {\n storage.write(currentLanguage);\n }, [currentLanguage, storage]);\n\n // Translation function\n const t: (key: string, params?: Record<string, string | number>) => string = useCallback(\n (key: string, params?: Record<string, string | number>): string => {\n let translation: string | undefined;\n\n // Try to get translation from current language bundle\n const currentBundle: I18nBundle | undefined = mergedBundles[currentLanguage];\n if (currentBundle?.translations[key]) {\n translation = currentBundle.translations[key];\n }\n\n // Fallback to fallback language if translation not found\n if (!translation && currentLanguage !== fallbackLanguage) {\n const fallbackBundle: I18nBundle | undefined = mergedBundles[fallbackLanguage];\n if (fallbackBundle?.translations[key]) {\n translation = fallbackBundle.translations[key];\n }\n }\n\n // If still no translation found, return the key itself\n if (!translation) {\n translation = key;\n }\n\n // Replace parameters if provided\n if (params && Object.keys(params).length > 0) {\n return Object.entries(params).reduce(\n (acc: string, [paramKey, paramValue]: [string, string | number]) =>\n acc.replace(new RegExp(`\\\\{${paramKey}\\\\}`, 'g'), String(paramValue)),\n translation,\n );\n }\n\n return translation;\n },\n [mergedBundles, currentLanguage, fallbackLanguage],\n );\n\n // Language setter function\n const setLanguage: (language: string) => void = useCallback(\n (language: string) => {\n if (mergedBundles[language]) {\n setCurrentLanguage(language);\n } else {\n logger.warn(\n `Language '${language}' is not available. Available languages: ${Object.keys(mergedBundles).join(', ')}`,\n );\n }\n },\n [mergedBundles],\n );\n\n const contextValue: I18nContextValue = useMemo(\n () => ({\n bundles: mergedBundles,\n currentLanguage,\n fallbackLanguage,\n injectBundles,\n setLanguage,\n t,\n }),\n [currentLanguage, fallbackLanguage, injectBundles, mergedBundles, setLanguage, t],\n );\n\n return <I18nContext.Provider value={contextValue}>{children}</I18nContext.Provider>;\n};\n\nexport default I18nProvider;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"I18nProvider.js","names":["logger: ReturnType<typeof createPackageComponentLogger>","parts: string[]","match: RegExpMatchArray | null","maxAge: number","secure: string","domain: string","I18nProvider: FC<PropsWithChildren<I18nProviderProps>>","defaultBundles: Record<string, I18nBundle>","storageStrategy: I18nStorageStrategy","storageKey: string","urlParamConfig: string | false","resolvedCookieDomain: string | undefined","storage: StorageAdapter","urlLanguage: string | null","storedLanguage: string | null","browserLanguage: string","injectBundles: (newBundles: Record<string, I18nBundle>) => void","merged: Record<string, I18nBundle>","normalizedTranslations: I18nTranslations","mergedBundles: Record<string, I18nBundle>","languageKey: string","fallbackLanguage: string","t: (key: string, params?: Record<string, string | number>) => string","translation: string | undefined","currentBundle: I18nBundle | undefined","fallbackBundle: I18nBundle | undefined","setLanguage: (language: string) => void","contextValue: I18nContextValue","I18nContext"],"sources":["../../../src/contexts/I18n/I18nProvider.tsx"],"sourcesContent":["/**\n * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n deepMerge,\n I18nPreferences,\n I18nStorageStrategy,\n createPackageComponentLogger,\n I18nBundle,\n I18nTranslations,\n TranslationBundleConstants,\n getDefaultI18nBundles,\n normalizeTranslations,\n getVendorPrefix,\n} from '@thunderid/browser';\nimport {FC, PropsWithChildren, ReactElement, useCallback, useEffect, useMemo, useState} from 'react';\nimport I18nContext, {I18nContextValue} from './I18nContext';\n\nconst logger: ReturnType<typeof createPackageComponentLogger> = createPackageComponentLogger(\n '@thunderid/react',\n 'I18nProvider',\n);\n\nconst DEFAULT_URL_PARAM = 'lang';\n\nexport interface I18nProviderProps {\n /**\n * The i18n preferences from the ThunderIDProvider\n */\n preferences?: I18nPreferences;\n\n /**\n * Vendor/brand namespace used to derive the default language storage key\n * (e.g. `${vendor}-i18n-language`). Resolved from the SDK config's `vendor` option.\n * @default 'thunderid'\n */\n vendor?: string;\n}\n\nconst detectBrowserLanguage = (): string => {\n if (typeof window !== 'undefined' && window.navigator) {\n return window.navigator.language || TranslationBundleConstants.FALLBACK_LOCALE;\n }\n\n return TranslationBundleConstants.FALLBACK_LOCALE;\n};\n\nconst deriveRootDomain = (hostname: string): string => {\n const parts: string[] = hostname.split('.');\n return parts.length > 1 ? parts.slice(-2).join('.') : hostname;\n};\n\nconst getCookie = (name: string): string | null => {\n if (typeof document === 'undefined') return null;\n const match: RegExpMatchArray | null = new RegExp(\n `(?:^|; )${name.replace(/([.*+?^${}()|[\\]\\\\])/g, '\\\\$1')}=([^;]*)`,\n ).exec(document.cookie);\n return match ? decodeURIComponent(match[1]) : null;\n};\n\nconst setCookie = (name: string, value: string, domain: string): void => {\n if (typeof document === 'undefined') return;\n const maxAge: number = 365 * 24 * 60 * 60;\n const secure: string = typeof window !== 'undefined' && window.location.protocol === 'https:' ? '; Secure' : '';\n document.cookie =\n `${encodeURIComponent(name)}=${encodeURIComponent(value)}` +\n `; Max-Age=${maxAge}` +\n `; Path=/` +\n `; Domain=${domain}` +\n `; SameSite=Lax${secure}`;\n};\n\ninterface StorageAdapter {\n read: () => string | null;\n write: (language: string) => void;\n}\n\nconst createStorageAdapter = (strategy: I18nStorageStrategy, key: string, cookieDomain?: string): StorageAdapter => {\n switch (strategy) {\n case 'cookie':\n return {\n read: (): string | null => getCookie(key),\n write: (language: string): void => {\n const domain: string =\n cookieDomain ?? (typeof window !== 'undefined' ? deriveRootDomain(window.location.hostname) : '');\n if (domain) setCookie(key, language, domain);\n },\n };\n case 'localStorage':\n return {\n read: (): string | null => {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n write: (language: string): void => {\n if (typeof window === 'undefined' || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, language);\n } catch {\n logger.warn('Failed to persist language preference to localStorage.');\n }\n },\n };\n case 'none':\n default:\n return {read: (): null => null, write: (): void => {}};\n }\n};\n\nconst detectUrlParamLanguage = (paramName: string): string | null => {\n if (typeof window === 'undefined') return null;\n try {\n return new URLSearchParams(window.location.search).get(paramName);\n } catch {\n return null;\n }\n};\n\n/**\n * I18nProvider component that manages internationalization state and provides\n * translation functions to child components.\n */\nconst I18nProvider: FC<PropsWithChildren<I18nProviderProps>> = ({\n children,\n preferences,\n vendor,\n}: PropsWithChildren<I18nProviderProps>): ReactElement => {\n // Get default bundles from the browser package\n const defaultBundles: Record<string, I18nBundle> = getDefaultI18nBundles();\n\n const storageStrategy: I18nStorageStrategy = preferences?.storageStrategy ?? 'cookie';\n const storageKey: string = preferences?.storageKey ?? `${getVendorPrefix(vendor)}-i18n-language`;\n const urlParamConfig: string | false = preferences?.urlParam === undefined ? DEFAULT_URL_PARAM : preferences.urlParam;\n\n const resolvedCookieDomain: string | undefined = useMemo((): string | undefined => {\n if (storageStrategy !== 'cookie') return undefined;\n if (preferences?.cookieDomain) return preferences.cookieDomain;\n return typeof window !== 'undefined' ? deriveRootDomain(window.location.hostname) : undefined;\n }, [storageStrategy, preferences?.cookieDomain]);\n\n const storage: StorageAdapter = useMemo(\n () => createStorageAdapter(storageStrategy, storageKey, resolvedCookieDomain),\n [storageStrategy, storageKey, resolvedCookieDomain],\n );\n\n const determineInitialLanguage = (): string => {\n if (preferences?.language) return preferences.language;\n if (urlParamConfig !== false) {\n const urlLanguage: string | null = detectUrlParamLanguage(urlParamConfig);\n if (urlLanguage) {\n storage.write(urlLanguage);\n return urlLanguage;\n }\n }\n const storedLanguage: string | null = storage.read();\n if (storedLanguage) return storedLanguage;\n const browserLanguage: string = detectBrowserLanguage();\n if (browserLanguage) return browserLanguage;\n return preferences?.fallbackLanguage || TranslationBundleConstants.FALLBACK_LOCALE;\n };\n\n const [currentLanguage, setCurrentLanguage] = useState<string>(determineInitialLanguage);\n\n // Bundles injected at runtime (e.g., from flow metadata i18n translations).\n // These take precedence over defaults but are overridden by prop-provided bundles.\n const [injectedBundles, setInjectedBundles] = useState<Record<string, I18nBundle>>({});\n\n const injectBundles: (newBundles: Record<string, I18nBundle>) => void = useCallback(\n (newBundles: Record<string, I18nBundle>): void => {\n setInjectedBundles((prev: Record<string, I18nBundle>) => {\n const merged: Record<string, I18nBundle> = {...prev};\n Object.entries(newBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n bundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...bundle, translations: normalizedTranslations};\n }\n });\n return merged;\n });\n },\n [],\n );\n\n /**\n * Merge bundles in priority order: defaults → injected (meta) → prop-provided\n */\n const mergedBundles: Record<string, I18nBundle> = useMemo(() => {\n const merged: Record<string, I18nBundle> = {};\n\n // 1. Default bundles\n Object.entries(defaultBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n // Convert key format (e.g., 'en_US' to 'en-US')\n const languageKey: string = key.replace('_', '-');\n merged[languageKey] = bundle;\n });\n\n // 2. Injected bundles (e.g., from flow metadata) — override defaults\n Object.entries(injectedBundles).forEach(([key, bundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n bundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...bundle, translations: normalizedTranslations};\n }\n });\n\n // 3. User-provided bundles (from props) — highest priority, override everything\n if (preferences?.bundles) {\n Object.entries(preferences.bundles).forEach(([key, userBundle]: [string, I18nBundle]) => {\n const normalizedTranslations: I18nTranslations = normalizeTranslations(\n userBundle.translations as unknown as Record<string, string | Record<string, string>>,\n );\n if (merged[key]) {\n merged[key] = {\n ...merged[key],\n metadata: userBundle.metadata ? {...merged[key].metadata, ...userBundle.metadata} : merged[key].metadata,\n translations: deepMerge(merged[key].translations, normalizedTranslations),\n };\n } else {\n merged[key] = {...userBundle, translations: normalizedTranslations};\n }\n });\n }\n\n return merged;\n }, [defaultBundles, injectedBundles, preferences?.bundles]);\n\n const fallbackLanguage: string = preferences?.fallbackLanguage || TranslationBundleConstants.FALLBACK_LOCALE;\n\n // Persist language changes to the configured storage.\n useEffect(() => {\n storage.write(currentLanguage);\n }, [currentLanguage, storage]);\n\n // Translation function\n const t: (key: string, params?: Record<string, string | number>) => string = useCallback(\n (key: string, params?: Record<string, string | number>): string => {\n let translation: string | undefined;\n\n // Try to get translation from current language bundle\n const currentBundle: I18nBundle | undefined = mergedBundles[currentLanguage];\n if (currentBundle?.translations[key]) {\n translation = currentBundle.translations[key];\n }\n\n // Fallback to fallback language if translation not found\n if (!translation && currentLanguage !== fallbackLanguage) {\n const fallbackBundle: I18nBundle | undefined = mergedBundles[fallbackLanguage];\n if (fallbackBundle?.translations[key]) {\n translation = fallbackBundle.translations[key];\n }\n }\n\n // If still no translation found, return the key itself\n if (!translation) {\n translation = key;\n }\n\n // Replace parameters if provided\n if (params && Object.keys(params).length > 0) {\n return Object.entries(params).reduce(\n (acc: string, [paramKey, paramValue]: [string, string | number]) =>\n acc.replace(new RegExp(`\\\\{${paramKey}\\\\}`, 'g'), String(paramValue)),\n translation,\n );\n }\n\n return translation;\n },\n [mergedBundles, currentLanguage, fallbackLanguage],\n );\n\n // Language setter function\n const setLanguage: (language: string) => void = useCallback(\n (language: string) => {\n if (mergedBundles[language]) {\n setCurrentLanguage(language);\n } else {\n logger.warn(\n `Language '${language}' is not available. Available languages: ${Object.keys(mergedBundles).join(', ')}`,\n );\n }\n },\n [mergedBundles],\n );\n\n const contextValue: I18nContextValue = useMemo(\n () => ({\n bundles: mergedBundles,\n currentLanguage,\n fallbackLanguage,\n injectBundles,\n setLanguage,\n t,\n }),\n [currentLanguage, fallbackLanguage, injectBundles, mergedBundles, setLanguage, t],\n );\n\n return <I18nContext.Provider value={contextValue}>{children}</I18nContext.Provider>;\n};\n\nexport default I18nProvider;\n"],"mappings":"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"}
|
|
@@ -184,6 +184,11 @@ export type ThunderIDContextProps = {
|
|
|
184
184
|
signUp: (...args: any[]) => Promise<any>;
|
|
185
185
|
signUpUrl: string | undefined;
|
|
186
186
|
user: any;
|
|
187
|
+
/**
|
|
188
|
+
* Vendor/brand namespace used to prefix storage keys, cookie names, and CSS class names.
|
|
189
|
+
* Resolved from the `vendor` config option, defaulting to `'thunderid'`.
|
|
190
|
+
*/
|
|
191
|
+
vendor: string;
|
|
187
192
|
} & Pick<ThunderIDReactClient, 'clearSession'>;
|
|
188
193
|
/**
|
|
189
194
|
* Context object for managing the Authentication flow builder core context.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/ThunderID/ThunderIDContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,wBAAwB,EACxB,aAAa,EACb,0BAA0B,EAC1B,aAAa,
|
|
1
|
+
{"version":3,"file":"ThunderIDContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/ThunderID/ThunderIDContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VendorConstants } from "@thunderid/browser";
|
|
1
2
|
import { createContext } from "react";
|
|
2
3
|
|
|
3
4
|
//#region src/contexts/ThunderID/ThunderIDContext.ts
|
|
@@ -37,7 +38,8 @@ const ThunderIDContext = createContext({
|
|
|
37
38
|
signOut: () => Promise.resolve({}),
|
|
38
39
|
signUp: () => Promise.resolve({}),
|
|
39
40
|
signUpUrl: void 0,
|
|
40
|
-
user: null
|
|
41
|
+
user: null,
|
|
42
|
+
vendor: VendorConstants.VENDOR_PREFIX
|
|
41
43
|
});
|
|
42
44
|
ThunderIDContext.displayName = "ThunderIDContext";
|
|
43
45
|
var ThunderIDContext_default = ThunderIDContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDContext.js","names":["ThunderIDContext: Context<ThunderIDContextProps | null>"],"sources":["../../../src/contexts/ThunderID/ThunderIDContext.ts"],"sourcesContent":["/**\n * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n FlowMetadataResponse,\n HttpRequestConfig,\n HttpResponse,\n IdToken,\n OIDCDiscoveryApiResponse,\n SignInOptions,\n TokenExchangeRequestConfig,\n TokenResponse,\n} from '@thunderid/browser';\nimport {Context, createContext} from 'react';\nimport {ThunderIDReactConfig} from '../../models/config';\nimport ThunderIDReactClient from '../../ThunderIDReactClient';\n\n/**\n * Props interface of {@link ThunderIDContext}\n */\nexport type ThunderIDContextProps = {\n afterSignInUrl: string | undefined;\n applicationId: string | undefined;\n baseUrl: string | undefined;\n clientId: string | undefined;\n scopes: string | string[] | undefined;\n /**\n * OIDC discovery data.\n */\n discovery: {\n /**\n * The response from the `.well-known/openid-configuration` endpoint.\n * Contains server capabilities, supported endpoints, and metadata.\n * `null` while loading or when discovery has not been fetched.\n */\n wellKnown: OIDCDiscoveryApiResponse | null;\n };\n /**\n * Swaps the current access token with a new one based on the provided configuration (with a grant type).\n * @param config - Configuration for the token exchange request.\n * @returns A promise that resolves to the token response or the raw response.\n */\n exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;\n /**\n * Retrieves the access token stored in the storage.\n * This function retrieves the access token and returns it.\n * @remarks This does not work in the `webWorker` or any other worker environment.\n * @returns A promise that resolves to the access token.\n */\n getAccessToken: () => Promise<string>;\n /**\n * Function to retrieve the decoded ID token.\n * This function decodes the ID token and returns its payload.\n * It can be used to access user claims and other information contained in the ID token.\n *\n * @returns A promise that resolves to the decoded ID token payload.\n */\n getDecodedIdToken: () => Promise<IdToken>;\n /**\n * Function to retrieve the ID token.\n * This function retrieves the ID token and returns it.\n *\n * @returns A promise that resolves to the ID token.\n */\n getIdToken: () => Promise<string>;\n /**\n * Returns the underlying StorageManager instance for reading and writing SDK-managed storage.\n */\n getStorageManager: () => Promise<any>;\n /**\n * HTTP request function to make API calls.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n http: {\n /**\n * Makes an HTTP request using the provided configuration.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n request: (requestConfig?: HttpRequestConfig) => Promise<HttpResponse<any>>;\n /**\n * Makes multiple HTTP requests based on the provided configuration.\n * @param requestConfigs - Set of configurations for the HTTP requests.\n * @returns A promise that resolves to an array of HTTP responses.\n */\n requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise<HttpResponse<any>[]>;\n };\n /**\n * Instance ID for multi-instance support.\n */\n instanceId: number;\n isInitialized: boolean;\n /**\n * Flag indicating whether the SDK is working in the background.\n */\n isLoading: boolean;\n /**\n * Flag indicating whether flow metadata is currently being fetched.\n */\n isMetaLoading: boolean;\n /**\n * Flag indicating whether the user is signed in or not.\n */\n isSignedIn: boolean;\n\n /**\n * Flow metadata returned by `GET /flow/meta` (v2 platform only).\n * `null` while loading or when `resolveFromMeta` is disabled.\n */\n meta: FlowMetadataResponse | null;\n\n organizationHandle: string | undefined;\n\n /**\n * Re-initializes the client with a new configuration.\n *\n * @remarks\n * This can be partial configuration to update only specific fields.\n *\n * @param config - New configuration to re-initialize the client with.\n * @returns Promise resolving to boolean indicating success.\n */\n reInitialize: (config: Partial<ThunderIDReactConfig>) => Promise<boolean>;\n\n /**\n * Recovery function to initiate the account/password recovery flow.\n */\n recover: (...args: any[]) => Promise<any>;\n\n /**\n * Resolves `{{ t(key) }}` and `{{ meta(path) }}` template expressions in a string,\n * using the current i18n translation function and flow metadata from context.\n *\n * Useful in render-props patterns where consumers need to expand template strings\n * that come from the server (e.g. component labels, placeholders, headings).\n *\n * @example\n * const {resolveFlowTemplateLiterals} = useThunderID();\n * resolveFlowTemplateLiterals('{{ t(signin.heading.label) }}') // → 'Sign In'\n * resolveFlowTemplateLiterals('Login to {{ meta(application.name) }}') // → 'Login to My App'\n */\n resolveFlowTemplateLiterals: (text: string | undefined) => string;\n\n /**\n * Sign-in function to initiate the authentication process.\n * @remark This is the programmatic version of the `SignInButton` component.\n * TODO: Fix the types.\n */\n signIn: (...args: any) => Promise<any>;\n\n /**\n * Optional additional parameters to be sent in the sign-in request.\n * This can include custom parameters that your authorization server supports.\n * These parameters will be included in the authorization request sent to the server.\n * If not provided, no additional parameters will be sent.\n *\n * @example\n * signInOptions: { prompt: \"login\", fidp: \"OrganizationSSO\" }\n */\n signInOptions?: SignInOptions;\n\n /**\n * Optional token request configuration. params are appended to the token endpoint POST body.\n *\n * @example\n * tokenRequest: { params: { resource: \"https://api.example.com\" } }\n */\n tokenRequest?: {\n params?: Record<string, unknown>;\n };\n\n /**\n * Silent sign-in function to re-authenticate the user without user interaction.\n * @remark This is the programmatic version of the `SilentSignIn` component.\n */\n signInSilently: ThunderIDReactClient['signInSilently'];\n\n signInUrl: string | undefined;\n /**\n * Sign-out function to terminate the authentication session.\n * @remark This is the programmatic version of the `SignOutButton` component.\n * FIXME: Fix the types.\n */\n signOut: any;\n\n /**\n * Sign-up function to initiate the registration process.\n * @remark This is the programmatic version of the `SignUpButton` component.\n */\n signUp: (...args: any[]) => Promise<any>;\n\n signUpUrl: string | undefined;\n\n user: any;\n} & Pick<ThunderIDReactClient, 'clearSession'>;\n\n/**\n * Context object for managing the Authentication flow builder core context.\n */\nconst ThunderIDContext: Context<ThunderIDContextProps | null> = createContext<null | ThunderIDContextProps>({\n afterSignInUrl: undefined,\n applicationId: undefined,\n baseUrl: undefined,\n clearSession: () => {},\n clientId: undefined,\n scopes: undefined,\n discovery: {\n wellKnown: null,\n },\n exchangeToken: null as unknown as ThunderIDContextProps['exchangeToken'],\n getAccessToken: null as unknown as ThunderIDContextProps['getAccessToken'],\n getDecodedIdToken: null as unknown as ThunderIDContextProps['getDecodedIdToken'],\n getIdToken: null as unknown as ThunderIDContextProps['getIdToken'],\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: () => null as unknown as Promise<HttpResponse<any>>,\n requestAll: () => null as unknown as Promise<HttpResponse<any>[]>,\n },\n instanceId: 0,\n isInitialized: false,\n isLoading: true,\n isMetaLoading: false,\n isSignedIn: false,\n meta: null,\n organizationHandle: undefined,\n reInitialize: null as unknown as ThunderIDContextProps['reInitialize'],\n recover: () => Promise.resolve({} as any),\n resolveFlowTemplateLiterals: (text: string | undefined) => text ?? '',\n signIn: () => Promise.resolve({} as any),\n signInSilently: () => Promise.resolve({} as any),\n signInUrl: undefined,\n signOut: () => Promise.resolve({} as any),\n signUp: () => Promise.resolve({} as any),\n signUpUrl: undefined,\n user: null,\n});\n\nThunderIDContext.displayName = 'ThunderIDContext';\n\nexport default ThunderIDContext;\n"],"mappings":";;;;;;AAuNA,MAAMA,mBAA0D,cAA4C;CAC1G,gBAAgB;CAChB,eAAe;CACf,SAAS;CACT,oBAAoB;CACpB,UAAU;CACV,QAAQ;CACR,WAAW,EACT,WAAW,MACZ;CACD,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,YAAY;CACZ,yBAAyB,QAAQ,QAAQ,KAAK;CAC9C,MAAM;EACJ,eAAe;EACf,kBAAkB;EACnB;CACD,YAAY;CACZ,eAAe;CACf,WAAW;CACX,eAAe;CACf,YAAY;CACZ,MAAM;CACN,oBAAoB;CACpB,cAAc;CACd,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,8BAA8B,SAA6B,QAAQ;CACnE,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,sBAAsB,QAAQ,QAAQ,EAAE,CAAQ;CAChD,WAAW;CACX,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,WAAW;CACX,MAAM;CACP,CAAC;AAEF,iBAAiB,cAAc;AAE/B,+BAAe"}
|
|
1
|
+
{"version":3,"file":"ThunderIDContext.js","names":["ThunderIDContext: Context<ThunderIDContextProps | null>"],"sources":["../../../src/contexts/ThunderID/ThunderIDContext.ts"],"sourcesContent":["/**\n * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).\n *\n * WSO2 LLC. licenses this file to you under the Apache License,\n * Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport {\n FlowMetadataResponse,\n HttpRequestConfig,\n HttpResponse,\n IdToken,\n OIDCDiscoveryApiResponse,\n SignInOptions,\n TokenExchangeRequestConfig,\n TokenResponse,\n VendorConstants,\n} from '@thunderid/browser';\nimport {Context, createContext} from 'react';\nimport {ThunderIDReactConfig} from '../../models/config';\nimport ThunderIDReactClient from '../../ThunderIDReactClient';\n\n/**\n * Props interface of {@link ThunderIDContext}\n */\nexport type ThunderIDContextProps = {\n afterSignInUrl: string | undefined;\n applicationId: string | undefined;\n baseUrl: string | undefined;\n clientId: string | undefined;\n scopes: string | string[] | undefined;\n /**\n * OIDC discovery data.\n */\n discovery: {\n /**\n * The response from the `.well-known/openid-configuration` endpoint.\n * Contains server capabilities, supported endpoints, and metadata.\n * `null` while loading or when discovery has not been fetched.\n */\n wellKnown: OIDCDiscoveryApiResponse | null;\n };\n /**\n * Swaps the current access token with a new one based on the provided configuration (with a grant type).\n * @param config - Configuration for the token exchange request.\n * @returns A promise that resolves to the token response or the raw response.\n */\n exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;\n /**\n * Retrieves the access token stored in the storage.\n * This function retrieves the access token and returns it.\n * @remarks This does not work in the `webWorker` or any other worker environment.\n * @returns A promise that resolves to the access token.\n */\n getAccessToken: () => Promise<string>;\n /**\n * Function to retrieve the decoded ID token.\n * This function decodes the ID token and returns its payload.\n * It can be used to access user claims and other information contained in the ID token.\n *\n * @returns A promise that resolves to the decoded ID token payload.\n */\n getDecodedIdToken: () => Promise<IdToken>;\n /**\n * Function to retrieve the ID token.\n * This function retrieves the ID token and returns it.\n *\n * @returns A promise that resolves to the ID token.\n */\n getIdToken: () => Promise<string>;\n /**\n * Returns the underlying StorageManager instance for reading and writing SDK-managed storage.\n */\n getStorageManager: () => Promise<any>;\n /**\n * HTTP request function to make API calls.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n http: {\n /**\n * Makes an HTTP request using the provided configuration.\n * @param requestConfig - Configuration for the HTTP request.\n * @returns A promise that resolves to the HTTP response.\n */\n request: (requestConfig?: HttpRequestConfig) => Promise<HttpResponse<any>>;\n /**\n * Makes multiple HTTP requests based on the provided configuration.\n * @param requestConfigs - Set of configurations for the HTTP requests.\n * @returns A promise that resolves to an array of HTTP responses.\n */\n requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise<HttpResponse<any>[]>;\n };\n /**\n * Instance ID for multi-instance support.\n */\n instanceId: number;\n isInitialized: boolean;\n /**\n * Flag indicating whether the SDK is working in the background.\n */\n isLoading: boolean;\n /**\n * Flag indicating whether flow metadata is currently being fetched.\n */\n isMetaLoading: boolean;\n /**\n * Flag indicating whether the user is signed in or not.\n */\n isSignedIn: boolean;\n\n /**\n * Flow metadata returned by `GET /flow/meta` (v2 platform only).\n * `null` while loading or when `resolveFromMeta` is disabled.\n */\n meta: FlowMetadataResponse | null;\n\n organizationHandle: string | undefined;\n\n /**\n * Re-initializes the client with a new configuration.\n *\n * @remarks\n * This can be partial configuration to update only specific fields.\n *\n * @param config - New configuration to re-initialize the client with.\n * @returns Promise resolving to boolean indicating success.\n */\n reInitialize: (config: Partial<ThunderIDReactConfig>) => Promise<boolean>;\n\n /**\n * Recovery function to initiate the account/password recovery flow.\n */\n recover: (...args: any[]) => Promise<any>;\n\n /**\n * Resolves `{{ t(key) }}` and `{{ meta(path) }}` template expressions in a string,\n * using the current i18n translation function and flow metadata from context.\n *\n * Useful in render-props patterns where consumers need to expand template strings\n * that come from the server (e.g. component labels, placeholders, headings).\n *\n * @example\n * const {resolveFlowTemplateLiterals} = useThunderID();\n * resolveFlowTemplateLiterals('{{ t(signin.heading.label) }}') // → 'Sign In'\n * resolveFlowTemplateLiterals('Login to {{ meta(application.name) }}') // → 'Login to My App'\n */\n resolveFlowTemplateLiterals: (text: string | undefined) => string;\n\n /**\n * Sign-in function to initiate the authentication process.\n * @remark This is the programmatic version of the `SignInButton` component.\n * TODO: Fix the types.\n */\n signIn: (...args: any) => Promise<any>;\n\n /**\n * Optional additional parameters to be sent in the sign-in request.\n * This can include custom parameters that your authorization server supports.\n * These parameters will be included in the authorization request sent to the server.\n * If not provided, no additional parameters will be sent.\n *\n * @example\n * signInOptions: { prompt: \"login\", fidp: \"OrganizationSSO\" }\n */\n signInOptions?: SignInOptions;\n\n /**\n * Optional token request configuration. params are appended to the token endpoint POST body.\n *\n * @example\n * tokenRequest: { params: { resource: \"https://api.example.com\" } }\n */\n tokenRequest?: {\n params?: Record<string, unknown>;\n };\n\n /**\n * Silent sign-in function to re-authenticate the user without user interaction.\n * @remark This is the programmatic version of the `SilentSignIn` component.\n */\n signInSilently: ThunderIDReactClient['signInSilently'];\n\n signInUrl: string | undefined;\n /**\n * Sign-out function to terminate the authentication session.\n * @remark This is the programmatic version of the `SignOutButton` component.\n * FIXME: Fix the types.\n */\n signOut: any;\n\n /**\n * Sign-up function to initiate the registration process.\n * @remark This is the programmatic version of the `SignUpButton` component.\n */\n signUp: (...args: any[]) => Promise<any>;\n\n signUpUrl: string | undefined;\n\n user: any;\n\n /**\n * Vendor/brand namespace used to prefix storage keys, cookie names, and CSS class names.\n * Resolved from the `vendor` config option, defaulting to `'thunderid'`.\n */\n vendor: string;\n} & Pick<ThunderIDReactClient, 'clearSession'>;\n\n/**\n * Context object for managing the Authentication flow builder core context.\n */\nconst ThunderIDContext: Context<ThunderIDContextProps | null> = createContext<null | ThunderIDContextProps>({\n afterSignInUrl: undefined,\n applicationId: undefined,\n baseUrl: undefined,\n clearSession: () => {},\n clientId: undefined,\n scopes: undefined,\n discovery: {\n wellKnown: null,\n },\n exchangeToken: null as unknown as ThunderIDContextProps['exchangeToken'],\n getAccessToken: null as unknown as ThunderIDContextProps['getAccessToken'],\n getDecodedIdToken: null as unknown as ThunderIDContextProps['getDecodedIdToken'],\n getIdToken: null as unknown as ThunderIDContextProps['getIdToken'],\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: () => null as unknown as Promise<HttpResponse<any>>,\n requestAll: () => null as unknown as Promise<HttpResponse<any>[]>,\n },\n instanceId: 0,\n isInitialized: false,\n isLoading: true,\n isMetaLoading: false,\n isSignedIn: false,\n meta: null,\n organizationHandle: undefined,\n reInitialize: null as unknown as ThunderIDContextProps['reInitialize'],\n recover: () => Promise.resolve({} as any),\n resolveFlowTemplateLiterals: (text: string | undefined) => text ?? '',\n signIn: () => Promise.resolve({} as any),\n signInSilently: () => Promise.resolve({} as any),\n signInUrl: undefined,\n signOut: () => Promise.resolve({} as any),\n signUp: () => Promise.resolve({} as any),\n signUpUrl: undefined,\n user: null,\n vendor: VendorConstants.VENDOR_PREFIX,\n});\n\nThunderIDContext.displayName = 'ThunderIDContext';\n\nexport default ThunderIDContext;\n"],"mappings":";;;;;;;AA8NA,MAAMA,mBAA0D,cAA4C;CAC1G,gBAAgB;CAChB,eAAe;CACf,SAAS;CACT,oBAAoB;CACpB,UAAU;CACV,QAAQ;CACR,WAAW,EACT,WAAW,MACZ;CACD,eAAe;CACf,gBAAgB;CAChB,mBAAmB;CACnB,YAAY;CACZ,yBAAyB,QAAQ,QAAQ,KAAK;CAC9C,MAAM;EACJ,eAAe;EACf,kBAAkB;EACnB;CACD,YAAY;CACZ,eAAe;CACf,WAAW;CACX,eAAe;CACf,YAAY;CACZ,MAAM;CACN,oBAAoB;CACpB,cAAc;CACd,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,8BAA8B,SAA6B,QAAQ;CACnE,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,sBAAsB,QAAQ,QAAQ,EAAE,CAAQ;CAChD,WAAW;CACX,eAAe,QAAQ,QAAQ,EAAE,CAAQ;CACzC,cAAc,QAAQ,QAAQ,EAAE,CAAQ;CACxC,WAAW;CACX,MAAM;CACN,QAAQ,gBAAgB;CACzB,CAAC;AAEF,iBAAiB,cAAc;AAE/B,+BAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDProvider.d.ts","sourceRoot":"","sources":["../../../src/contexts/ThunderID/ThunderIDProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"ThunderIDProvider.d.ts","sourceRoot":"","sources":["../../../src/contexts/ThunderID/ThunderIDProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH,OAAO,EAAC,EAAE,EAAa,iBAAiB,EAAkE,MAAM,OAAO,CAAC;AAGxH,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAczD;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE1D,QAAA,MAAM,iBAAiB,EAAE,EAAE,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAiepE,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -7,7 +7,7 @@ import FlowMetaProvider_default from "../FlowMeta/FlowMetaProvider.js";
|
|
|
7
7
|
import I18nProvider_default from "../I18n/I18nProvider.js";
|
|
8
8
|
import ThemeProvider_default from "../Theme/ThemeProvider.js";
|
|
9
9
|
import UserProvider_default from "../User/UserProvider.js";
|
|
10
|
-
import { ThunderIDRuntimeError, createPackageComponentLogger, extractUserClaimsFromIdToken, generateFlattenedUserProfile } from "@thunderid/browser";
|
|
10
|
+
import { ThunderIDRuntimeError, createPackageComponentLogger, extractUserClaimsFromIdToken, generateFlattenedUserProfile, getVendorPrefix } from "@thunderid/browser";
|
|
11
11
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
12
12
|
import { jsx } from "react/jsx-runtime";
|
|
13
13
|
|
|
@@ -126,7 +126,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
126
126
|
const urlParams = currentUrl.searchParams;
|
|
127
127
|
const code = urlParams.get("code");
|
|
128
128
|
const executionIdFromUrl = urlParams.get("executionId");
|
|
129
|
-
const storedExecutionId = sessionStorage.getItem(
|
|
129
|
+
const storedExecutionId = sessionStorage.getItem(`${getVendorPrefix(config.vendor)}_execution_id`);
|
|
130
130
|
if (code && !executionIdFromUrl && !storedExecutionId) await signIn();
|
|
131
131
|
} catch (error) {
|
|
132
132
|
throw new ThunderIDRuntimeError(`Sign in failed: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, "thunderid-signIn-Error", "react", "An error occurred while trying to sign in.");
|
|
@@ -258,10 +258,12 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
258
258
|
signUp,
|
|
259
259
|
signUpUrl,
|
|
260
260
|
syncSession,
|
|
261
|
-
user
|
|
261
|
+
user,
|
|
262
|
+
vendor: getVendorPrefix(config.vendor)
|
|
262
263
|
}), [
|
|
263
264
|
applicationId,
|
|
264
265
|
config?.organizationHandle,
|
|
266
|
+
config.vendor,
|
|
265
267
|
config.afterSignInUrl,
|
|
266
268
|
config.scopes,
|
|
267
269
|
signInUrl,
|
|
@@ -297,6 +299,7 @@ const ThunderIDProvider = ({ afterSignInUrl, afterSignOutUrl, baseUrl: initialBa
|
|
|
297
299
|
value,
|
|
298
300
|
children: /* @__PURE__ */ jsx(I18nProvider_default, {
|
|
299
301
|
preferences: preferences?.i18n,
|
|
302
|
+
vendor: getVendorPrefix(config.vendor),
|
|
300
303
|
children: /* @__PURE__ */ jsx(FlowMetaProvider_default, {
|
|
301
304
|
enabled: preferences?.resolveFromMeta !== false,
|
|
302
305
|
children: /* @__PURE__ */ jsx(ThemeProvider_default, {
|