npm package: @gsarthak783/accesskit-react
Description: React SDK for AccessKit Authentication System
Author: AccessKit Team
Latest version: 1.2.6

# @gsarthak783/accesskit-react

React SDK for AccessKit Authentication System - Ready-to-use hooks and components for React applications.

[![npm version](https://badge.fury.io/js/@gsarthak783%2Faccesskit-react.svg)](https://badge.fury.io/js/@gsarthak783%2Faccesskit-react)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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