@harlkwin/authflow-react
v0.4.15
Published
React components and hooks for authflow authentication
Downloads
104
Maintainers
Readme
AuthFlow Frontend
React/TypeScript components and hooks for AuthFlow authentication
Complete frontend package for building authentication UIs with AuthFlow backend.
🎯 Features
- ✅ Zustand State Management - Persistent auth state with localStorage
- ✅ TanStack Query Hooks - Data fetching with automatic caching and refetching
- ✅ Automatic Token Refresh - Axios interceptors handle 401s gracefully
- ✅ TypeScript Support - Full type safety throughout
- ✅ Pre-built Components - Login, Signup, UserMenu, ProtectedRoute, PermissionGate
- ✅ Permission System - Declarative role and permission checking
- ✅ Responsive Design - Mobile-friendly with Tailwind CSS
📦 Installation
npm install zustand @tanstack/react-query axios react-router-dom🚀 Quick Start
1. Configure API Client
// src/config.ts
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8001/api/v1';2. Setup Query Client Provider
// app/layout.tsx or main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
retry: 1,
},
},
});
export default function App({ children }) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}3. Use Authentication
import { useLogin, useCurrentUser, useIsAuthenticated } from '@/hooks';
import { LoginPage, ProtectedRoute, UserMenu } from '@/components';
function LoginExample() {
const login = useLogin();
const isAuthenticated = useIsAuthenticated();
const handleLogin = async () => {
await login.mutateAsync({
username: 'demo-admin',
password: 'admin123',
});
};
if (isAuthenticated) {
return <div>Already logged in!</div>;
}
return (
<button onClick={handleLogin} disabled={login.isPending}>
{login.isPending ? 'Logging in...' : 'Login'}
</button>
);
}
function Dashboard() {
const { data: user, isLoading } = useCurrentUser();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>Welcome {user?.username}!</h1>
<UserMenu />
</div>
);
}4. Protected Routes
import { ProtectedRoute } from '@/components';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="/admin"
element={
<ProtectedRoute requiredRole="admin">
<AdminPanel />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
);
}5. Permission-Based Rendering
import { PermissionGate } from '@/components';
import { useAuthStore } from '@/store/authStore';
function UserManagement() {
const canWrite = useAuthStore((state) => state.hasPermission('users:write'));
const canDelete = useAuthStore((state) => state.hasPermission('users:delete'));
return (
<div>
<h2>Users</h2>
{/* Only show if user has permission */}
<PermissionGate permission="users:write">
<button>Create User</button>
</PermissionGate>
{/* Only show for admins or moderators */}
<PermissionGate anyRole={['admin', 'moderator']}>
<button>Moderate Users</button>
</PermissionGate>
{/* Show fallback if no permission */}
<PermissionGate permission="users:delete" fallback={<DisabledButton />}>
<button>Delete User</button>
</PermissionGate>
</div>
);
}📚 API Reference
Hooks
Authentication
useLogin()- Login mutationuseLogout()- Logout mutationuseRegister()- User registration mutationuseCurrentUser()- Get current user queryuseRefreshToken()- Refresh token mutationuseChangePassword()- Change password mutationuseForgotPassword()- Forgot password mutationuseResetPassword()- Reset password mutationuseActiveSessions()- Get active sessions queryuseRevokeSession(sessionId)- Revoke session mutation
User Management
useUsers(filters?)- List users with paginationuseUser(userId)- Get single useruseCreateUser()- Create user mutationuseUpdateUser()- Update user mutationuseDeleteUser()- Delete user mutationuseUserRoles(userId)- Get user rolesuseAssignUserRoles()- Assign roles to useruseUpdateUserPassword()- Update user passworduseUserSessions(userId)- Get user sessions
Organizations
useOrganizations(filters?)- List organizationsuseOrganization(orgId)- Get organizationuseCreateOrganization()- Create organizationuseUpdateOrganization()- Update organizationuseDeleteOrganization()- Delete organizationuseOrganizationMembers(orgId)- Get membersuseAddOrganizationMember()- Add memberuseRemoveOrganizationMember()- Remove memberuseOrganizationSettings(orgId)- Get settingsuseUpdateOrganizationSettings()- Update settings
Teams
useTeams(filters?)- List teamsuseTeam(teamId)- Get teamuseCreateTeam()- Create teamuseUpdateTeam()- Update teamuseDeleteTeam()- Delete teamuseTeamMembers(teamId)- Get membersuseAddTeamMember()- Add memberuseRemoveTeamMember()- Remove memberuseTeamSubteams(teamId)- Get subteamsuseCreateSubteam()- Create subteam
Roles
useRoles(filters?)- List rolesuseRole(roleId)- Get roleuseCreateRole()- Create roleuseUpdateRole()- Update roleuseDeleteRole()- Delete roleuseRoleMembers(roleId)- Get role membersuseAssignRole()- Assign role to useruseUnassignRole()- Unassign role from useruseRolePermissions(roleId)- Get role permissions
Convenience Hooks
useIsAuthenticated()- Check if user is authenticateduseUser()- Get current user from storeuseHasRole(role)- Check if user has roleuseHasPermission(permission)- Check if user has permission
Components
<LoginPage />
Pre-built login page with form validation.
import { LoginPage } from '@/components';
<Route path="/login" element={<LoginPage />} /><SignupPage />
Pre-built signup page with validation.
import { SignupPage } from '@/components';
<Route path="/signup" element={<SignupPage />} /><UserMenu />
User dropdown menu with profile, settings, and logout.
import { UserMenu } from '@/components';
<header>
<UserMenu />
</header><ProtectedRoute>
Route-level authentication and authorization guard.
import { ProtectedRoute } from '@/components';
// Require authentication only
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
// Require specific role
<ProtectedRoute requiredRole="admin">
<AdminPanel />
</ProtectedRoute>
// Require specific permission
<ProtectedRoute requiredPermission="users:write">
<UserManagement />
</ProtectedRoute>
// Custom fallback
<ProtectedRoute
requiredRole="admin"
fallback={<div>Access Denied</div>}
>
<AdminPanel />
</ProtectedRoute><PermissionGate>
Conditional rendering based on permissions or roles.
import { PermissionGate } from '@/components';
// Require permission
<PermissionGate permission="users:write">
<button>Edit User</button>
</PermissionGate>
// Require role
<PermissionGate role="admin">
<AdminTools />
</PermissionGate>
// Require any of multiple roles
<PermissionGate anyRole={["admin", "moderator"]}>
<ModeratorPanel />
</PermissionGate>
// With fallback
<PermissionGate permission="users:delete" fallback={<DisabledButton />}>
<button>Delete User</button>
</PermissionGate>Store
useAuthStore
Zustand store for auth state management.
import { useAuthStore } from '@/store/authStore';
// Get state
const user = useAuthStore((state) => state.user);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
// Call actions
const login = useAuthStore((state) => state.login);
const logout = useAuthStore((state) => state.logout);
// Check permissions
const hasRole = useAuthStore((state) => state.hasRole('admin'));
const hasPermission = useAuthStore((state) => state.hasPermission('users:write'));
const hasAnyRole = useAuthStore((state) => state.hasAnyRole(['admin', 'moderator']));
const isInGroup = useAuthStore((state) => state.isInGroup('my-org'));Store Selectors
import {
selectUser,
selectIsAuthenticated,
selectAccessToken,
selectIsLoading,
selectUserRoles,
selectUserGroups,
} from '@/store/authStore';
const user = useAuthStore(selectUser);
const isAuthenticated = useAuthStore(selectIsAuthenticated);🎨 Styling
Components use Tailwind CSS classes. To customize styles:
- Override with custom classes:
<LoginPage className="my-custom-class" />- Create your own components using the hooks:
import { useLogin } from '@/hooks';
function CustomLoginForm() {
const login = useLogin();
// Your custom UI
}🔧 Advanced Usage
Custom API Client Configuration
// src/api/client.ts
import { apiClient } from '@/api/client';
// Add custom headers
apiClient.defaults.headers.common['X-Custom-Header'] = 'value';
// Add request interceptor
apiClient.interceptors.request.use((config) => {
// Custom logic
return config;
});Programmatic Navigation After Login
import { useLogin } from '@/hooks';
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const login = useLogin();
const navigate = useNavigate();
const handleLogin = async (credentials) => {
try {
await login.mutateAsync(credentials);
navigate('/dashboard'); // Redirect after login
} catch (error) {
// Handle error
}
};
}📖 Examples
See the /examples directory for complete working examples:
- Next.js 14 App Router
- Next.js 14 Pages Router
- React SPA with React Router
- Multi-tenant application
🤝 Contributing
Contributions welcome! Please read the contributing guide.
📄 License
MIT
