@agility/management-sdk
Version:
Agility CMS Tyescript SDK for Management API.
361 lines (292 loc) • 9.09 kB
Markdown
# 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).