UNPKG

@asgardeo/nextjs

Version:

Next.js implementation of Asgardeo JavaScript SDK.

189 lines 9.16 kB
/** * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ 'use client'; import { jsx as _jsx } from "react/jsx-runtime"; import { generateFlattenedUserProfile, } from '@asgardeo/node'; import { I18nProvider, FlowProvider, UserProvider, ThemeProvider, OrganizationProvider, BrandingProvider, } from '@asgardeo/react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useMemo, useRef, useState } from 'react'; import AsgardeoContext from './AsgardeoContext'; import logger from '../../../utils/logger'; const AsgardeoClientProvider = ({ baseUrl, children, signIn, signOut, signUp, handleOAuthCallback, createOrganization, preferences, isSignedIn, signInUrl, signUpUrl, user: _user, userProfile: _userProfile, currentOrganization, updateProfile, applicationId, organizationHandle, myOrganizations, revalidateMyOrganizations, getAllOrganizations, switchOrganization, brandingPreference, }) => { const reRenderCheckRef = useRef(false); const router = useRouter(); const searchParams = useSearchParams(); const [isDarkMode, setIsDarkMode] = useState(false); const [isLoading, setIsLoading] = useState(true); const [user, setUser] = useState(_user); const [userProfile, setUserProfile] = useState(_userProfile); useEffect(() => { setUserProfile(_userProfile); }, [_userProfile]); useEffect(() => { setUser(_user); }, [_user]); // Handle OAuth callback automatically useEffect(() => { // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature. // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state // This will remount all the useEffects to ensure that there are no unexpected side effects. // When react remounts the signIn hook of the AuthProvider, it will cause a race condition. Hence, we have to // prevent the re-render of this hook as suggested in the following discussion. // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623 if (reRenderCheckRef.current) { return; } reRenderCheckRef.current = true; // Don't handle callback if already signed in if (isSignedIn) return; (async () => { try { const code = searchParams.get('code'); const state = searchParams.get('state'); const sessionState = searchParams.get('session_state'); const error = searchParams.get('error'); const errorDescription = searchParams.get('error_description'); // Check for OAuth errors first if (error) { logger.error('[AsgardeoClientProvider] An error was received for the initiated sign-in request.'); return; } // Handle OAuth callback if code and state are present if (code && state) { setIsLoading(true); const result = await handleOAuthCallback(code, state, sessionState || undefined); if (result.success) { // Redirect to the success URL if (result.redirectUrl) { router.push(result.redirectUrl); } else { // Refresh the page to update authentication state window.location.reload(); } } else { logger.error(`[AsgardeoClientProvider] An error occurred while signing in: ${result.error || 'Authentication failed'}`); } } } catch (error) { logger.error('[AsgardeoClientProvider] Failed to handle OAuth callback:', error); } })(); }, []); useEffect(() => { if (!preferences?.theme?.mode || preferences.theme.mode === 'system') { setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches); } else { setIsDarkMode(preferences.theme.mode === 'dark'); } }, [preferences?.theme?.mode]); useEffect(() => { // Set loading to false when server has resolved authentication state setIsLoading(false); }, [isSignedIn, user]); const handleSignIn = async (payload, request) => { try { const result = await signIn(payload, request); // Redirect based flow URL is sent as `signInUrl` in the response. if (result?.data?.signInUrl) { router.push(result.data.signInUrl); return; } // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignInUrl` in the response. if (result?.data?.afterSignInUrl) { router.push(result.data.afterSignInUrl); return; } if (result?.error) { throw new Error(result.error); } return result?.data ?? result; } catch (error) { throw error; } }; const handleSignUp = async (payload, request) => { try { const result = await signUp(payload, request); // Redirect based flow URL is sent as `signUpUrl` in the response. if (result?.data?.signUpUrl) { router.push(result.data.signUpUrl); return; } // After the Embedded flow is successful, the URL to navigate next is sent as `afterSignUpUrl` in the response. if (result?.data?.afterSignUpUrl) { router.push(result.data.afterSignUpUrl); return; } if (result?.error) { throw new Error(result.error); } return result?.data ?? result; } catch (error) { throw error; } }; const handleSignOut = async () => { logger.debug('[AsgardeoClientProvider][handleSignOut] `handleSignOut` called.'); try { const result = await signOut(); logger.debug('[AsgardeoClientProvider][handleSignOut] Sign out result:', result); if (result?.data?.afterSignOutUrl) { router.push(result.data.afterSignOutUrl); return { redirected: true, location: result.data.afterSignOutUrl }; } if (result?.error) { logger.error('[AsgardeoClientProvider][handleSignOut] Error result was returned during signing the user out with a button click:', result.error); } return result?.data ?? result; } catch (error) { logger.error('[AsgardeoClientProvider][handleSignOut] Error occurred during signing the user out with a button click:', error); } }; const contextValue = useMemo(() => ({ baseUrl, user, isSignedIn, isLoading, signIn: handleSignIn, signOut: handleSignOut, signUp: handleSignUp, signInUrl, signUpUrl, applicationId, organizationHandle, }), [baseUrl, user, isSignedIn, isLoading, signInUrl, signUpUrl, applicationId, organizationHandle]); const handleProfileUpdate = (payload) => { setUser(payload); setUserProfile(prev => ({ ...prev, profile: payload, flattenedProfile: generateFlattenedUserProfile(payload, prev?.schemas), })); }; return (_jsx(AsgardeoContext.Provider, { value: contextValue, children: _jsx(I18nProvider, { preferences: preferences?.i18n, children: _jsx(BrandingProvider, { brandingPreference: brandingPreference, children: _jsx(ThemeProvider, { theme: preferences?.theme?.overrides, mode: isDarkMode ? 'dark' : 'light', children: _jsx(FlowProvider, { children: _jsx(UserProvider, { profile: userProfile, onUpdateProfile: handleProfileUpdate, updateProfile: updateProfile, children: _jsx(OrganizationProvider, { createOrganization: createOrganization, getAllOrganizations: getAllOrganizations, myOrganizations: myOrganizations, currentOrganization: currentOrganization, onOrganizationSwitch: switchOrganization, revalidateMyOrganizations: revalidateMyOrganizations, children: children }) }) }) }) }) }) })); }; export default AsgardeoClientProvider; //# sourceMappingURL=AsgardeoProvider.js.map