UNPKG

@wristband/react-client-auth

Version:

A lightweight React SDK that pairs with your backend server auth to initialize and sync frontend sessions via secure session cookies.

132 lines (128 loc) 5.5 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); var wristbandAuthContext = require('./wristband-auth-context.js'); var authProviderTypes = require('../types/auth-provider-types.js'); var authUtils = require('../utils/auth-utils.js'); var apiClient = require('../api/api-client.js'); var authProviderUtils = require('../utils/auth-provider-utils.js'); /** * WristbandAuthProvider establishes an authenticated session with your backend server * by making a request to your session endpoint. It manages authentication state and * provides session data to all child components through React Context. * * This component should be placed near the root of your application to make authentication * state and session data available throughout your component tree. * * @example Basic usage * ```jsx * function App() { * return ( * <WristbandAuthProvider * loginUrl="/api/auth/login" * logoutUrl="/api/auth/logout" * sessionUrl="/api/auth/session" * > * <YourAppComponents /> * </WristbandAuthProvider> * ); * } * ``` * * @example With custom session metadata handling * ```jsx * function App() { * const queryClient = useQueryClient(); * * return ( * <WristbandAuthProvider * loginUrl="/api/auth/login" * logoutUrl="/api/auth/logout" * sessionUrl="/api/auth/session" * transformSessionMetadata={(rawMetadata) => ({ * name: rawMetadata.displayName, * email: rawMetadata.email, * role: rawMetadata.userRole * })} * onSessionSuccess={(sessionData) => { * // Cache additional data in React Query * queryClient.setQueryData(['user-permissions'], sessionData.permissions); * }} * > * <YourAppComponents /> * </WristbandAuthProvider> * ); * } * ``` * * Once rendered, child components can access authentication state using the hooks: * - useWristbandAuth() - For authentication status (isAuthenticated, isLoading, authStatus) * - useWristbandSession() - For session data (userId, tenantId, metadata) * * @template TSessionMetaData - Type for the transformed session metadata, if applicable. */ function WristbandAuthProvider({ children, csrfCookieName = 'CSRF-TOKEN', csrfHeaderName = 'X-CSRF-TOKEN', disableRedirectOnUnauthenticated = false, loginUrl, logoutUrl, onSessionSuccess, sessionUrl, transformSessionMetadata, }) { const resolvedLoginUrl = authProviderUtils.resolveAuthProviderLoginUrl(loginUrl); authProviderUtils.validateAuthProviderLogoutUrl(logoutUrl); authProviderUtils.validateAuthProviderSessionUrl(sessionUrl); const [isAuthenticated, setIsAuthenticated] = react.useState(false); const [isLoading, setIsLoading] = react.useState(true); const [userId, setUserId] = react.useState(''); const [tenantId, setTenantId] = react.useState(''); const [metadata, setMetadata] = react.useState({}); const authStatus = isLoading ? authProviderTypes.AuthStatus.LOADING : isAuthenticated ? authProviderTypes.AuthStatus.AUTHENTICATED : authProviderTypes.AuthStatus.UNAUTHENTICATED; const updateMetadata = react.useCallback((newMetadata) => { setMetadata((prevData) => ({ ...prevData, ...newMetadata })); }, []); // Bootstrap the application with the authenticated user's session data. react.useEffect(() => { const fetchSession = async () => { try { // The session API will let React know if the user has a previously authenticated session. // If so, it will initialize session data. const response = await apiClient.get(sessionUrl, { csrfCookieName, csrfHeaderName }); const { userId, tenantId, metadata: rawMetadata } = response.data; // Execute side effects callback before updating state if provided if (onSessionSuccess) { await Promise.resolve(onSessionSuccess(response.data)); } // Apply transformation if provided if (rawMetadata) { setMetadata(transformSessionMetadata ? transformSessionMetadata(rawMetadata) : rawMetadata); } // Update remaining context state last setTenantId(tenantId || ''); setUserId(userId || ''); setIsAuthenticated(true); setIsLoading(false); } catch (error) { console.log(error); if (disableRedirectOnUnauthenticated) { setIsAuthenticated(false); setIsLoading(false); } else { // Don't call logout on 401 to preserve the current page for when the user returns after re-authentication. window.location.href = authUtils.isUnauthorizedError(error) ? resolvedLoginUrl : logoutUrl; } } }; fetchSession(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (jsxRuntime.jsx(wristbandAuthContext.WristbandAuthContext.Provider, { value: { authStatus, isAuthenticated, isLoading, metadata, tenantId, updateMetadata, userId, }, children: children })); } exports.WristbandAuthProvider = WristbandAuthProvider;