@gsarthak783/accesskit-react
Version:
React SDK for AccessKit Authentication System
747 lines (604 loc) โข 18.2 kB
Markdown
# @gsarthak783/accesskit-react
React SDK for AccessKit Authentication System - Ready-to-use hooks and components for React applications.
[](https://badge.fury.io/js/@gsarthak783%2Faccesskit-react)
[](https://opensource.org/licenses/MIT)
## ๐ Quick Start
### Installation
```bash
npm install @gsarthak783/accesskit-react @gsarthak783/accesskit-auth
```
### Basic Usage
Wrap your app with the `AuthProvider`:
```jsx
import { AuthProvider } from '@gsarthak783/accesskit-react';
function App() {
const authConfig = {
projectId: 'your-project-id',
apiKey: 'your-api-key'
};
return (
<AuthProvider config={authConfig}>
{/* Your app components */}
</AuthProvider>
);
}
```
### Using Auth in Components
```jsx
import { useAuth } from '@gsarthak783/accesskit-react';
function MyComponent() {
const { user, isAuthenticated, isLoading, login, logout } = useAuth();
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return (
<button onClick={() => login('user@example.com', 'password')}>
Login
</button>
);
}
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
<button onClick={logout}>Logout</button>
</div>
);
}
```
### Persistent Authentication
The SDK automatically maintains authentication state across page refreshes, similar to Firebase Auth.
```jsx
function App() {
const { user, isAuthenticated, isLoading } = useAuth();
// On initial load and page refresh:
// 1. isLoading is true while checking stored tokens
// 2. If valid tokens exist, user is automatically logged in
// 3. isLoading becomes false once check is complete
if (isLoading) {
return <div>Checking authentication...</div>;
}
return (
<div>
{isAuthenticated ? (
<div>
Welcome back, {user.firstName}!
{/* User stays logged in even after page refresh */}
</div>
) : (
<div>Please log in</div>
)}
</div>
);
}
```
#### How Persistence Works
1. **Token Storage**: Access and refresh tokens are stored in localStorage by default
2. **Automatic Verification**: On app initialization, stored tokens are verified
3. **Seamless Experience**: Valid tokens = user stays logged in across sessions
4. **Token Refresh**: Expired access tokens are automatically refreshed using the refresh token
#### Example: Protected Routes with Persistence
```jsx
function ProtectedRoute({ children }) {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) {
// Important: Show loading while checking auth state
return <LoadingSpinner />;
}
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return children;
}
// Usage
function App() {
return (
<AuthProvider config={config}>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
</Routes>
</AuthProvider>
);
}
```
#### Handling Different Scenarios
```jsx
function AuthStatus() {
const { user, isAuthenticated, isLoading, logout } = useAuth();
useEffect(() => {
if (isAuthenticated && user) {
console.log('User restored from previous session:', user.email);
}
}, [isAuthenticated, user]);
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <div>Not logged in</div>;
}
return (
<div>
<p>Logged in as: {user.email}</p>
<p>Session persists across refreshes</p>
<button onClick={logout}>Logout</button>
</div>
);
}
```
## ๐ฏ Get Your API Keys
1. Visit the [AccessKit Dashboard](https://access-kit.vercel.app/)
2. Create an account or login
3. Create a new project
4. Copy your Project ID and API Key from the project settings
## ๐ช useAuth Hook
The `useAuth` hook provides all authentication functionality:
```typescript
interface AuthContextType {
// State
user: User | null;
isLoading: boolean;
isAuthenticated: boolean;
// Actions
login: (email: string, password: string) => Promise<void>;
register: (userData: RegisterData) => Promise<void>;
logout: () => Promise<void>;
updateProfile: (userData: Partial<User>) => Promise<void>;
requestPasswordReset: (email: string) => Promise<void>;
resetPassword: (token: string, password: string) => Promise<void>;
verifyEmail: (token: string) => Promise<void>;
// Direct SDK access
client: AuthClient;
}
```
### Registration
```jsx
function SignupForm() {
const { register, isLoading } = useAuth();
const handleSubmit = async (formData) => {
try {
await register({
email: formData.email,
password: formData.password,
firstName: formData.firstName,
lastName: formData.lastName,
username: formData.username,
customFields: { role: 'user' }
});
// User is automatically logged in after registration
} catch (error) {
console.error('Registration failed:', error);
}
};
return (
<form onSubmit={handleSubmit}>
{/* Your form fields */}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Creating Account...' : 'Sign Up'}
</button>
</form>
);
}
```
### Profile Management
```jsx
function UserProfile() {
const { user, updateProfile } = useAuth();
const handleUpdate = async (newData) => {
try {
await updateProfile(newData);
// User state is automatically updated
} catch (error) {
console.error('Update failed:', error);
}
};
return (
<div>
<h2>{user.firstName} {user.lastName}</h2>
<p>{user.email}</p>
<button onClick={() => handleUpdate({ firstName: 'NewName' })}>
Update Name
</button>
</div>
);
}
```
## ๐งฉ Ready-to-Use Components
### LoginForm Component
A complete login form with validation:
```jsx
import { LoginForm } from '@gsarthak783/accesskit-react';
function LoginPage() {
return (
<div className="login-page">
<LoginForm
onSuccess={() => console.log('Login successful!')}
onError={(error) => console.error('Login failed:', error)}
className="custom-login-form"
buttonText="Sign In"
showSignupLink={true}
onSignupClick={() => navigate('/signup')}
/>
</div>
);
}
```
### LoginForm Props
```typescript
interface LoginFormProps {
onSuccess?: () => void;
onError?: (error: Error) => void;
className?: string;
buttonText?: string;
showSignupLink?: boolean;
onSignupClick?: () => void;
}
```
## โ๏ธ AuthProvider Configuration
```jsx
<AuthProvider
config={{
projectId: 'your-project-id',
apiKey: 'your-api-key',
baseUrl: 'https://access-kit-server.vercel.app/api/project-users', // Optional, defaults to this
timeout: 10000 // Optional, request timeout in ms
}}
storage={customStorage} // Optional, custom token storage
autoInitialize={true} // Optional, auto-check authentication on mount
>
<App />
</AuthProvider>
```
### Custom Storage
```jsx
import { AuthProvider } from '@gsarthak783/accesskit-react';
// Custom storage for React Native or other environments
const customStorage = {
getItem: (key) => AsyncStorage.getItem(key),
setItem: (key, value) => AsyncStorage.setItem(key, value),
removeItem: (key) => AsyncStorage.removeItem(key)
};
<AuthProvider config={config} storage={customStorage}>
<App />
</AuthProvider>
```
## ๐จ Advanced Usage
### Account Security
```jsx
import { useAuth } from '@gsarthak783/accesskit-react';
function AccountSettings() {
const { user, updatePassword, updateEmail, reauthenticateWithCredential } = useAuth();
const [isReauthenticated, setIsReauthenticated] = useState(false);
// Change password
const handlePasswordChange = async (currentPassword, newPassword) => {
try {
await updatePassword(currentPassword, newPassword);
alert('Password updated successfully! Please login again.');
// User will be logged out automatically
} catch (error) {
alert(`Error: ${error.message}`);
}
};
// Update email
const handleEmailUpdate = async (newEmail, password) => {
try {
const result = await updateEmail(newEmail, password);
alert(`Email updated to ${result.email}. Please verify your new email.`);
} catch (error) {
alert(`Error: ${error.message}`);
}
};
// Reauthenticate before sensitive operations
const handleReauthentication = async (password) => {
try {
const result = await reauthenticateWithCredential(password);
setIsReauthenticated(true);
console.log('Reauthenticated at:', result.authenticatedAt);
// Now you can show sensitive settings
} catch (error) {
alert('Invalid password');
}
};
return (
<div>
{!isReauthenticated ? (
<form onSubmit={(e) => {
e.preventDefault();
handleReauthentication(e.target.password.value);
}}>
<input type="password" name="password" placeholder="Enter password to continue" />
<button type="submit">Verify Identity</button>
</form>
) : (
<div>
{/* Show sensitive account settings */}
</div>
)}
</div>
);
}
```
### Protected Routes
```jsx
import { useAuth } from '@gsarthak783/accesskit-react';
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ children }) {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) {
return <div>Loading...</div>;
}
return isAuthenticated ? children : <Navigate to="/login" />;
}
// Usage
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
```
### Role-Based Access
```jsx
function AdminPanel() {
const { user, isAuthenticated } = useAuth();
if (!isAuthenticated || user.customFields?.role !== 'admin') {
return <div>Access denied</div>;
}
return <div>Admin content</div>;
}
```
### Form Validation
```jsx
function RegisterForm() {
const { register, isLoading } = useAuth();
const [formData, setFormData] = useState({
email: '',
password: '',
firstName: '',
lastName: ''
});
const [errors, setErrors] = useState({});
const validateForm = () => {
const newErrors = {};
if (!formData.email.includes('@')) {
newErrors.email = 'Invalid email';
}
if (formData.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) return;
try {
await register(formData);
} catch (error) {
setErrors({ submit: error.message });
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
<input
type="password"
value={formData.password}
onChange={(e) => setFormData({...formData, password: e.target.value})}
placeholder="Password"
/>
{errors.password && <span className="error">{errors.password}</span>}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Registering...' : 'Register'}
</button>
{errors.submit && <div className="error">{errors.submit}</div>}
</form>
);
}
```
### Direct SDK Access
```jsx
function AdvancedComponent() {
const { client } = useAuth();
const handleAdminAction = async () => {
try {
// Direct access to the underlying AuthClient
const users = await client.getAllUsers({
page: 1,
limit: 10,
status: 'active'
});
console.log('Users:', users);
} catch (error) {
console.error('Failed to fetch users:', error);
}
};
return (
<button onClick={handleAdminAction}>
Get All Users (Admin)
</button>
);
}
```
## ๐จ Styling
The components come with minimal styling. You can customize them using CSS classes:
```css
/* Custom styles for LoginForm */
.custom-login-form {
max-width: 400px;
margin: 0 auto;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.custom-login-form input {
width: 100%;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.custom-login-form button {
width: 100%;
padding: 0.75rem;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.custom-login-form button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
```
## ๐ Security Best Practices
### Environment Variables
Never expose API keys in frontend code:
```javascript
// โ Don't do this
const config = {
projectId: 'proj_123',
apiKey: 'sk_live_abc123' // Never expose secret keys in frontend!
};
// โ
Do this instead
const config = {
projectId: process.env.REACT_APP_PROJECT_ID,
// Use public keys only in frontend, manage auth server-side
};
```
### Secure Token Storage
```jsx
// For React Native or when you need secure storage
import * as SecureStore from 'expo-secure-store';
const secureStorage = {
getItem: async (key) => await SecureStore.getItemAsync(key),
setItem: async (key, value) => await SecureStore.setItemAsync(key, value),
removeItem: async (key) => await SecureStore.deleteItemAsync(key)
};
<AuthProvider config={config} storage={secureStorage}>
<App />
</AuthProvider>
```
### Input Validation
Always validate user inputs:
```jsx
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const validatePassword = (password) => {
return password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/\d/.test(password);
};
```
## ๐งช Examples
### Complete Login/Register Flow
```jsx
import React, { useState } from 'react';
import { useAuth, LoginForm } from '@gsarthak783/accesskit-react';
function AuthFlow() {
const { isAuthenticated, user, logout } = useAuth();
const [showLogin, setShowLogin] = useState(true);
if (isAuthenticated) {
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
<button onClick={logout}>Logout</button>
</div>
);
}
return (
<div>
<div className="auth-tabs">
<button
onClick={() => setShowLogin(true)}
className={showLogin ? 'active' : ''}
>
Login
</button>
<button
onClick={() => setShowLogin(false)}
className={!showLogin ? 'active' : ''}
>
Register
</button>
</div>
{showLogin ? (
<LoginForm
onSuccess={() => console.log('Logged in!')}
showSignupLink={true}
onSignupClick={() => setShowLogin(false)}
/>
) : (
<RegisterForm onSuccess={() => setShowLogin(true)} />
)}
</div>
);
}
```
### TypeScript Support
```tsx
import { useAuth } from '@gsarthak783/accesskit-react';
import type { User } from '@gsarthak783/accesskit-auth';
interface UserProfileProps {
onUpdate?: (user: User) => void;
}
const UserProfile: React.FC<UserProfileProps> = ({ onUpdate }) => {
const { user, updateProfile } = useAuth();
const handleUpdate = async (data: Partial<User>) => {
try {
const updatedUser = await updateProfile(data);
onUpdate?.(updatedUser);
} catch (error) {
console.error('Update failed:', error);
}
};
return (
<div>
<h2>{user?.firstName} {user?.lastName}</h2>
<button onClick={() => handleUpdate({ firstName: 'New Name' })}>
Update Profile
</button>
</div>
);
};
```
## ๐ Support
- **Live Demo**: [https://access-kit.vercel.app/](https://access-kit.vercel.app/)
- **npm Package**: [https://npmjs.com/package/@gsarthak783/accesskit-react](https://npmjs.com/package/@gsarthak783/accesskit-react)
- **Core SDK**: [https://npmjs.com/package/@gsarthak783/accesskit-auth](https://npmjs.com/package/@gsarthak783/accesskit-auth)
## ๐ License
MIT License - see [LICENSE](./LICENSE) file for details.
## ๐ Changelog
### 1.2.2 (Latest)
- Updated to use @gsarthak783/accesskit-auth v1.2.2 with critical token storage fix
- Tokens are now properly saved during login/register
### 1.2.1
- Fixed authentication persistence across page refreshes
- Improved integration with core SDK's initialization flow
- Better handling of loading states during auth checks
### 1.2.0
- Added support for account security methods
- Exposed `updatePassword`, `updateEmail`, and `reauthenticateWithCredential` via useAuth hook
### Version 1.1.0
- Simplified AuthProvider using onAuthStateChange from core SDK
- Removed autoInitialize prop (always auto-initializes now)
- Automatic auth state persistence across page refreshes
- Improved TypeScript types
### Version 1.0.5
- Updated to use @gsarthak783/accesskit-auth v1.0.5
---
Built with โค๏ธ by the AccessKit Team