@thunderid/nextjs 0.3.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/client/contexts/ThunderID/ThunderIDProvider.cjs +5 -3
- package/dist/cjs/client/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/cjs/utils/SessionManager.cjs +2 -2
- package/dist/cjs/utils/SessionManager.cjs.map +1 -1
- package/dist/cjs/utils/decorateConfigWithNextEnv.cjs +3 -2
- package/dist/cjs/utils/decorateConfigWithNextEnv.cjs.map +1 -1
- package/dist/client/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
- package/dist/client/contexts/ThunderID/ThunderIDProvider.js +6 -4
- package/dist/client/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/dist/utils/SessionManager.js +2 -2
- package/dist/utils/SessionManager.js.map +1 -1
- package/dist/utils/decorateConfigWithNextEnv.d.ts.map +1 -1
- package/dist/utils/decorateConfigWithNextEnv.js +3 -2
- package/dist/utils/decorateConfigWithNextEnv.js.map +1 -1
- package/package.json +3 -3
|
@@ -16,7 +16,7 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
16
16
|
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
17
17
|
|
|
18
18
|
//#region src/client/contexts/ThunderID/ThunderIDProvider.tsx
|
|
19
|
-
const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refreshToken, signOut, signUp, handleOAuthCallback, preferences, isSignedIn, signInUrl, signUpUrl, user: _user, userProfile: _userProfile, updateProfile, applicationId, organizationHandle, scopes, initialMeta = null }) => {
|
|
19
|
+
const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refreshToken, signOut, signUp, handleOAuthCallback, preferences, isSignedIn, signInUrl, signUpUrl, user: _user, userProfile: _userProfile, updateProfile, applicationId, organizationHandle, scopes, vendor, initialMeta = null }) => {
|
|
20
20
|
const reRenderCheckRef = (0, react.useRef)(false);
|
|
21
21
|
const router = (0, next_navigation.useRouter)();
|
|
22
22
|
const searchParams = (0, next_navigation.useSearchParams)();
|
|
@@ -174,7 +174,8 @@ const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refr
|
|
|
174
174
|
signOut: handleSignOut,
|
|
175
175
|
signUp: handleSignUp,
|
|
176
176
|
signUpUrl,
|
|
177
|
-
user
|
|
177
|
+
user,
|
|
178
|
+
vendor: (0, __thunderid_node.getVendorPrefix)(vendor)
|
|
178
179
|
}), [
|
|
179
180
|
applicationId,
|
|
180
181
|
baseUrl,
|
|
@@ -186,7 +187,8 @@ const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refr
|
|
|
186
187
|
signInUrl,
|
|
187
188
|
signUpUrl,
|
|
188
189
|
user,
|
|
189
|
-
initialMeta
|
|
190
|
+
initialMeta,
|
|
191
|
+
vendor
|
|
190
192
|
]);
|
|
191
193
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ThunderIDContext.default.Provider, {
|
|
192
194
|
value: contextValue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDProvider.cjs","names":["ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>>","reRenderCheckRef: RefObject<boolean>","router: AppRouterInstance","searchParams: ReadonlyURLSearchParams","code: string | null","state: string | null","sessionState: string | null","result: {error?: string; redirectUrl?: string; success: boolean}","ThunderIDRuntimeError","result: any","contextValue: ThunderIDContextProps","reactContextValue: ReactThunderIDContextProps","ThunderIDContext","ReactThunderIDContext","I18nProvider","FlowMetaProvider","ThemeProvider","FlowProvider","UserProvider"],"sources":["../../../../../src/client/contexts/ThunderID/ThunderIDProvider.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\n'use client';\n\nimport {\n EmbeddedFlowExecuteRequestConfig,\n FlowMetadataResponse,\n generateFlattenedUserProfile,\n UpdateMeProfileConfig,\n User,\n UserProfile,\n ThunderIDRuntimeError,\n} from '@thunderid/node';\nimport {\n I18nProvider,\n FlowMetaProvider,\n FlowProvider,\n UserProvider,\n ThemeProvider,\n ThunderIDContext as ReactThunderIDContext,\n ThunderIDContextProps as ReactThunderIDContextProps,\n ThunderIDProviderProps,\n getActiveTheme,\n} from '@thunderid/react';\nimport {ReadonlyURLSearchParams} from 'next/dist/client/components/navigation.react-server';\nimport {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared-runtime';\nimport {useRouter, useSearchParams} from 'next/navigation';\nimport {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';\nimport ThunderIDContext, {ThunderIDContextProps} from './ThunderIDContext';\nimport {RefreshResult} from '../../../server/actions/refreshToken';\nimport logger from '../../../utils/logger';\n\n/**\n * Props interface of {@link ThunderIDClientProvider}\n */\nexport type ThunderIDClientProviderProps = Partial<Omit<ThunderIDProviderProps, 'baseUrl' | 'clientId'>> &\n Pick<ThunderIDProviderProps, 'baseUrl' | 'clientId'> & {\n applicationId: ThunderIDContextProps['applicationId'];\n clearSession: () => Promise<void>;\n handleOAuthCallback: (\n code: string,\n state: string,\n sessionState?: string,\n ) => Promise<{error?: string; redirectUrl?: string; success: boolean}>;\n /**\n * Flow metadata fetched server-side ahead of time, seeding `FlowMetaProvider` so it can skip\n * its own initial client-side fetch (avoiding a flash of untranslated i18n keys).\n */\n initialMeta?: FlowMetadataResponse | null;\n isSignedIn: boolean;\n organizationHandle: ThunderIDContextProps['organizationHandle'];\n refreshToken: () => Promise<RefreshResult>;\n signIn: ThunderIDContextProps['signIn'];\n signOut: ThunderIDContextProps['signOut'];\n signUp: ThunderIDContextProps['signUp'];\n updateProfile: (\n requestConfig: UpdateMeProfileConfig,\n sessionId?: string,\n ) => Promise<{data: {user: User}; error: string; success: boolean}>;\n user: User | null;\n userProfile: UserProfile;\n };\n\nconst ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>> = ({\n baseUrl,\n children,\n signIn,\n clearSession,\n refreshToken,\n signOut,\n signUp,\n handleOAuthCallback,\n preferences,\n isSignedIn,\n signInUrl,\n signUpUrl,\n user: _user,\n userProfile: _userProfile,\n updateProfile,\n applicationId,\n organizationHandle,\n scopes,\n initialMeta = null,\n}: PropsWithChildren<ThunderIDClientProviderProps>) => {\n const reRenderCheckRef: RefObject<boolean> = useRef(false);\n const router: AppRouterInstance = useRouter();\n const searchParams: ReadonlyURLSearchParams = useSearchParams();\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [user, setUser] = useState<User | null>(_user);\n const [userProfile, setUserProfile] = useState<UserProfile>(_userProfile);\n\n useEffect(() => {\n setUserProfile(_userProfile);\n }, [_userProfile]);\n\n useEffect(() => {\n setUser(_user);\n }, [_user]);\n\n // Handle OAuth callback automatically\n useEffect(() => {\n // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature.\n // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state\n // This will remount all the useEffects to ensure that there are no unexpected side effects.\n // When react remounts the signIn hook of the AuthProvider, it will cause a race condition. Hence, we have to\n // prevent the re-render of this hook as suggested in the following discussion.\n // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623\n if (reRenderCheckRef.current) {\n return;\n }\n\n reRenderCheckRef.current = true;\n\n // Don't handle callback if already signed in\n if (isSignedIn) return;\n\n (async (): Promise<void> => {\n try {\n const code: string | null = searchParams.get('code');\n const state: string | null = searchParams.get('state');\n const sessionState: string | null = searchParams.get('session_state');\n const error: string | null = searchParams.get('error');\n\n // Check for OAuth errors first\n if (error) {\n logger.error('[ThunderIDClientProvider] An error was received for the initiated sign-in request.');\n\n return;\n }\n\n // Handle OAuth callback if code and state are present\n if (code && state) {\n setIsLoading(true);\n\n const result: {error?: string; redirectUrl?: string; success: boolean} = await handleOAuthCallback(\n code,\n state,\n sessionState || undefined,\n );\n\n if (result.success) {\n // Redirect to the success URL\n if (result.redirectUrl) {\n router.push(result.redirectUrl);\n } else {\n // Refresh the page to update authentication state\n window.location.reload();\n }\n } else {\n logger.error(\n `[ThunderIDClientProvider] An error occurred while signing in: ${result.error || 'Authentication failed'}`,\n );\n }\n }\n } catch (error) {\n logger.error('[ThunderIDClientProvider] Failed to handle OAuth callback:', error);\n }\n })();\n }, []);\n\n useEffect(() => {\n // Set loading to false when server has resolved authentication state\n setIsLoading(false);\n }, [isSignedIn, user]);\n\n const handleSignIn = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signIn) {\n throw new ThunderIDRuntimeError(\n '`signIn` function is not available.',\n 'ThunderIDClientProvider-handleSignIn-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signIn(payload, request);\n\n // Redirect based flow URL is sent as `signInUrl` in the response.\n // Use window.location.href instead of router.push() — the OAuth authorization\n // endpoint is on an external server, and router.push() would send RSC fetch\n // headers that the identity provider doesn't understand, causing a CORS error.\n if (result?.data?.signInUrl) {\n window.location.href = result.data.signInUrl;\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignInUrl` in the response.\n if (result?.data?.afterSignInUrl) {\n router.push(result.data.afterSignInUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignUp = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signUp) {\n throw new ThunderIDRuntimeError(\n '`signUp` function is not available.',\n 'ThunderIDClientProvider-handleSignUp-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signUp(payload, request);\n\n // Redirect based flow URL is sent as `signUpUrl` in the response.\n if (result?.data?.signUpUrl) {\n router.push(result.data.signUpUrl);\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignUpUrl` in the response.\n if (result?.data?.afterSignUpUrl) {\n router.push(result.data.afterSignUpUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignOut = async (): Promise<any> => {\n logger.debug('[ThunderIDClientProvider][handleSignOut] `handleSignOut` called.');\n\n try {\n const result: any = await signOut();\n\n logger.debug('[ThunderIDClientProvider][handleSignOut] Sign out result:', result);\n\n if (result?.data?.afterSignOutUrl) {\n router.push(result.data.afterSignOutUrl);\n\n return {location: result.data.afterSignOutUrl, redirected: true};\n }\n\n if (result?.error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:',\n result.error,\n );\n }\n\n return result?.data ?? result;\n } catch (error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error occurred during signing the user out with a button click:',\n error,\n );\n\n return undefined;\n }\n };\n\n const contextValue: ThunderIDContextProps = useMemo(\n () => ({\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n refreshToken,\n scopes,\n signIn: handleSignIn,\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, scopes],\n );\n\n const handleProfileUpdate = (payload: User): void => {\n setUser(payload);\n setUserProfile((prev: UserProfile) => ({\n ...prev,\n flattenedProfile: generateFlattenedUserProfile(payload, prev?.schemas),\n profile: payload,\n }));\n };\n\n // Bridge into @thunderid/react's own ThunderIDContext. Internal react components rendered by\n // BaseSignIn/BaseSignUp — most notably FlowMetaProvider, which fetches `/flow/meta` and injects\n // its i18n bundle — call react's `useThunderID()` directly. Without this bridge they'd only see\n // react's context default (applicationId/baseUrl undefined, isInitialized false), so the meta\n // fetch would never fire and flow labels would render as untranslated i18n keys. Fields with no\n // nextjs equivalent (token/http helpers meant for direct browser API calls) are stubbed out,\n // since nextjs routes those operations through server actions instead.\n const unsupported = (name: string): (() => Promise<never>) => {\n return () =>\n Promise.reject(\n new ThunderIDRuntimeError(\n `\\`${name}\\` is not supported in @thunderid/nextjs.`,\n `ThunderIDClientProvider-${name}-NotSupportedError-001`,\n 'nextjs',\n ),\n );\n };\n\n const reactContextValue: ReactThunderIDContextProps = useMemo(\n () => ({\n afterSignInUrl: undefined,\n applicationId,\n baseUrl,\n clearSession,\n clientId: undefined,\n discovery: {wellKnown: null},\n exchangeToken: unsupported('exchangeToken'),\n getAccessToken: unsupported('getAccessToken'),\n getDecodedIdToken: unsupported('getDecodedIdToken'),\n getIdToken: unsupported('getIdToken'),\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: unsupported('http.request'),\n requestAll: unsupported('http.requestAll'),\n },\n instanceId: 0,\n isInitialized: !isLoading,\n isLoading,\n isMetaLoading: false,\n isSignedIn,\n meta: initialMeta ?? null,\n organizationHandle,\n reInitialize: unsupported('reInitialize'),\n recover: unsupported('recover'),\n resolveFlowTemplateLiterals: (text: string | undefined): string => text ?? '',\n scopes,\n signIn: handleSignIn,\n signInSilently: unsupported('signInSilently'),\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n scopes,\n signInUrl,\n signUpUrl,\n user,\n initialMeta,\n ],\n );\n\n return (\n <ThunderIDContext.Provider value={contextValue}>\n <ReactThunderIDContext.Provider value={reactContextValue}>\n <I18nProvider preferences={preferences?.i18n}>\n <FlowMetaProvider enabled={preferences?.resolveFromMeta !== false} initialMeta={initialMeta}>\n <ThemeProvider theme={preferences?.theme?.overrides} mode={getActiveTheme(preferences?.theme?.mode as any)}>\n <FlowProvider>\n <UserProvider profile={userProfile} onUpdateProfile={handleProfileUpdate} updateProfile={updateProfile}>\n {children}\n </UserProvider>\n </FlowProvider>\n </ThemeProvider>\n </FlowMetaProvider>\n </I18nProvider>\n </ReactThunderIDContext.Provider>\n </ThunderIDContext.Provider>\n );\n};\n\nexport default ThunderIDClientProvider;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThunderIDProvider.cjs","names":["ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>>","reRenderCheckRef: RefObject<boolean>","router: AppRouterInstance","searchParams: ReadonlyURLSearchParams","code: string | null","state: string | null","sessionState: string | null","result: {error?: string; redirectUrl?: string; success: boolean}","ThunderIDRuntimeError","result: any","contextValue: ThunderIDContextProps","reactContextValue: ReactThunderIDContextProps","ThunderIDContext","ReactThunderIDContext","I18nProvider","FlowMetaProvider","ThemeProvider","FlowProvider","UserProvider"],"sources":["../../../../../src/client/contexts/ThunderID/ThunderIDProvider.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\n'use client';\n\nimport {\n EmbeddedFlowExecuteRequestConfig,\n FlowMetadataResponse,\n generateFlattenedUserProfile,\n UpdateMeProfileConfig,\n User,\n UserProfile,\n ThunderIDRuntimeError,\n getVendorPrefix,\n} from '@thunderid/node';\nimport {\n I18nProvider,\n FlowMetaProvider,\n FlowProvider,\n UserProvider,\n ThemeProvider,\n ThunderIDContext as ReactThunderIDContext,\n ThunderIDContextProps as ReactThunderIDContextProps,\n ThunderIDProviderProps,\n getActiveTheme,\n} from '@thunderid/react';\nimport {ReadonlyURLSearchParams} from 'next/dist/client/components/navigation.react-server';\nimport {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared-runtime';\nimport {useRouter, useSearchParams} from 'next/navigation';\nimport {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';\nimport ThunderIDContext, {ThunderIDContextProps} from './ThunderIDContext';\nimport {RefreshResult} from '../../../server/actions/refreshToken';\nimport logger from '../../../utils/logger';\n\n/**\n * Props interface of {@link ThunderIDClientProvider}\n */\nexport type ThunderIDClientProviderProps = Partial<Omit<ThunderIDProviderProps, 'baseUrl' | 'clientId'>> &\n Pick<ThunderIDProviderProps, 'baseUrl' | 'clientId'> & {\n applicationId: ThunderIDContextProps['applicationId'];\n clearSession: () => Promise<void>;\n handleOAuthCallback: (\n code: string,\n state: string,\n sessionState?: string,\n ) => Promise<{error?: string; redirectUrl?: string; success: boolean}>;\n /**\n * Flow metadata fetched server-side ahead of time, seeding `FlowMetaProvider` so it can skip\n * its own initial client-side fetch (avoiding a flash of untranslated i18n keys).\n */\n initialMeta?: FlowMetadataResponse | null;\n isSignedIn: boolean;\n organizationHandle: ThunderIDContextProps['organizationHandle'];\n refreshToken: () => Promise<RefreshResult>;\n signIn: ThunderIDContextProps['signIn'];\n signOut: ThunderIDContextProps['signOut'];\n signUp: ThunderIDContextProps['signUp'];\n updateProfile: (\n requestConfig: UpdateMeProfileConfig,\n sessionId?: string,\n ) => Promise<{data: {user: User}; error: string; success: boolean}>;\n user: User | null;\n userProfile: UserProfile;\n };\n\nconst ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>> = ({\n baseUrl,\n children,\n signIn,\n clearSession,\n refreshToken,\n signOut,\n signUp,\n handleOAuthCallback,\n preferences,\n isSignedIn,\n signInUrl,\n signUpUrl,\n user: _user,\n userProfile: _userProfile,\n updateProfile,\n applicationId,\n organizationHandle,\n scopes,\n vendor,\n initialMeta = null,\n}: PropsWithChildren<ThunderIDClientProviderProps>) => {\n const reRenderCheckRef: RefObject<boolean> = useRef(false);\n const router: AppRouterInstance = useRouter();\n const searchParams: ReadonlyURLSearchParams = useSearchParams();\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [user, setUser] = useState<User | null>(_user);\n const [userProfile, setUserProfile] = useState<UserProfile>(_userProfile);\n\n useEffect(() => {\n setUserProfile(_userProfile);\n }, [_userProfile]);\n\n useEffect(() => {\n setUser(_user);\n }, [_user]);\n\n // Handle OAuth callback automatically\n useEffect(() => {\n // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature.\n // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state\n // This will remount all the useEffects to ensure that there are no unexpected side effects.\n // When react remounts the signIn hook of the AuthProvider, it will cause a race condition. Hence, we have to\n // prevent the re-render of this hook as suggested in the following discussion.\n // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623\n if (reRenderCheckRef.current) {\n return;\n }\n\n reRenderCheckRef.current = true;\n\n // Don't handle callback if already signed in\n if (isSignedIn) return;\n\n (async (): Promise<void> => {\n try {\n const code: string | null = searchParams.get('code');\n const state: string | null = searchParams.get('state');\n const sessionState: string | null = searchParams.get('session_state');\n const error: string | null = searchParams.get('error');\n\n // Check for OAuth errors first\n if (error) {\n logger.error('[ThunderIDClientProvider] An error was received for the initiated sign-in request.');\n\n return;\n }\n\n // Handle OAuth callback if code and state are present\n if (code && state) {\n setIsLoading(true);\n\n const result: {error?: string; redirectUrl?: string; success: boolean} = await handleOAuthCallback(\n code,\n state,\n sessionState || undefined,\n );\n\n if (result.success) {\n // Redirect to the success URL\n if (result.redirectUrl) {\n router.push(result.redirectUrl);\n } else {\n // Refresh the page to update authentication state\n window.location.reload();\n }\n } else {\n logger.error(\n `[ThunderIDClientProvider] An error occurred while signing in: ${result.error || 'Authentication failed'}`,\n );\n }\n }\n } catch (error) {\n logger.error('[ThunderIDClientProvider] Failed to handle OAuth callback:', error);\n }\n })();\n }, []);\n\n useEffect(() => {\n // Set loading to false when server has resolved authentication state\n setIsLoading(false);\n }, [isSignedIn, user]);\n\n const handleSignIn = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signIn) {\n throw new ThunderIDRuntimeError(\n '`signIn` function is not available.',\n 'ThunderIDClientProvider-handleSignIn-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signIn(payload, request);\n\n // Redirect based flow URL is sent as `signInUrl` in the response.\n // Use window.location.href instead of router.push() — the OAuth authorization\n // endpoint is on an external server, and router.push() would send RSC fetch\n // headers that the identity provider doesn't understand, causing a CORS error.\n if (result?.data?.signInUrl) {\n window.location.href = result.data.signInUrl;\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignInUrl` in the response.\n if (result?.data?.afterSignInUrl) {\n router.push(result.data.afterSignInUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignUp = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signUp) {\n throw new ThunderIDRuntimeError(\n '`signUp` function is not available.',\n 'ThunderIDClientProvider-handleSignUp-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signUp(payload, request);\n\n // Redirect based flow URL is sent as `signUpUrl` in the response.\n if (result?.data?.signUpUrl) {\n router.push(result.data.signUpUrl);\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignUpUrl` in the response.\n if (result?.data?.afterSignUpUrl) {\n router.push(result.data.afterSignUpUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignOut = async (): Promise<any> => {\n logger.debug('[ThunderIDClientProvider][handleSignOut] `handleSignOut` called.');\n\n try {\n const result: any = await signOut();\n\n logger.debug('[ThunderIDClientProvider][handleSignOut] Sign out result:', result);\n\n if (result?.data?.afterSignOutUrl) {\n router.push(result.data.afterSignOutUrl);\n\n return {location: result.data.afterSignOutUrl, redirected: true};\n }\n\n if (result?.error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:',\n result.error,\n );\n }\n\n return result?.data ?? result;\n } catch (error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error occurred during signing the user out with a button click:',\n error,\n );\n\n return undefined;\n }\n };\n\n const contextValue: ThunderIDContextProps = useMemo(\n () => ({\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n refreshToken,\n scopes,\n signIn: handleSignIn,\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, scopes],\n );\n\n const handleProfileUpdate = (payload: User): void => {\n setUser(payload);\n setUserProfile((prev: UserProfile) => ({\n ...prev,\n flattenedProfile: generateFlattenedUserProfile(payload, prev?.schemas),\n profile: payload,\n }));\n };\n\n // Bridge into @thunderid/react's own ThunderIDContext. Internal react components rendered by\n // BaseSignIn/BaseSignUp — most notably FlowMetaProvider, which fetches `/flow/meta` and injects\n // its i18n bundle — call react's `useThunderID()` directly. Without this bridge they'd only see\n // react's context default (applicationId/baseUrl undefined, isInitialized false), so the meta\n // fetch would never fire and flow labels would render as untranslated i18n keys. Fields with no\n // nextjs equivalent (token/http helpers meant for direct browser API calls) are stubbed out,\n // since nextjs routes those operations through server actions instead.\n const unsupported = (name: string): (() => Promise<never>) => {\n return () =>\n Promise.reject(\n new ThunderIDRuntimeError(\n `\\`${name}\\` is not supported in @thunderid/nextjs.`,\n `ThunderIDClientProvider-${name}-NotSupportedError-001`,\n 'nextjs',\n ),\n );\n };\n\n const reactContextValue: ReactThunderIDContextProps = useMemo(\n () => ({\n afterSignInUrl: undefined,\n applicationId,\n baseUrl,\n clearSession,\n clientId: undefined,\n discovery: {wellKnown: null},\n exchangeToken: unsupported('exchangeToken'),\n getAccessToken: unsupported('getAccessToken'),\n getDecodedIdToken: unsupported('getDecodedIdToken'),\n getIdToken: unsupported('getIdToken'),\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: unsupported('http.request'),\n requestAll: unsupported('http.requestAll'),\n },\n instanceId: 0,\n isInitialized: !isLoading,\n isLoading,\n isMetaLoading: false,\n isSignedIn,\n meta: initialMeta ?? null,\n organizationHandle,\n reInitialize: unsupported('reInitialize'),\n recover: unsupported('recover'),\n resolveFlowTemplateLiterals: (text: string | undefined): string => text ?? '',\n scopes,\n signIn: handleSignIn,\n signInSilently: unsupported('signInSilently'),\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n vendor: getVendorPrefix(vendor),\n }),\n [\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n scopes,\n signInUrl,\n signUpUrl,\n user,\n initialMeta,\n vendor,\n ],\n );\n\n return (\n <ThunderIDContext.Provider value={contextValue}>\n <ReactThunderIDContext.Provider value={reactContextValue}>\n <I18nProvider preferences={preferences?.i18n}>\n <FlowMetaProvider enabled={preferences?.resolveFromMeta !== false} initialMeta={initialMeta}>\n <ThemeProvider theme={preferences?.theme?.overrides} mode={getActiveTheme(preferences?.theme?.mode as any)}>\n <FlowProvider>\n <UserProvider profile={userProfile} onUpdateProfile={handleProfileUpdate} updateProfile={updateProfile}>\n {children}\n </UserProvider>\n </FlowProvider>\n </ThemeProvider>\n </FlowMetaProvider>\n </I18nProvider>\n </ReactThunderIDContext.Provider>\n </ThunderIDContext.Provider>\n );\n};\n\nexport default ThunderIDClientProvider;\n"],"mappings":"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"}
|
|
@@ -134,13 +134,13 @@ var SessionManager = class {
|
|
|
134
134
|
* Get session cookie name
|
|
135
135
|
*/
|
|
136
136
|
static getSessionCookieName() {
|
|
137
|
-
return __thunderid_node.CookieConfig.
|
|
137
|
+
return __thunderid_node.CookieConfig.getSessionCookieName();
|
|
138
138
|
}
|
|
139
139
|
/**
|
|
140
140
|
* Get temporary session cookie name
|
|
141
141
|
*/
|
|
142
142
|
static getTempSessionCookieName() {
|
|
143
|
-
return __thunderid_node.CookieConfig.
|
|
143
|
+
return __thunderid_node.CookieConfig.getTempSessionCookieName();
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
var SessionManager_default = SessionManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionManager.cjs","names":["secret: string | undefined","ThunderIDRuntimeError","secret: Uint8Array","SignJWT","envValue: string | undefined","parsed: number","DEFAULT_SESSION_COOKIE_EXPIRY_TIME","payload: SessionTokenPayload","CookieConfig"],"sources":["../../../src/utils/SessionManager.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 {ThunderIDRuntimeError, CookieConfig} from '@thunderid/node';\nimport {SignJWT, jwtVerify, compactVerify, JWTPayload} from 'jose';\nimport {DEFAULT_SESSION_COOKIE_EXPIRY_TIME} from '../constants/sessionConstants';\n\n/**\n * Session token payload interface\n */\nexport interface SessionTokenPayload extends JWTPayload {\n /** Expiration timestamp — doubles as the access token expiry (JWT exp == access token exp) */\n exp: number;\n /** Issued at timestamp */\n iat: number;\n /** Organization ID if applicable */\n organizationId?: string;\n /** The refresh token; empty string if not provided by the auth server */\n refreshToken: string;\n /** OAuth scopes */\n scopes: string[];\n /** Session ID */\n sessionId: string;\n /** User ID */\n sub: string;\n /** Token type discriminant — must be 'session' for access-session JWTs */\n type: 'session';\n}\n\n/**\n * Session management utility class for JWT-based session cookies\n */\nclass SessionManager {\n /**\n * Get the signing secret from environment variable\n * Throws error in production if not set\n */\n private static getSecret(): Uint8Array {\n const secret: string | undefined = process.env['THUNDERID_SECRET'];\n\n if (!secret) {\n if (process.env['NODE_ENV'] === 'production') {\n throw new ThunderIDRuntimeError(\n 'THUNDERID_SECRET environment variable is required in production',\n 'session-secret-required',\n 'nextjs',\n 'Set the THUNDERID_SECRET environment variable with a secure random string',\n );\n }\n // Use a default secret for development (not secure)\n // eslint-disable-next-line no-console\n console.warn('Using default secret for development. Set THUNDERID_SECRET for production!');\n return new TextEncoder().encode('development-secret-not-for-production');\n }\n\n return new TextEncoder().encode(secret);\n }\n\n /**\n * Create a temporary session cookie for login initiation\n */\n static async createTempSession(sessionId: string): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n sessionId,\n type: 'temp',\n })\n .setProtectedHeader({alg: 'HS256'})\n .setIssuedAt()\n .setExpirationTime('15m')\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Resolve the session cookie expiry time in seconds.\n *\n * Resolution order (first defined value wins):\n * 1. `configuredExpiry` — value from `ThunderIDNodeConfig.sessionCookie?.expiryTime`\n * 2. `THUNDERID_SESSION_COOKIE_EXPIRY_TIME` environment variable\n * 3. `DEFAULT_SESSION_COOKIE_EXPIRY_TIME` (24 hours)\n */\n static resolveSessionCookieExpiry(configuredExpiry?: number): number {\n if (configuredExpiry != null && configuredExpiry > 0) {\n return configuredExpiry;\n }\n\n const envValue: string | undefined = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'];\n\n if (envValue) {\n const parsed: number = parseInt(envValue, 10);\n\n if (!Number.isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n\n return DEFAULT_SESSION_COOKIE_EXPIRY_TIME;\n }\n\n static async createSessionToken(\n accessToken: string,\n userId: string,\n sessionId: string,\n scopes: string,\n accessTokenTtlSeconds: number,\n refreshToken: string,\n organizationId?: string,\n ): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n accessToken,\n organizationId,\n refreshToken,\n scopes,\n sessionId,\n type: 'session',\n } as Omit<SessionTokenPayload, 'sub' | 'iat' | 'exp'>)\n .setProtectedHeader({alg: 'HS256'})\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(Math.floor(Date.now() / 1000) + accessTokenTtlSeconds)\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Verify and decode a session token\n */\n static async verifySessionToken(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload as SessionTokenPayload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token',\n 'nextjs',\n 'Session token verification failed',\n );\n }\n }\n\n /**\n * Verify a session token for refresh. Validates the HMAC signature and the\n * `type === 'session'` discriminant but intentionally skips the `exp` check\n * so an expired access token can still be exchanged for a new one.\n *\n * Session lifetime is still bounded — the cookie's `maxAge` is set from\n * `sessionCookieExpiryTime`, so the browser drops an over-age session regardless\n * of the access-token exp embedded in the JWT.\n *\n * Never use the returned payload for authorization.\n */\n static async verifySessionTokenForRefresh(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload: rawPayload} = await compactVerify(token, secret);\n const payload: SessionTokenPayload = JSON.parse(new TextDecoder().decode(rawPayload)) as SessionTokenPayload;\n\n if (payload.type !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token-for-refresh',\n 'nextjs',\n 'Session token signature or type check failed during refresh',\n );\n }\n }\n\n /**\n * Verify and decode a temporary session token\n */\n static async verifyTempSession(token: string): Promise<{sessionId: string}> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'temp') {\n throw new Error('Invalid token type');\n }\n\n return {sessionId: payload['sessionId'] as string};\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid temporary session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-temp-session-token',\n 'nextjs',\n 'Temporary session token verification failed',\n );\n }\n }\n\n /**\n * Get session cookie options\n */\n static getSessionCookieOptions(maxAge: number): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get temporary session cookie options\n */\n static getTempSessionCookieOptions(): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge: 15 * 60,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get session cookie name\n */\n static getSessionCookieName(): string {\n return CookieConfig.SESSION_COOKIE_NAME;\n }\n\n /**\n * Get temporary session cookie name\n */\n static getTempSessionCookieName(): string {\n return CookieConfig.TEMP_SESSION_COOKIE_NAME;\n }\n}\n\nexport default SessionManager;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionManager.cjs","names":["secret: string | undefined","ThunderIDRuntimeError","secret: Uint8Array","SignJWT","envValue: string | undefined","parsed: number","DEFAULT_SESSION_COOKIE_EXPIRY_TIME","payload: SessionTokenPayload","CookieConfig"],"sources":["../../../src/utils/SessionManager.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 {ThunderIDRuntimeError, CookieConfig} from '@thunderid/node';\nimport {SignJWT, jwtVerify, compactVerify, JWTPayload} from 'jose';\nimport {DEFAULT_SESSION_COOKIE_EXPIRY_TIME} from '../constants/sessionConstants';\n\n/**\n * Session token payload interface\n */\nexport interface SessionTokenPayload extends JWTPayload {\n /** Expiration timestamp — doubles as the access token expiry (JWT exp == access token exp) */\n exp: number;\n /** Issued at timestamp */\n iat: number;\n /** Organization ID if applicable */\n organizationId?: string;\n /** The refresh token; empty string if not provided by the auth server */\n refreshToken: string;\n /** OAuth scopes */\n scopes: string[];\n /** Session ID */\n sessionId: string;\n /** User ID */\n sub: string;\n /** Token type discriminant — must be 'session' for access-session JWTs */\n type: 'session';\n}\n\n/**\n * Session management utility class for JWT-based session cookies\n */\nclass SessionManager {\n /**\n * Get the signing secret from environment variable\n * Throws error in production if not set\n */\n private static getSecret(): Uint8Array {\n const secret: string | undefined = process.env['THUNDERID_SECRET'];\n\n if (!secret) {\n if (process.env['NODE_ENV'] === 'production') {\n throw new ThunderIDRuntimeError(\n 'THUNDERID_SECRET environment variable is required in production',\n 'session-secret-required',\n 'nextjs',\n 'Set the THUNDERID_SECRET environment variable with a secure random string',\n );\n }\n // Use a default secret for development (not secure)\n // eslint-disable-next-line no-console\n console.warn('Using default secret for development. Set THUNDERID_SECRET for production!');\n return new TextEncoder().encode('development-secret-not-for-production');\n }\n\n return new TextEncoder().encode(secret);\n }\n\n /**\n * Create a temporary session cookie for login initiation\n */\n static async createTempSession(sessionId: string): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n sessionId,\n type: 'temp',\n })\n .setProtectedHeader({alg: 'HS256'})\n .setIssuedAt()\n .setExpirationTime('15m')\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Resolve the session cookie expiry time in seconds.\n *\n * Resolution order (first defined value wins):\n * 1. `configuredExpiry` — value from `ThunderIDNodeConfig.sessionCookie?.expiryTime`\n * 2. `THUNDERID_SESSION_COOKIE_EXPIRY_TIME` environment variable\n * 3. `DEFAULT_SESSION_COOKIE_EXPIRY_TIME` (24 hours)\n */\n static resolveSessionCookieExpiry(configuredExpiry?: number): number {\n if (configuredExpiry != null && configuredExpiry > 0) {\n return configuredExpiry;\n }\n\n const envValue: string | undefined = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'];\n\n if (envValue) {\n const parsed: number = parseInt(envValue, 10);\n\n if (!Number.isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n\n return DEFAULT_SESSION_COOKIE_EXPIRY_TIME;\n }\n\n static async createSessionToken(\n accessToken: string,\n userId: string,\n sessionId: string,\n scopes: string,\n accessTokenTtlSeconds: number,\n refreshToken: string,\n organizationId?: string,\n ): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n accessToken,\n organizationId,\n refreshToken,\n scopes,\n sessionId,\n type: 'session',\n } as Omit<SessionTokenPayload, 'sub' | 'iat' | 'exp'>)\n .setProtectedHeader({alg: 'HS256'})\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(Math.floor(Date.now() / 1000) + accessTokenTtlSeconds)\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Verify and decode a session token\n */\n static async verifySessionToken(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload as SessionTokenPayload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token',\n 'nextjs',\n 'Session token verification failed',\n );\n }\n }\n\n /**\n * Verify a session token for refresh. Validates the HMAC signature and the\n * `type === 'session'` discriminant but intentionally skips the `exp` check\n * so an expired access token can still be exchanged for a new one.\n *\n * Session lifetime is still bounded — the cookie's `maxAge` is set from\n * `sessionCookieExpiryTime`, so the browser drops an over-age session regardless\n * of the access-token exp embedded in the JWT.\n *\n * Never use the returned payload for authorization.\n */\n static async verifySessionTokenForRefresh(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload: rawPayload} = await compactVerify(token, secret);\n const payload: SessionTokenPayload = JSON.parse(new TextDecoder().decode(rawPayload)) as SessionTokenPayload;\n\n if (payload.type !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token-for-refresh',\n 'nextjs',\n 'Session token signature or type check failed during refresh',\n );\n }\n }\n\n /**\n * Verify and decode a temporary session token\n */\n static async verifyTempSession(token: string): Promise<{sessionId: string}> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'temp') {\n throw new Error('Invalid token type');\n }\n\n return {sessionId: payload['sessionId'] as string};\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid temporary session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-temp-session-token',\n 'nextjs',\n 'Temporary session token verification failed',\n );\n }\n }\n\n /**\n * Get session cookie options\n */\n static getSessionCookieOptions(maxAge: number): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get temporary session cookie options\n */\n static getTempSessionCookieOptions(): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge: 15 * 60,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get session cookie name\n */\n static getSessionCookieName(): string {\n return CookieConfig.getSessionCookieName();\n }\n\n /**\n * Get temporary session cookie name\n */\n static getTempSessionCookieName(): string {\n return CookieConfig.getTempSessionCookieName();\n }\n}\n\nexport default SessionManager;\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
//#region src/utils/decorateConfigWithNextEnv.ts
|
|
3
3
|
const decorateConfigWithNextEnv = (config) => {
|
|
4
|
-
const { organizationHandle, scopes, applicationId, baseUrl, clientId, clientSecret, signInUrl, signUpUrl, afterSignInUrl, afterSignOutUrl,...rest } = config;
|
|
4
|
+
const { organizationHandle, scopes, applicationId, baseUrl, clientId, clientSecret, signInUrl, signUpUrl, afterSignInUrl, afterSignOutUrl, vendor,...rest } = config;
|
|
5
5
|
const envExpiryTime = process.env["THUNDERID_SESSION_COOKIE_EXPIRY_TIME"] ? parseInt(process.env["THUNDERID_SESSION_COOKIE_EXPIRY_TIME"], 10) : void 0;
|
|
6
6
|
return {
|
|
7
7
|
...rest,
|
|
@@ -18,7 +18,8 @@ const decorateConfigWithNextEnv = (config) => {
|
|
|
18
18
|
expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime
|
|
19
19
|
},
|
|
20
20
|
signInUrl: signInUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_IN_URL"],
|
|
21
|
-
signUpUrl: signUpUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_UP_URL"]
|
|
21
|
+
signUpUrl: signUpUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_UP_URL"],
|
|
22
|
+
vendor: vendor || process.env["NEXT_PUBLIC_THUNDERID_VENDOR"]
|
|
22
23
|
};
|
|
23
24
|
};
|
|
24
25
|
var decorateConfigWithNextEnv_default = decorateConfigWithNextEnv;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorateConfigWithNextEnv.cjs","names":[],"sources":["../../../src/utils/decorateConfigWithNextEnv.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 {ThunderIDNextConfig} from '../models/config';\n\nconst decorateConfigWithNextEnv = (config: ThunderIDNextConfig): ThunderIDNextConfig => {\n const {\n organizationHandle,\n scopes,\n applicationId,\n baseUrl,\n clientId,\n clientSecret,\n signInUrl,\n signUpUrl,\n afterSignInUrl,\n afterSignOutUrl,\n ...rest\n } = config;\n\n const envExpiryTime = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME']\n ? parseInt(process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'], 10)\n : undefined;\n\n return {\n ...rest,\n afterSignInUrl: afterSignInUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL']!,\n afterSignOutUrl: afterSignOutUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL']!,\n applicationId: applicationId || process.env['NEXT_PUBLIC_THUNDERID_APPLICATION_ID']!,\n baseUrl: baseUrl || process.env['NEXT_PUBLIC_THUNDERID_BASE_URL']!,\n clientId: clientId || process.env['NEXT_PUBLIC_THUNDERID_CLIENT_ID']!,\n clientSecret: clientSecret || process.env['THUNDERID_CLIENT_SECRET']!,\n organizationHandle: organizationHandle || process.env['NEXT_PUBLIC_THUNDERID_ORGANIZATION_HANDLE']!,\n scopes: scopes || process.env['NEXT_PUBLIC_THUNDERID_SCOPES']!,\n sessionCookie: {\n ...rest.sessionCookie,\n expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime,\n },\n signInUrl: signInUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_IN_URL']!,\n signUpUrl: signUpUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_UP_URL']!,\n };\n};\n\nexport default decorateConfigWithNextEnv;\n"],"mappings":";;AAoBA,MAAM,6BAA6B,WAAqD;CACtF,MAAM,EACJ,oBACA,QACA,eACA,SACA,UACA,cACA,WACA,WACA,gBACA,
|
|
1
|
+
{"version":3,"file":"decorateConfigWithNextEnv.cjs","names":[],"sources":["../../../src/utils/decorateConfigWithNextEnv.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 {ThunderIDNextConfig} from '../models/config';\n\nconst decorateConfigWithNextEnv = (config: ThunderIDNextConfig): ThunderIDNextConfig => {\n const {\n organizationHandle,\n scopes,\n applicationId,\n baseUrl,\n clientId,\n clientSecret,\n signInUrl,\n signUpUrl,\n afterSignInUrl,\n afterSignOutUrl,\n vendor,\n ...rest\n } = config;\n\n const envExpiryTime = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME']\n ? parseInt(process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'], 10)\n : undefined;\n\n return {\n ...rest,\n afterSignInUrl: afterSignInUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL']!,\n afterSignOutUrl: afterSignOutUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL']!,\n applicationId: applicationId || process.env['NEXT_PUBLIC_THUNDERID_APPLICATION_ID']!,\n baseUrl: baseUrl || process.env['NEXT_PUBLIC_THUNDERID_BASE_URL']!,\n clientId: clientId || process.env['NEXT_PUBLIC_THUNDERID_CLIENT_ID']!,\n clientSecret: clientSecret || process.env['THUNDERID_CLIENT_SECRET']!,\n organizationHandle: organizationHandle || process.env['NEXT_PUBLIC_THUNDERID_ORGANIZATION_HANDLE']!,\n scopes: scopes || process.env['NEXT_PUBLIC_THUNDERID_SCOPES']!,\n sessionCookie: {\n ...rest.sessionCookie,\n expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime,\n },\n signInUrl: signInUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_IN_URL']!,\n signUpUrl: signUpUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_UP_URL']!,\n vendor: vendor || process.env['NEXT_PUBLIC_THUNDERID_VENDOR'],\n };\n};\n\nexport default decorateConfigWithNextEnv;\n"],"mappings":";;AAoBA,MAAM,6BAA6B,WAAqD;CACtF,MAAM,EACJ,oBACA,QACA,eACA,SACA,UACA,cACA,WACA,WACA,gBACA,iBACA,OACA,GAAG,SACD;CAEJ,MAAM,gBAAgB,QAAQ,IAAI,0CAC9B,SAAS,QAAQ,IAAI,yCAAyC,GAAG,GACjE;AAEJ,QAAO;EACL,GAAG;EACH,gBAAgB,kBAAkB,QAAQ,IAAI;EAC9C,iBAAiB,mBAAmB,QAAQ,IAAI;EAChD,eAAe,iBAAiB,QAAQ,IAAI;EAC5C,SAAS,WAAW,QAAQ,IAAI;EAChC,UAAU,YAAY,QAAQ,IAAI;EAClC,cAAc,gBAAgB,QAAQ,IAAI;EAC1C,oBAAoB,sBAAsB,QAAQ,IAAI;EACtD,QAAQ,UAAU,QAAQ,IAAI;EAC9B,eAAe;GACb,GAAG,KAAK;GACR,YAAY,KAAK,eAAe,cAAc;GAC/C;EACD,WAAW,aAAa,QAAQ,IAAI;EACpC,WAAW,aAAa,QAAQ,IAAI;EACpC,QAAQ,UAAU,QAAQ,IAAI;EAC/B;;AAGH,wCAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDProvider.d.ts","sourceRoot":"","sources":["../../../../src/client/contexts/ThunderID/ThunderIDProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAEL,oBAAoB,EAEpB,qBAAqB,EACrB,IAAI,EACJ,WAAW,
|
|
1
|
+
{"version":3,"file":"ThunderIDProvider.d.ts","sourceRoot":"","sources":["../../../../src/client/contexts/ThunderID/ThunderIDProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAEL,oBAAoB,EAEpB,qBAAqB,EACrB,IAAI,EACJ,WAAW,EAGZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAQL,sBAAsB,EAEvB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAC,EAAE,EAAE,iBAAiB,EAAkD,MAAM,OAAO,CAAC;AAC7F,OAAyB,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAC,aAAa,EAAC,MAAM,sCAAsC,CAAC;AAGnE;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC,GACtG,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,UAAU,CAAC,GAAG;IACrD,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAC;IACtD,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,mBAAmB,EAAE,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,KAClB,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC;IACvE;;;OAGG;IACH,WAAW,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAC;IAChE,YAAY,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACxC,aAAa,EAAE,CACb,aAAa,EAAE,qBAAqB,EACpC,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC;QAAC,IAAI,EAAE;YAAC,IAAI,EAAE,IAAI,CAAA;SAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC;IACpE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC;AAEJ,QAAA,MAAM,uBAAuB,EAAE,EAAE,CAAC,iBAAiB,CAAC,4BAA4B,CAAC,CA+ThF,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
import logger_default from "../../../utils/logger.js";
|
|
5
5
|
import ThunderIDContext_default from "./ThunderIDContext.js";
|
|
6
|
-
import { ThunderIDRuntimeError, generateFlattenedUserProfile } from "@thunderid/node";
|
|
6
|
+
import { ThunderIDRuntimeError, generateFlattenedUserProfile, getVendorPrefix } from "@thunderid/node";
|
|
7
7
|
import { FlowMetaProvider, FlowProvider, I18nProvider, ThemeProvider, ThunderIDContext, UserProvider, getActiveTheme } from "@thunderid/react";
|
|
8
8
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
9
9
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
10
10
|
import { jsx } from "react/jsx-runtime";
|
|
11
11
|
|
|
12
12
|
//#region src/client/contexts/ThunderID/ThunderIDProvider.tsx
|
|
13
|
-
const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refreshToken, signOut, signUp, handleOAuthCallback, preferences, isSignedIn, signInUrl, signUpUrl, user: _user, userProfile: _userProfile, updateProfile, applicationId, organizationHandle, scopes, initialMeta = null }) => {
|
|
13
|
+
const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refreshToken, signOut, signUp, handleOAuthCallback, preferences, isSignedIn, signInUrl, signUpUrl, user: _user, userProfile: _userProfile, updateProfile, applicationId, organizationHandle, scopes, vendor, initialMeta = null }) => {
|
|
14
14
|
const reRenderCheckRef = useRef(false);
|
|
15
15
|
const router = useRouter();
|
|
16
16
|
const searchParams = useSearchParams();
|
|
@@ -168,7 +168,8 @@ const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refr
|
|
|
168
168
|
signOut: handleSignOut,
|
|
169
169
|
signUp: handleSignUp,
|
|
170
170
|
signUpUrl,
|
|
171
|
-
user
|
|
171
|
+
user,
|
|
172
|
+
vendor: getVendorPrefix(vendor)
|
|
172
173
|
}), [
|
|
173
174
|
applicationId,
|
|
174
175
|
baseUrl,
|
|
@@ -180,7 +181,8 @@ const ThunderIDClientProvider = ({ baseUrl, children, signIn, clearSession, refr
|
|
|
180
181
|
signInUrl,
|
|
181
182
|
signUpUrl,
|
|
182
183
|
user,
|
|
183
|
-
initialMeta
|
|
184
|
+
initialMeta,
|
|
185
|
+
vendor
|
|
184
186
|
]);
|
|
185
187
|
return /* @__PURE__ */ jsx(ThunderIDContext_default.Provider, {
|
|
186
188
|
value: contextValue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThunderIDProvider.js","names":["ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>>","reRenderCheckRef: RefObject<boolean>","router: AppRouterInstance","searchParams: ReadonlyURLSearchParams","code: string | null","state: string | null","sessionState: string | null","result: {error?: string; redirectUrl?: string; success: boolean}","result: any","contextValue: ThunderIDContextProps","reactContextValue: ReactThunderIDContextProps","ThunderIDContext","ReactThunderIDContext"],"sources":["../../../../src/client/contexts/ThunderID/ThunderIDProvider.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\n'use client';\n\nimport {\n EmbeddedFlowExecuteRequestConfig,\n FlowMetadataResponse,\n generateFlattenedUserProfile,\n UpdateMeProfileConfig,\n User,\n UserProfile,\n ThunderIDRuntimeError,\n} from '@thunderid/node';\nimport {\n I18nProvider,\n FlowMetaProvider,\n FlowProvider,\n UserProvider,\n ThemeProvider,\n ThunderIDContext as ReactThunderIDContext,\n ThunderIDContextProps as ReactThunderIDContextProps,\n ThunderIDProviderProps,\n getActiveTheme,\n} from '@thunderid/react';\nimport {ReadonlyURLSearchParams} from 'next/dist/client/components/navigation.react-server';\nimport {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared-runtime';\nimport {useRouter, useSearchParams} from 'next/navigation';\nimport {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';\nimport ThunderIDContext, {ThunderIDContextProps} from './ThunderIDContext';\nimport {RefreshResult} from '../../../server/actions/refreshToken';\nimport logger from '../../../utils/logger';\n\n/**\n * Props interface of {@link ThunderIDClientProvider}\n */\nexport type ThunderIDClientProviderProps = Partial<Omit<ThunderIDProviderProps, 'baseUrl' | 'clientId'>> &\n Pick<ThunderIDProviderProps, 'baseUrl' | 'clientId'> & {\n applicationId: ThunderIDContextProps['applicationId'];\n clearSession: () => Promise<void>;\n handleOAuthCallback: (\n code: string,\n state: string,\n sessionState?: string,\n ) => Promise<{error?: string; redirectUrl?: string; success: boolean}>;\n /**\n * Flow metadata fetched server-side ahead of time, seeding `FlowMetaProvider` so it can skip\n * its own initial client-side fetch (avoiding a flash of untranslated i18n keys).\n */\n initialMeta?: FlowMetadataResponse | null;\n isSignedIn: boolean;\n organizationHandle: ThunderIDContextProps['organizationHandle'];\n refreshToken: () => Promise<RefreshResult>;\n signIn: ThunderIDContextProps['signIn'];\n signOut: ThunderIDContextProps['signOut'];\n signUp: ThunderIDContextProps['signUp'];\n updateProfile: (\n requestConfig: UpdateMeProfileConfig,\n sessionId?: string,\n ) => Promise<{data: {user: User}; error: string; success: boolean}>;\n user: User | null;\n userProfile: UserProfile;\n };\n\nconst ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>> = ({\n baseUrl,\n children,\n signIn,\n clearSession,\n refreshToken,\n signOut,\n signUp,\n handleOAuthCallback,\n preferences,\n isSignedIn,\n signInUrl,\n signUpUrl,\n user: _user,\n userProfile: _userProfile,\n updateProfile,\n applicationId,\n organizationHandle,\n scopes,\n initialMeta = null,\n}: PropsWithChildren<ThunderIDClientProviderProps>) => {\n const reRenderCheckRef: RefObject<boolean> = useRef(false);\n const router: AppRouterInstance = useRouter();\n const searchParams: ReadonlyURLSearchParams = useSearchParams();\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [user, setUser] = useState<User | null>(_user);\n const [userProfile, setUserProfile] = useState<UserProfile>(_userProfile);\n\n useEffect(() => {\n setUserProfile(_userProfile);\n }, [_userProfile]);\n\n useEffect(() => {\n setUser(_user);\n }, [_user]);\n\n // Handle OAuth callback automatically\n useEffect(() => {\n // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature.\n // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state\n // This will remount all the useEffects to ensure that there are no unexpected side effects.\n // When react remounts the signIn hook of the AuthProvider, it will cause a race condition. Hence, we have to\n // prevent the re-render of this hook as suggested in the following discussion.\n // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623\n if (reRenderCheckRef.current) {\n return;\n }\n\n reRenderCheckRef.current = true;\n\n // Don't handle callback if already signed in\n if (isSignedIn) return;\n\n (async (): Promise<void> => {\n try {\n const code: string | null = searchParams.get('code');\n const state: string | null = searchParams.get('state');\n const sessionState: string | null = searchParams.get('session_state');\n const error: string | null = searchParams.get('error');\n\n // Check for OAuth errors first\n if (error) {\n logger.error('[ThunderIDClientProvider] An error was received for the initiated sign-in request.');\n\n return;\n }\n\n // Handle OAuth callback if code and state are present\n if (code && state) {\n setIsLoading(true);\n\n const result: {error?: string; redirectUrl?: string; success: boolean} = await handleOAuthCallback(\n code,\n state,\n sessionState || undefined,\n );\n\n if (result.success) {\n // Redirect to the success URL\n if (result.redirectUrl) {\n router.push(result.redirectUrl);\n } else {\n // Refresh the page to update authentication state\n window.location.reload();\n }\n } else {\n logger.error(\n `[ThunderIDClientProvider] An error occurred while signing in: ${result.error || 'Authentication failed'}`,\n );\n }\n }\n } catch (error) {\n logger.error('[ThunderIDClientProvider] Failed to handle OAuth callback:', error);\n }\n })();\n }, []);\n\n useEffect(() => {\n // Set loading to false when server has resolved authentication state\n setIsLoading(false);\n }, [isSignedIn, user]);\n\n const handleSignIn = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signIn) {\n throw new ThunderIDRuntimeError(\n '`signIn` function is not available.',\n 'ThunderIDClientProvider-handleSignIn-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signIn(payload, request);\n\n // Redirect based flow URL is sent as `signInUrl` in the response.\n // Use window.location.href instead of router.push() — the OAuth authorization\n // endpoint is on an external server, and router.push() would send RSC fetch\n // headers that the identity provider doesn't understand, causing a CORS error.\n if (result?.data?.signInUrl) {\n window.location.href = result.data.signInUrl;\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignInUrl` in the response.\n if (result?.data?.afterSignInUrl) {\n router.push(result.data.afterSignInUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignUp = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signUp) {\n throw new ThunderIDRuntimeError(\n '`signUp` function is not available.',\n 'ThunderIDClientProvider-handleSignUp-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signUp(payload, request);\n\n // Redirect based flow URL is sent as `signUpUrl` in the response.\n if (result?.data?.signUpUrl) {\n router.push(result.data.signUpUrl);\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignUpUrl` in the response.\n if (result?.data?.afterSignUpUrl) {\n router.push(result.data.afterSignUpUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignOut = async (): Promise<any> => {\n logger.debug('[ThunderIDClientProvider][handleSignOut] `handleSignOut` called.');\n\n try {\n const result: any = await signOut();\n\n logger.debug('[ThunderIDClientProvider][handleSignOut] Sign out result:', result);\n\n if (result?.data?.afterSignOutUrl) {\n router.push(result.data.afterSignOutUrl);\n\n return {location: result.data.afterSignOutUrl, redirected: true};\n }\n\n if (result?.error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:',\n result.error,\n );\n }\n\n return result?.data ?? result;\n } catch (error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error occurred during signing the user out with a button click:',\n error,\n );\n\n return undefined;\n }\n };\n\n const contextValue: ThunderIDContextProps = useMemo(\n () => ({\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n refreshToken,\n scopes,\n signIn: handleSignIn,\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, scopes],\n );\n\n const handleProfileUpdate = (payload: User): void => {\n setUser(payload);\n setUserProfile((prev: UserProfile) => ({\n ...prev,\n flattenedProfile: generateFlattenedUserProfile(payload, prev?.schemas),\n profile: payload,\n }));\n };\n\n // Bridge into @thunderid/react's own ThunderIDContext. Internal react components rendered by\n // BaseSignIn/BaseSignUp — most notably FlowMetaProvider, which fetches `/flow/meta` and injects\n // its i18n bundle — call react's `useThunderID()` directly. Without this bridge they'd only see\n // react's context default (applicationId/baseUrl undefined, isInitialized false), so the meta\n // fetch would never fire and flow labels would render as untranslated i18n keys. Fields with no\n // nextjs equivalent (token/http helpers meant for direct browser API calls) are stubbed out,\n // since nextjs routes those operations through server actions instead.\n const unsupported = (name: string): (() => Promise<never>) => {\n return () =>\n Promise.reject(\n new ThunderIDRuntimeError(\n `\\`${name}\\` is not supported in @thunderid/nextjs.`,\n `ThunderIDClientProvider-${name}-NotSupportedError-001`,\n 'nextjs',\n ),\n );\n };\n\n const reactContextValue: ReactThunderIDContextProps = useMemo(\n () => ({\n afterSignInUrl: undefined,\n applicationId,\n baseUrl,\n clearSession,\n clientId: undefined,\n discovery: {wellKnown: null},\n exchangeToken: unsupported('exchangeToken'),\n getAccessToken: unsupported('getAccessToken'),\n getDecodedIdToken: unsupported('getDecodedIdToken'),\n getIdToken: unsupported('getIdToken'),\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: unsupported('http.request'),\n requestAll: unsupported('http.requestAll'),\n },\n instanceId: 0,\n isInitialized: !isLoading,\n isLoading,\n isMetaLoading: false,\n isSignedIn,\n meta: initialMeta ?? null,\n organizationHandle,\n reInitialize: unsupported('reInitialize'),\n recover: unsupported('recover'),\n resolveFlowTemplateLiterals: (text: string | undefined): string => text ?? '',\n scopes,\n signIn: handleSignIn,\n signInSilently: unsupported('signInSilently'),\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n scopes,\n signInUrl,\n signUpUrl,\n user,\n initialMeta,\n ],\n );\n\n return (\n <ThunderIDContext.Provider value={contextValue}>\n <ReactThunderIDContext.Provider value={reactContextValue}>\n <I18nProvider preferences={preferences?.i18n}>\n <FlowMetaProvider enabled={preferences?.resolveFromMeta !== false} initialMeta={initialMeta}>\n <ThemeProvider theme={preferences?.theme?.overrides} mode={getActiveTheme(preferences?.theme?.mode as any)}>\n <FlowProvider>\n <UserProvider profile={userProfile} onUpdateProfile={handleProfileUpdate} updateProfile={updateProfile}>\n {children}\n </UserProvider>\n </FlowProvider>\n </ThemeProvider>\n </FlowMetaProvider>\n </I18nProvider>\n </ReactThunderIDContext.Provider>\n </ThunderIDContext.Provider>\n );\n};\n\nexport default ThunderIDClientProvider;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThunderIDProvider.js","names":["ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>>","reRenderCheckRef: RefObject<boolean>","router: AppRouterInstance","searchParams: ReadonlyURLSearchParams","code: string | null","state: string | null","sessionState: string | null","result: {error?: string; redirectUrl?: string; success: boolean}","result: any","contextValue: ThunderIDContextProps","reactContextValue: ReactThunderIDContextProps","ThunderIDContext","ReactThunderIDContext"],"sources":["../../../../src/client/contexts/ThunderID/ThunderIDProvider.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\n'use client';\n\nimport {\n EmbeddedFlowExecuteRequestConfig,\n FlowMetadataResponse,\n generateFlattenedUserProfile,\n UpdateMeProfileConfig,\n User,\n UserProfile,\n ThunderIDRuntimeError,\n getVendorPrefix,\n} from '@thunderid/node';\nimport {\n I18nProvider,\n FlowMetaProvider,\n FlowProvider,\n UserProvider,\n ThemeProvider,\n ThunderIDContext as ReactThunderIDContext,\n ThunderIDContextProps as ReactThunderIDContextProps,\n ThunderIDProviderProps,\n getActiveTheme,\n} from '@thunderid/react';\nimport {ReadonlyURLSearchParams} from 'next/dist/client/components/navigation.react-server';\nimport {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared-runtime';\nimport {useRouter, useSearchParams} from 'next/navigation';\nimport {FC, PropsWithChildren, RefObject, useEffect, useMemo, useRef, useState} from 'react';\nimport ThunderIDContext, {ThunderIDContextProps} from './ThunderIDContext';\nimport {RefreshResult} from '../../../server/actions/refreshToken';\nimport logger from '../../../utils/logger';\n\n/**\n * Props interface of {@link ThunderIDClientProvider}\n */\nexport type ThunderIDClientProviderProps = Partial<Omit<ThunderIDProviderProps, 'baseUrl' | 'clientId'>> &\n Pick<ThunderIDProviderProps, 'baseUrl' | 'clientId'> & {\n applicationId: ThunderIDContextProps['applicationId'];\n clearSession: () => Promise<void>;\n handleOAuthCallback: (\n code: string,\n state: string,\n sessionState?: string,\n ) => Promise<{error?: string; redirectUrl?: string; success: boolean}>;\n /**\n * Flow metadata fetched server-side ahead of time, seeding `FlowMetaProvider` so it can skip\n * its own initial client-side fetch (avoiding a flash of untranslated i18n keys).\n */\n initialMeta?: FlowMetadataResponse | null;\n isSignedIn: boolean;\n organizationHandle: ThunderIDContextProps['organizationHandle'];\n refreshToken: () => Promise<RefreshResult>;\n signIn: ThunderIDContextProps['signIn'];\n signOut: ThunderIDContextProps['signOut'];\n signUp: ThunderIDContextProps['signUp'];\n updateProfile: (\n requestConfig: UpdateMeProfileConfig,\n sessionId?: string,\n ) => Promise<{data: {user: User}; error: string; success: boolean}>;\n user: User | null;\n userProfile: UserProfile;\n };\n\nconst ThunderIDClientProvider: FC<PropsWithChildren<ThunderIDClientProviderProps>> = ({\n baseUrl,\n children,\n signIn,\n clearSession,\n refreshToken,\n signOut,\n signUp,\n handleOAuthCallback,\n preferences,\n isSignedIn,\n signInUrl,\n signUpUrl,\n user: _user,\n userProfile: _userProfile,\n updateProfile,\n applicationId,\n organizationHandle,\n scopes,\n vendor,\n initialMeta = null,\n}: PropsWithChildren<ThunderIDClientProviderProps>) => {\n const reRenderCheckRef: RefObject<boolean> = useRef(false);\n const router: AppRouterInstance = useRouter();\n const searchParams: ReadonlyURLSearchParams = useSearchParams();\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [user, setUser] = useState<User | null>(_user);\n const [userProfile, setUserProfile] = useState<UserProfile>(_userProfile);\n\n useEffect(() => {\n setUserProfile(_userProfile);\n }, [_userProfile]);\n\n useEffect(() => {\n setUser(_user);\n }, [_user]);\n\n // Handle OAuth callback automatically\n useEffect(() => {\n // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature.\n // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state\n // This will remount all the useEffects to ensure that there are no unexpected side effects.\n // When react remounts the signIn hook of the AuthProvider, it will cause a race condition. Hence, we have to\n // prevent the re-render of this hook as suggested in the following discussion.\n // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623\n if (reRenderCheckRef.current) {\n return;\n }\n\n reRenderCheckRef.current = true;\n\n // Don't handle callback if already signed in\n if (isSignedIn) return;\n\n (async (): Promise<void> => {\n try {\n const code: string | null = searchParams.get('code');\n const state: string | null = searchParams.get('state');\n const sessionState: string | null = searchParams.get('session_state');\n const error: string | null = searchParams.get('error');\n\n // Check for OAuth errors first\n if (error) {\n logger.error('[ThunderIDClientProvider] An error was received for the initiated sign-in request.');\n\n return;\n }\n\n // Handle OAuth callback if code and state are present\n if (code && state) {\n setIsLoading(true);\n\n const result: {error?: string; redirectUrl?: string; success: boolean} = await handleOAuthCallback(\n code,\n state,\n sessionState || undefined,\n );\n\n if (result.success) {\n // Redirect to the success URL\n if (result.redirectUrl) {\n router.push(result.redirectUrl);\n } else {\n // Refresh the page to update authentication state\n window.location.reload();\n }\n } else {\n logger.error(\n `[ThunderIDClientProvider] An error occurred while signing in: ${result.error || 'Authentication failed'}`,\n );\n }\n }\n } catch (error) {\n logger.error('[ThunderIDClientProvider] Failed to handle OAuth callback:', error);\n }\n })();\n }, []);\n\n useEffect(() => {\n // Set loading to false when server has resolved authentication state\n setIsLoading(false);\n }, [isSignedIn, user]);\n\n const handleSignIn = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signIn) {\n throw new ThunderIDRuntimeError(\n '`signIn` function is not available.',\n 'ThunderIDClientProvider-handleSignIn-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signIn(payload, request);\n\n // Redirect based flow URL is sent as `signInUrl` in the response.\n // Use window.location.href instead of router.push() — the OAuth authorization\n // endpoint is on an external server, and router.push() would send RSC fetch\n // headers that the identity provider doesn't understand, causing a CORS error.\n if (result?.data?.signInUrl) {\n window.location.href = result.data.signInUrl;\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignInUrl` in the response.\n if (result?.data?.afterSignInUrl) {\n router.push(result.data.afterSignInUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignUp = async (payload: any, request: EmbeddedFlowExecuteRequestConfig): Promise<any> => {\n if (!signUp) {\n throw new ThunderIDRuntimeError(\n '`signUp` function is not available.',\n 'ThunderIDClientProvider-handleSignUp-RuntimeError-001',\n 'nextjs',\n );\n }\n\n const result: any = await signUp(payload, request);\n\n // Redirect based flow URL is sent as `signUpUrl` in the response.\n if (result?.data?.signUpUrl) {\n router.push(result.data.signUpUrl);\n\n return undefined;\n }\n\n // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignUpUrl` in the response.\n if (result?.data?.afterSignUpUrl) {\n router.push(result.data.afterSignUpUrl);\n\n return undefined;\n }\n\n if (result?.error) {\n throw new Error(result.error);\n }\n\n return result?.data ?? result;\n };\n\n const handleSignOut = async (): Promise<any> => {\n logger.debug('[ThunderIDClientProvider][handleSignOut] `handleSignOut` called.');\n\n try {\n const result: any = await signOut();\n\n logger.debug('[ThunderIDClientProvider][handleSignOut] Sign out result:', result);\n\n if (result?.data?.afterSignOutUrl) {\n router.push(result.data.afterSignOutUrl);\n\n return {location: result.data.afterSignOutUrl, redirected: true};\n }\n\n if (result?.error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:',\n result.error,\n );\n }\n\n return result?.data ?? result;\n } catch (error) {\n logger.error(\n '[ThunderIDClientProvider][handleSignOut] Error occurred during signing the user out with a button click:',\n error,\n );\n\n return undefined;\n }\n };\n\n const contextValue: ThunderIDContextProps = useMemo(\n () => ({\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n refreshToken,\n scopes,\n signIn: handleSignIn,\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n }),\n [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle, scopes],\n );\n\n const handleProfileUpdate = (payload: User): void => {\n setUser(payload);\n setUserProfile((prev: UserProfile) => ({\n ...prev,\n flattenedProfile: generateFlattenedUserProfile(payload, prev?.schemas),\n profile: payload,\n }));\n };\n\n // Bridge into @thunderid/react's own ThunderIDContext. Internal react components rendered by\n // BaseSignIn/BaseSignUp — most notably FlowMetaProvider, which fetches `/flow/meta` and injects\n // its i18n bundle — call react's `useThunderID()` directly. Without this bridge they'd only see\n // react's context default (applicationId/baseUrl undefined, isInitialized false), so the meta\n // fetch would never fire and flow labels would render as untranslated i18n keys. Fields with no\n // nextjs equivalent (token/http helpers meant for direct browser API calls) are stubbed out,\n // since nextjs routes those operations through server actions instead.\n const unsupported = (name: string): (() => Promise<never>) => {\n return () =>\n Promise.reject(\n new ThunderIDRuntimeError(\n `\\`${name}\\` is not supported in @thunderid/nextjs.`,\n `ThunderIDClientProvider-${name}-NotSupportedError-001`,\n 'nextjs',\n ),\n );\n };\n\n const reactContextValue: ReactThunderIDContextProps = useMemo(\n () => ({\n afterSignInUrl: undefined,\n applicationId,\n baseUrl,\n clearSession,\n clientId: undefined,\n discovery: {wellKnown: null},\n exchangeToken: unsupported('exchangeToken'),\n getAccessToken: unsupported('getAccessToken'),\n getDecodedIdToken: unsupported('getDecodedIdToken'),\n getIdToken: unsupported('getIdToken'),\n getStorageManager: () => Promise.resolve(null),\n http: {\n request: unsupported('http.request'),\n requestAll: unsupported('http.requestAll'),\n },\n instanceId: 0,\n isInitialized: !isLoading,\n isLoading,\n isMetaLoading: false,\n isSignedIn,\n meta: initialMeta ?? null,\n organizationHandle,\n reInitialize: unsupported('reInitialize'),\n recover: unsupported('recover'),\n resolveFlowTemplateLiterals: (text: string | undefined): string => text ?? '',\n scopes,\n signIn: handleSignIn,\n signInSilently: unsupported('signInSilently'),\n signInUrl,\n signOut: handleSignOut,\n signUp: handleSignUp,\n signUpUrl,\n user,\n vendor: getVendorPrefix(vendor),\n }),\n [\n applicationId,\n baseUrl,\n clearSession,\n isLoading,\n isSignedIn,\n organizationHandle,\n scopes,\n signInUrl,\n signUpUrl,\n user,\n initialMeta,\n vendor,\n ],\n );\n\n return (\n <ThunderIDContext.Provider value={contextValue}>\n <ReactThunderIDContext.Provider value={reactContextValue}>\n <I18nProvider preferences={preferences?.i18n}>\n <FlowMetaProvider enabled={preferences?.resolveFromMeta !== false} initialMeta={initialMeta}>\n <ThemeProvider theme={preferences?.theme?.overrides} mode={getActiveTheme(preferences?.theme?.mode as any)}>\n <FlowProvider>\n <UserProvider profile={userProfile} onUpdateProfile={handleProfileUpdate} updateProfile={updateProfile}>\n {children}\n </UserProvider>\n </FlowProvider>\n </ThemeProvider>\n </FlowMetaProvider>\n </I18nProvider>\n </ReactThunderIDContext.Provider>\n </ThunderIDContext.Provider>\n );\n};\n\nexport default ThunderIDClientProvider;\n"],"mappings":"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"}
|
|
@@ -131,13 +131,13 @@ var SessionManager = class {
|
|
|
131
131
|
* Get session cookie name
|
|
132
132
|
*/
|
|
133
133
|
static getSessionCookieName() {
|
|
134
|
-
return CookieConfig.
|
|
134
|
+
return CookieConfig.getSessionCookieName();
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* Get temporary session cookie name
|
|
138
138
|
*/
|
|
139
139
|
static getTempSessionCookieName() {
|
|
140
|
-
return CookieConfig.
|
|
140
|
+
return CookieConfig.getTempSessionCookieName();
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
143
|
var SessionManager_default = SessionManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionManager.js","names":["secret: string | undefined","secret: Uint8Array","envValue: string | undefined","parsed: number","payload: SessionTokenPayload"],"sources":["../../src/utils/SessionManager.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 {ThunderIDRuntimeError, CookieConfig} from '@thunderid/node';\nimport {SignJWT, jwtVerify, compactVerify, JWTPayload} from 'jose';\nimport {DEFAULT_SESSION_COOKIE_EXPIRY_TIME} from '../constants/sessionConstants';\n\n/**\n * Session token payload interface\n */\nexport interface SessionTokenPayload extends JWTPayload {\n /** Expiration timestamp — doubles as the access token expiry (JWT exp == access token exp) */\n exp: number;\n /** Issued at timestamp */\n iat: number;\n /** Organization ID if applicable */\n organizationId?: string;\n /** The refresh token; empty string if not provided by the auth server */\n refreshToken: string;\n /** OAuth scopes */\n scopes: string[];\n /** Session ID */\n sessionId: string;\n /** User ID */\n sub: string;\n /** Token type discriminant — must be 'session' for access-session JWTs */\n type: 'session';\n}\n\n/**\n * Session management utility class for JWT-based session cookies\n */\nclass SessionManager {\n /**\n * Get the signing secret from environment variable\n * Throws error in production if not set\n */\n private static getSecret(): Uint8Array {\n const secret: string | undefined = process.env['THUNDERID_SECRET'];\n\n if (!secret) {\n if (process.env['NODE_ENV'] === 'production') {\n throw new ThunderIDRuntimeError(\n 'THUNDERID_SECRET environment variable is required in production',\n 'session-secret-required',\n 'nextjs',\n 'Set the THUNDERID_SECRET environment variable with a secure random string',\n );\n }\n // Use a default secret for development (not secure)\n // eslint-disable-next-line no-console\n console.warn('Using default secret for development. Set THUNDERID_SECRET for production!');\n return new TextEncoder().encode('development-secret-not-for-production');\n }\n\n return new TextEncoder().encode(secret);\n }\n\n /**\n * Create a temporary session cookie for login initiation\n */\n static async createTempSession(sessionId: string): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n sessionId,\n type: 'temp',\n })\n .setProtectedHeader({alg: 'HS256'})\n .setIssuedAt()\n .setExpirationTime('15m')\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Resolve the session cookie expiry time in seconds.\n *\n * Resolution order (first defined value wins):\n * 1. `configuredExpiry` — value from `ThunderIDNodeConfig.sessionCookie?.expiryTime`\n * 2. `THUNDERID_SESSION_COOKIE_EXPIRY_TIME` environment variable\n * 3. `DEFAULT_SESSION_COOKIE_EXPIRY_TIME` (24 hours)\n */\n static resolveSessionCookieExpiry(configuredExpiry?: number): number {\n if (configuredExpiry != null && configuredExpiry > 0) {\n return configuredExpiry;\n }\n\n const envValue: string | undefined = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'];\n\n if (envValue) {\n const parsed: number = parseInt(envValue, 10);\n\n if (!Number.isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n\n return DEFAULT_SESSION_COOKIE_EXPIRY_TIME;\n }\n\n static async createSessionToken(\n accessToken: string,\n userId: string,\n sessionId: string,\n scopes: string,\n accessTokenTtlSeconds: number,\n refreshToken: string,\n organizationId?: string,\n ): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n accessToken,\n organizationId,\n refreshToken,\n scopes,\n sessionId,\n type: 'session',\n } as Omit<SessionTokenPayload, 'sub' | 'iat' | 'exp'>)\n .setProtectedHeader({alg: 'HS256'})\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(Math.floor(Date.now() / 1000) + accessTokenTtlSeconds)\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Verify and decode a session token\n */\n static async verifySessionToken(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload as SessionTokenPayload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token',\n 'nextjs',\n 'Session token verification failed',\n );\n }\n }\n\n /**\n * Verify a session token for refresh. Validates the HMAC signature and the\n * `type === 'session'` discriminant but intentionally skips the `exp` check\n * so an expired access token can still be exchanged for a new one.\n *\n * Session lifetime is still bounded — the cookie's `maxAge` is set from\n * `sessionCookieExpiryTime`, so the browser drops an over-age session regardless\n * of the access-token exp embedded in the JWT.\n *\n * Never use the returned payload for authorization.\n */\n static async verifySessionTokenForRefresh(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload: rawPayload} = await compactVerify(token, secret);\n const payload: SessionTokenPayload = JSON.parse(new TextDecoder().decode(rawPayload)) as SessionTokenPayload;\n\n if (payload.type !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token-for-refresh',\n 'nextjs',\n 'Session token signature or type check failed during refresh',\n );\n }\n }\n\n /**\n * Verify and decode a temporary session token\n */\n static async verifyTempSession(token: string): Promise<{sessionId: string}> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'temp') {\n throw new Error('Invalid token type');\n }\n\n return {sessionId: payload['sessionId'] as string};\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid temporary session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-temp-session-token',\n 'nextjs',\n 'Temporary session token verification failed',\n );\n }\n }\n\n /**\n * Get session cookie options\n */\n static getSessionCookieOptions(maxAge: number): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get temporary session cookie options\n */\n static getTempSessionCookieOptions(): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge: 15 * 60,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get session cookie name\n */\n static getSessionCookieName(): string {\n return CookieConfig.SESSION_COOKIE_NAME;\n }\n\n /**\n * Get temporary session cookie name\n */\n static getTempSessionCookieName(): string {\n return CookieConfig.TEMP_SESSION_COOKIE_NAME;\n }\n}\n\nexport default SessionManager;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionManager.js","names":["secret: string | undefined","secret: Uint8Array","envValue: string | undefined","parsed: number","payload: SessionTokenPayload"],"sources":["../../src/utils/SessionManager.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 {ThunderIDRuntimeError, CookieConfig} from '@thunderid/node';\nimport {SignJWT, jwtVerify, compactVerify, JWTPayload} from 'jose';\nimport {DEFAULT_SESSION_COOKIE_EXPIRY_TIME} from '../constants/sessionConstants';\n\n/**\n * Session token payload interface\n */\nexport interface SessionTokenPayload extends JWTPayload {\n /** Expiration timestamp — doubles as the access token expiry (JWT exp == access token exp) */\n exp: number;\n /** Issued at timestamp */\n iat: number;\n /** Organization ID if applicable */\n organizationId?: string;\n /** The refresh token; empty string if not provided by the auth server */\n refreshToken: string;\n /** OAuth scopes */\n scopes: string[];\n /** Session ID */\n sessionId: string;\n /** User ID */\n sub: string;\n /** Token type discriminant — must be 'session' for access-session JWTs */\n type: 'session';\n}\n\n/**\n * Session management utility class for JWT-based session cookies\n */\nclass SessionManager {\n /**\n * Get the signing secret from environment variable\n * Throws error in production if not set\n */\n private static getSecret(): Uint8Array {\n const secret: string | undefined = process.env['THUNDERID_SECRET'];\n\n if (!secret) {\n if (process.env['NODE_ENV'] === 'production') {\n throw new ThunderIDRuntimeError(\n 'THUNDERID_SECRET environment variable is required in production',\n 'session-secret-required',\n 'nextjs',\n 'Set the THUNDERID_SECRET environment variable with a secure random string',\n );\n }\n // Use a default secret for development (not secure)\n // eslint-disable-next-line no-console\n console.warn('Using default secret for development. Set THUNDERID_SECRET for production!');\n return new TextEncoder().encode('development-secret-not-for-production');\n }\n\n return new TextEncoder().encode(secret);\n }\n\n /**\n * Create a temporary session cookie for login initiation\n */\n static async createTempSession(sessionId: string): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n sessionId,\n type: 'temp',\n })\n .setProtectedHeader({alg: 'HS256'})\n .setIssuedAt()\n .setExpirationTime('15m')\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Resolve the session cookie expiry time in seconds.\n *\n * Resolution order (first defined value wins):\n * 1. `configuredExpiry` — value from `ThunderIDNodeConfig.sessionCookie?.expiryTime`\n * 2. `THUNDERID_SESSION_COOKIE_EXPIRY_TIME` environment variable\n * 3. `DEFAULT_SESSION_COOKIE_EXPIRY_TIME` (24 hours)\n */\n static resolveSessionCookieExpiry(configuredExpiry?: number): number {\n if (configuredExpiry != null && configuredExpiry > 0) {\n return configuredExpiry;\n }\n\n const envValue: string | undefined = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'];\n\n if (envValue) {\n const parsed: number = parseInt(envValue, 10);\n\n if (!Number.isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n\n return DEFAULT_SESSION_COOKIE_EXPIRY_TIME;\n }\n\n static async createSessionToken(\n accessToken: string,\n userId: string,\n sessionId: string,\n scopes: string,\n accessTokenTtlSeconds: number,\n refreshToken: string,\n organizationId?: string,\n ): Promise<string> {\n const secret: Uint8Array = this.getSecret();\n\n const jwt: string = await new SignJWT({\n accessToken,\n organizationId,\n refreshToken,\n scopes,\n sessionId,\n type: 'session',\n } as Omit<SessionTokenPayload, 'sub' | 'iat' | 'exp'>)\n .setProtectedHeader({alg: 'HS256'})\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(Math.floor(Date.now() / 1000) + accessTokenTtlSeconds)\n .sign(secret);\n\n return jwt;\n }\n\n /**\n * Verify and decode a session token\n */\n static async verifySessionToken(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload as SessionTokenPayload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token',\n 'nextjs',\n 'Session token verification failed',\n );\n }\n }\n\n /**\n * Verify a session token for refresh. Validates the HMAC signature and the\n * `type === 'session'` discriminant but intentionally skips the `exp` check\n * so an expired access token can still be exchanged for a new one.\n *\n * Session lifetime is still bounded — the cookie's `maxAge` is set from\n * `sessionCookieExpiryTime`, so the browser drops an over-age session regardless\n * of the access-token exp embedded in the JWT.\n *\n * Never use the returned payload for authorization.\n */\n static async verifySessionTokenForRefresh(token: string): Promise<SessionTokenPayload> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload: rawPayload} = await compactVerify(token, secret);\n const payload: SessionTokenPayload = JSON.parse(new TextDecoder().decode(rawPayload)) as SessionTokenPayload;\n\n if (payload.type !== 'session') {\n throw new Error('Invalid token type');\n }\n\n return payload;\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-session-token-for-refresh',\n 'nextjs',\n 'Session token signature or type check failed during refresh',\n );\n }\n }\n\n /**\n * Verify and decode a temporary session token\n */\n static async verifyTempSession(token: string): Promise<{sessionId: string}> {\n try {\n const secret: Uint8Array = this.getSecret();\n const {payload} = await jwtVerify(token, secret);\n\n if (payload['type'] !== 'temp') {\n throw new Error('Invalid token type');\n }\n\n return {sessionId: payload['sessionId'] as string};\n } catch (error) {\n throw new ThunderIDRuntimeError(\n `Invalid temporary session token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'invalid-temp-session-token',\n 'nextjs',\n 'Temporary session token verification failed',\n );\n }\n }\n\n /**\n * Get session cookie options\n */\n static getSessionCookieOptions(maxAge: number): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get temporary session cookie options\n */\n static getTempSessionCookieOptions(): {\n httpOnly: boolean;\n maxAge: number;\n path: string;\n sameSite: 'lax';\n secure: boolean;\n } {\n return {\n httpOnly: true,\n maxAge: 15 * 60,\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env['NODE_ENV'] === 'production',\n };\n }\n\n /**\n * Get session cookie name\n */\n static getSessionCookieName(): string {\n return CookieConfig.getSessionCookieName();\n }\n\n /**\n * Get temporary session cookie name\n */\n static getTempSessionCookieName(): string {\n return CookieConfig.getTempSessionCookieName();\n }\n}\n\nexport default SessionManager;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorateConfigWithNextEnv.d.ts","sourceRoot":"","sources":["../../src/utils/decorateConfigWithNextEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AAErD,QAAA,MAAM,yBAAyB,GAAI,QAAQ,mBAAmB,KAAG,
|
|
1
|
+
{"version":3,"file":"decorateConfigWithNextEnv.d.ts","sourceRoot":"","sources":["../../src/utils/decorateConfigWithNextEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AAErD,QAAA,MAAM,yBAAyB,GAAI,QAAQ,mBAAmB,KAAG,mBAsChE,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/utils/decorateConfigWithNextEnv.ts
|
|
2
2
|
const decorateConfigWithNextEnv = (config) => {
|
|
3
|
-
const { organizationHandle, scopes, applicationId, baseUrl, clientId, clientSecret, signInUrl, signUpUrl, afterSignInUrl, afterSignOutUrl,...rest } = config;
|
|
3
|
+
const { organizationHandle, scopes, applicationId, baseUrl, clientId, clientSecret, signInUrl, signUpUrl, afterSignInUrl, afterSignOutUrl, vendor,...rest } = config;
|
|
4
4
|
const envExpiryTime = process.env["THUNDERID_SESSION_COOKIE_EXPIRY_TIME"] ? parseInt(process.env["THUNDERID_SESSION_COOKIE_EXPIRY_TIME"], 10) : void 0;
|
|
5
5
|
return {
|
|
6
6
|
...rest,
|
|
@@ -17,7 +17,8 @@ const decorateConfigWithNextEnv = (config) => {
|
|
|
17
17
|
expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime
|
|
18
18
|
},
|
|
19
19
|
signInUrl: signInUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_IN_URL"],
|
|
20
|
-
signUpUrl: signUpUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_UP_URL"]
|
|
20
|
+
signUpUrl: signUpUrl || process.env["NEXT_PUBLIC_THUNDERID_SIGN_UP_URL"],
|
|
21
|
+
vendor: vendor || process.env["NEXT_PUBLIC_THUNDERID_VENDOR"]
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
24
|
var decorateConfigWithNextEnv_default = decorateConfigWithNextEnv;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorateConfigWithNextEnv.js","names":[],"sources":["../../src/utils/decorateConfigWithNextEnv.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 {ThunderIDNextConfig} from '../models/config';\n\nconst decorateConfigWithNextEnv = (config: ThunderIDNextConfig): ThunderIDNextConfig => {\n const {\n organizationHandle,\n scopes,\n applicationId,\n baseUrl,\n clientId,\n clientSecret,\n signInUrl,\n signUpUrl,\n afterSignInUrl,\n afterSignOutUrl,\n ...rest\n } = config;\n\n const envExpiryTime = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME']\n ? parseInt(process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'], 10)\n : undefined;\n\n return {\n ...rest,\n afterSignInUrl: afterSignInUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL']!,\n afterSignOutUrl: afterSignOutUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL']!,\n applicationId: applicationId || process.env['NEXT_PUBLIC_THUNDERID_APPLICATION_ID']!,\n baseUrl: baseUrl || process.env['NEXT_PUBLIC_THUNDERID_BASE_URL']!,\n clientId: clientId || process.env['NEXT_PUBLIC_THUNDERID_CLIENT_ID']!,\n clientSecret: clientSecret || process.env['THUNDERID_CLIENT_SECRET']!,\n organizationHandle: organizationHandle || process.env['NEXT_PUBLIC_THUNDERID_ORGANIZATION_HANDLE']!,\n scopes: scopes || process.env['NEXT_PUBLIC_THUNDERID_SCOPES']!,\n sessionCookie: {\n ...rest.sessionCookie,\n expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime,\n },\n signInUrl: signInUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_IN_URL']!,\n signUpUrl: signUpUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_UP_URL']!,\n };\n};\n\nexport default decorateConfigWithNextEnv;\n"],"mappings":";AAoBA,MAAM,6BAA6B,WAAqD;CACtF,MAAM,EACJ,oBACA,QACA,eACA,SACA,UACA,cACA,WACA,WACA,gBACA,
|
|
1
|
+
{"version":3,"file":"decorateConfigWithNextEnv.js","names":[],"sources":["../../src/utils/decorateConfigWithNextEnv.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 {ThunderIDNextConfig} from '../models/config';\n\nconst decorateConfigWithNextEnv = (config: ThunderIDNextConfig): ThunderIDNextConfig => {\n const {\n organizationHandle,\n scopes,\n applicationId,\n baseUrl,\n clientId,\n clientSecret,\n signInUrl,\n signUpUrl,\n afterSignInUrl,\n afterSignOutUrl,\n vendor,\n ...rest\n } = config;\n\n const envExpiryTime = process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME']\n ? parseInt(process.env['THUNDERID_SESSION_COOKIE_EXPIRY_TIME'], 10)\n : undefined;\n\n return {\n ...rest,\n afterSignInUrl: afterSignInUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_IN_URL']!,\n afterSignOutUrl: afterSignOutUrl || process.env['NEXT_PUBLIC_THUNDERID_AFTER_SIGN_OUT_URL']!,\n applicationId: applicationId || process.env['NEXT_PUBLIC_THUNDERID_APPLICATION_ID']!,\n baseUrl: baseUrl || process.env['NEXT_PUBLIC_THUNDERID_BASE_URL']!,\n clientId: clientId || process.env['NEXT_PUBLIC_THUNDERID_CLIENT_ID']!,\n clientSecret: clientSecret || process.env['THUNDERID_CLIENT_SECRET']!,\n organizationHandle: organizationHandle || process.env['NEXT_PUBLIC_THUNDERID_ORGANIZATION_HANDLE']!,\n scopes: scopes || process.env['NEXT_PUBLIC_THUNDERID_SCOPES']!,\n sessionCookie: {\n ...rest.sessionCookie,\n expiryTime: rest.sessionCookie?.expiryTime || envExpiryTime,\n },\n signInUrl: signInUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_IN_URL']!,\n signUpUrl: signUpUrl || process.env['NEXT_PUBLIC_THUNDERID_SIGN_UP_URL']!,\n vendor: vendor || process.env['NEXT_PUBLIC_THUNDERID_VENDOR'],\n };\n};\n\nexport default decorateConfigWithNextEnv;\n"],"mappings":";AAoBA,MAAM,6BAA6B,WAAqD;CACtF,MAAM,EACJ,oBACA,QACA,eACA,SACA,UACA,cACA,WACA,WACA,gBACA,iBACA,OACA,GAAG,SACD;CAEJ,MAAM,gBAAgB,QAAQ,IAAI,0CAC9B,SAAS,QAAQ,IAAI,yCAAyC,GAAG,GACjE;AAEJ,QAAO;EACL,GAAG;EACH,gBAAgB,kBAAkB,QAAQ,IAAI;EAC9C,iBAAiB,mBAAmB,QAAQ,IAAI;EAChD,eAAe,iBAAiB,QAAQ,IAAI;EAC5C,SAAS,WAAW,QAAQ,IAAI;EAChC,UAAU,YAAY,QAAQ,IAAI;EAClC,cAAc,gBAAgB,QAAQ,IAAI;EAC1C,oBAAoB,sBAAsB,QAAQ,IAAI;EACtD,QAAQ,UAAU,QAAQ,IAAI;EAC9B,eAAe;GACb,GAAG,KAAK;GACR,YAAY,KAAK,eAAe,cAAc;GAC/C;EACD,WAAW,aAAa,QAAQ,IAAI;EACpC,WAAW,aAAa,QAAQ,IAAI;EACpC,QAAQ,UAAU,QAAQ,IAAI;EAC/B;;AAGH,wCAAe"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thunderid/nextjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Next.js SDK for ThunderID",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"thunderid",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"@types/react": "19.2.14",
|
|
41
41
|
"jose": "5.2.0",
|
|
42
42
|
"tslib": "2.8.1",
|
|
43
|
-
"@thunderid/node": "^0.
|
|
44
|
-
"@thunderid/react": "^0.
|
|
43
|
+
"@thunderid/node": "^0.6.0",
|
|
44
|
+
"@thunderid/react": "^0.6.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@thunderid/eslint-plugin": "0.0.2",
|