UNPKG

@agility/management-sdk

Version:
361 lines (292 loc) 9.09 kB
# Agility CMS Authentication Component ## Overview The Agility CMS Authentication Component provides a complete, reusable authentication system for React applications. It handles OAuth authentication, token management, and provides a rich set of hooks and components for building authentication UIs. ## Features - **OAuth Authentication**: Secure popup-based authentication flow - **Token Management**: Automatic token refresh and secure storage - **Type Safety**: Full TypeScript support with comprehensive interfaces - **Multiple Display Modes**: Fullscreen, footer bar, and button-only modes - **Extensive Customization**: Callbacks, styling, and slot-based customization - **Performance Optimized**: Uses React hooks and context for efficient state management ## Installation ```bash npm install @agility/management-sdk-typescript ``` ## Basic Usage ### 1. Using the Authentication Hook The simplest way to add authentication to your app: ```typescript import { useAgilityAuth } from '@agility/management-sdk-typescript'; function MyApp() { const { isAuthenticated, isLoading, user, websiteAccess, selectedWebsite, selectedLocale, locales, error, authenticate, signOut, selectWebsite, selectLocale, clearError, hasSelection, currentSelection } = useAgilityAuth({ redirectUri: 'https://myapp.com/auth-callback.html', scope: 'openid profile email offline_access', autoCheckAuth: true // Automatically check auth on mount }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; if (!isAuthenticated) { return ( <button onClick={authenticate}> Sign in with Agility CMS </button> ); } return ( <div> <h1>Welcome, {user?.userName}!</h1> <select value={selectedWebsite} onChange={(e) => selectWebsite(e.target.value)} > <option value="">Choose a website...</option> {websiteAccess.map(site => ( <option key={site.websiteGuid} value={site.websiteGuid}> {site.websiteName} </option> ))} </select> {selectedWebsite && ( <select value={selectedLocale} onChange={(e) => selectLocale(e.target.value)} > <option value="">Choose a locale...</option> {locales.map(locale => ( <option key={locale.localeCode} value={locale.localeCode}> {locale.localeName} </option> ))} </select> )} {hasSelection && ( <div> <p>Current Selection: {currentSelection?.websiteName} - {currentSelection?.localeName}</p> </div> )} <button onClick={signOut}>Sign Out</button> </div> ); } ``` ### 2. Using the Context Provider For larger applications, use the context provider to share authentication state: ```typescript import { AuthProvider, useAuthContext } from '@agility/management-sdk-typescript'; // App component function App() { return ( <AuthProvider> <MyAuthenticatedApp /> </AuthProvider> ); } // Child component that uses auth context function MyAuthenticatedApp() { const { state, actions } = useAuthContext(); return ( <div> {state.isAuthenticated ? ( <Dashboard user={state.user} /> ) : ( <LoginForm onLogin={actions.setAuthenticated} /> )} </div> ); } ``` ### 3. Using Granular Context Hooks For more specific use cases, use the granular hooks: ```typescript import { useIsAuthenticated, useCurrentUser, useSelection, useLoadingStates, useAuthError } from '@agility/management-sdk-typescript'; function UserProfile() { const isAuthenticated = useIsAuthenticated(); const user = useCurrentUser(); const { isLoading } = useLoadingStates(); if (!isAuthenticated) return <div>Please log in</div>; if (isLoading) return <div>Loading...</div>; return ( <div> <h2>{user?.userName}</h2> <p>{user?.emailAddress}</p> </div> ); } function WebsiteSelector() { const { selectedWebsite, selectedLocale, hasSelection } = useSelection(); const { error, clearError } = useAuthError(); return ( <div> {error && ( <div className="error"> {error} <button onClick={clearError}>Clear</button> </div> )} {hasSelection && ( <div>Selected: {selectedWebsite} - {selectedLocale}</div> )} </div> ); } ``` ## Advanced Usage ### Custom OAuth Configuration ```typescript const auth = useAgilityAuth({ redirectUri: 'https://myapp.com/auth-callback.html', scope: 'openid profile email offline_access', region: 'us-west-2', autoCheckAuth: true }); ``` ### Website Selection with Validation ```typescript import { useWebsiteSelection, validateSelection, sortWebsitesByName } from '@agility/management-sdk-typescript'; function WebsiteManager() { const auth = useAgilityAuth(); const selection = useWebsiteSelection( auth.websiteAccess, auth.selectedWebsite, auth.locales, auth.selectedLocale, auth.isLoadingLocales, auth.selectWebsite, auth.selectLocale ); const sortedWebsites = sortWebsitesByName(selection.websites); const validation = validateSelection( selection.websites, selection.selectedWebsite, selection.locales, selection.selectedLocale ); return ( <div> <select onChange={(e) => selection.selectWebsite(e.target.value)}> {sortedWebsites.map(site => ( <option key={site.websiteGuid} value={site.websiteGuid}> {site.websiteName} </option> ))} </select> {!validation.isValid && ( <div className="validation-errors"> {validation.errors.map(error => ( <div key={error} className="error">{error}</div> ))} </div> )} </div> ); } ``` ## Type Definitions ### Core Types ```typescript interface AgilityAuthConfig { // Callbacks onSignIn?: (user: ServerUser) => void; onSignOut?: () => void; onWebsiteSelect?: (website: WebsiteAccess) => void; onLocaleSelect?: (locale: LocaleInfo) => void; // Display & Behavior mode?: 'fullscreen' | 'footer' | 'button-only'; theme?: 'light' | 'dark' | 'auto'; showCurrentSelection?: boolean; // OAuth Settings redirectUri?: string; scope?: string; region?: string; } interface WebsiteAccess { websiteGuid: string; websiteName: string; websiteDescription?: string; } interface LocaleInfo { localeCode: string; localeID: number; localeName: string; isDefault?: boolean; isEnabled?: boolean; } ``` ## Coming Soon The following UI components are currently in development: - **AgilityAuth Component**: Full-featured authentication component - **AuthButton**: Standalone authentication button - **WebsiteSelector**: Website selection dropdown - **LocaleSelector**: Locale selection dropdown - **UserInfo**: User information display - **Footer Mode**: Collapsible footer bar implementation ## Migration from Direct SDK Usage If you're currently using the SDK directly, you can gradually migrate: ```typescript // Before: Direct SDK usage const client = new ApiClient(); await client.auth(); const user = await client.serverUserMethods.me(); // After: Using the hook const { authenticate, user, isAuthenticated } = useAgilityAuth(); if (!isAuthenticated) { await authenticate(); } // user is now available directly from the hook ``` ## Performance Considerations - State is managed using React's `useReducer` for optimal performance - Context values are memoized to prevent unnecessary re-renders - All callback functions are memoized with `useCallback` - Automatic cleanup of event listeners and timeouts ## Browser Support - Modern browsers with ES2017+ support - React 16.8+ (hooks support) - TypeScript 4.0+ (optional but recommended) ## Error Handling The authentication system provides comprehensive error handling: ```typescript const { error, clearError } = useAuthError(); // Display errors to user if (error) { return ( <div className="error"> {error} <button onClick={clearError}>Dismiss</button> </div> ); } ``` ## Security - Tokens are stored securely using `keytar` (Node.js) or `localStorage` (browser) - Automatic token refresh prevents expired token issues - OAuth flow uses secure popup window with proper origin validation - All sensitive operations are handled server-side ## Support For issues and feature requests, please visit the [GitHub repository](https://github.com/agility/agility-cms-management-sdk-typescript).