405 lines
9.8 KiB
Markdown
405 lines
9.8 KiB
Markdown
|
|
# Auth System Refactoring
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The authentication system has been completely refactored to improve code quality, maintainability, and user experience. This document outlines the changes and improvements made.
|
||
|
|
|
||
|
|
## Key Improvements
|
||
|
|
|
||
|
|
### 1. **Separation of Concerns**
|
||
|
|
- **Before**: Single 667-line monolithic component handling all auth logic
|
||
|
|
- **After**: Modular components with clear responsibilities:
|
||
|
|
- `AuthLayout`: Layout and styling
|
||
|
|
- `LoginForm`: Login form logic
|
||
|
|
- `EmailSetupForm`: Email validation form
|
||
|
|
- `OTPForm`: OTP verification form
|
||
|
|
- Custom hooks for business logic
|
||
|
|
- Utility functions for common operations
|
||
|
|
|
||
|
|
### 2. **Type Safety**
|
||
|
|
- **Before**: Extensive use of `any` types
|
||
|
|
- **After**: Comprehensive TypeScript interfaces and types:
|
||
|
|
- `LoginFormData`, `EmailValidationData`, `OTPData`
|
||
|
|
- `ProfileData`, `AuthState`, `AuthContextType`
|
||
|
|
- Proper API response types
|
||
|
|
- Component prop interfaces
|
||
|
|
|
||
|
|
### 3. **Form Validation**
|
||
|
|
- **Before**: Basic zod schema with unclear error messages
|
||
|
|
- **After**: Comprehensive validation with user-friendly messages:
|
||
|
|
- Username: 3-50 characters, required
|
||
|
|
- Password: 6-100 characters, required
|
||
|
|
- Email: Proper email format validation
|
||
|
|
- OTP: Exactly 6 digits, numbers only
|
||
|
|
|
||
|
|
### 4. **Error Handling**
|
||
|
|
- **Before**: Inconsistent error handling patterns
|
||
|
|
- **After**: Centralized error handling with:
|
||
|
|
- Consistent error messages
|
||
|
|
- Toast notifications
|
||
|
|
- Proper error boundaries
|
||
|
|
- Rate limiting for login attempts
|
||
|
|
|
||
|
|
### 5. **Accessibility**
|
||
|
|
- **Before**: Basic accessibility
|
||
|
|
- **After**: Enhanced accessibility with:
|
||
|
|
- Proper ARIA labels
|
||
|
|
- Keyboard navigation support
|
||
|
|
- Screen reader compatibility
|
||
|
|
- Focus management
|
||
|
|
- Error announcements
|
||
|
|
|
||
|
|
### 6. **Reusability**
|
||
|
|
- **Before**: Hardcoded components
|
||
|
|
- **After**: Highly reusable components:
|
||
|
|
- `FormField`: Reusable form input component
|
||
|
|
- `AuthLayout`: Reusable layout component
|
||
|
|
- Custom hooks for business logic
|
||
|
|
- Utility functions for common operations
|
||
|
|
|
||
|
|
## New File Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
types/
|
||
|
|
├── auth.ts # TypeScript interfaces and types
|
||
|
|
lib/
|
||
|
|
├── auth-utils.ts # Auth utility functions
|
||
|
|
hooks/
|
||
|
|
├── use-auth.ts # Custom auth hooks
|
||
|
|
components/
|
||
|
|
├── auth/
|
||
|
|
│ ├── index.ts # Component exports
|
||
|
|
│ ├── auth-layout.tsx # Reusable auth layout
|
||
|
|
│ ├── form-field.tsx # Reusable form field
|
||
|
|
│ ├── login-form.tsx # Login form component
|
||
|
|
│ ├── email-setup-form.tsx # Email setup form
|
||
|
|
│ └── otp-form.tsx # OTP verification form
|
||
|
|
app/[locale]/auth/
|
||
|
|
├── page.tsx # Main auth page
|
||
|
|
```
|
||
|
|
|
||
|
|
## Components
|
||
|
|
|
||
|
|
### AuthLayout
|
||
|
|
Reusable layout component for all auth pages.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { AuthLayout } from "@/components/auth";
|
||
|
|
|
||
|
|
<AuthLayout showSidebar={true}>
|
||
|
|
{/* Auth form content */}
|
||
|
|
</AuthLayout>
|
||
|
|
```
|
||
|
|
|
||
|
|
### FormField
|
||
|
|
Reusable form input component with built-in validation and accessibility.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { FormField } from "@/components/auth";
|
||
|
|
|
||
|
|
<FormField
|
||
|
|
label="Username"
|
||
|
|
name="username"
|
||
|
|
type="text"
|
||
|
|
placeholder="Enter your username"
|
||
|
|
error={errors.username?.message}
|
||
|
|
required
|
||
|
|
inputProps={{
|
||
|
|
size: "lg",
|
||
|
|
...register("username"),
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
### LoginForm
|
||
|
|
Complete login form with validation and error handling.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { LoginForm } from "@/components/auth";
|
||
|
|
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={(data) => console.log("Login successful", data)}
|
||
|
|
onError={(error) => console.error("Login failed", error)}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
### EmailSetupForm
|
||
|
|
Form for email validation and setup.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { EmailSetupForm } from "@/components/auth";
|
||
|
|
|
||
|
|
<EmailSetupForm
|
||
|
|
onSuccess={() => console.log("Email setup successful")}
|
||
|
|
onError={(error) => console.error("Email setup failed", error)}
|
||
|
|
onBack={() => console.log("Go back to login")}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
### OTPForm
|
||
|
|
OTP verification form with keyboard navigation.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { OTPForm } from "@/components/auth";
|
||
|
|
|
||
|
|
<OTPForm
|
||
|
|
onSuccess={() => console.log("OTP verification successful")}
|
||
|
|
onError={(error) => console.error("OTP verification failed", error)}
|
||
|
|
onResend={() => console.log("Resend OTP")}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Hooks
|
||
|
|
|
||
|
|
### useAuth
|
||
|
|
Main authentication hook providing login, logout, and token refresh functionality.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useAuth } from "@/hooks/use-auth";
|
||
|
|
|
||
|
|
const { login, logout, isAuthenticated, user, loading, error } = useAuth();
|
||
|
|
```
|
||
|
|
|
||
|
|
### useEmailValidation
|
||
|
|
Hook for email validation step.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useEmailValidation } from "@/hooks/use-auth";
|
||
|
|
|
||
|
|
const { validateEmail, loading, error } = useEmailValidation();
|
||
|
|
```
|
||
|
|
|
||
|
|
### useEmailSetup
|
||
|
|
Hook for email setup step.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useEmailSetup } from "@/hooks/use-auth";
|
||
|
|
|
||
|
|
const { setupEmail, loading, error } = useEmailSetup();
|
||
|
|
```
|
||
|
|
|
||
|
|
### useOTPVerification
|
||
|
|
Hook for OTP verification.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useOTPVerification } from "@/hooks/use-auth";
|
||
|
|
|
||
|
|
const { verifyOTP, loading, error } = useOTPVerification();
|
||
|
|
```
|
||
|
|
|
||
|
|
## Utilities
|
||
|
|
|
||
|
|
### Auth Utilities
|
||
|
|
Centralized utility functions for common auth operations.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import {
|
||
|
|
setAuthCookies,
|
||
|
|
setProfileCookies,
|
||
|
|
clearAllCookies,
|
||
|
|
isUserEligible,
|
||
|
|
getNavigationPath,
|
||
|
|
showAuthError,
|
||
|
|
showAuthSuccess,
|
||
|
|
loginRateLimiter
|
||
|
|
} from "@/lib/auth-utils";
|
||
|
|
```
|
||
|
|
|
||
|
|
### Rate Limiting
|
||
|
|
Built-in rate limiting to prevent brute force attacks.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// Check if user can attempt login
|
||
|
|
if (!loginRateLimiter.canAttempt(username)) {
|
||
|
|
const remainingTime = loginRateLimiter.getRemainingTime(username);
|
||
|
|
// Show lockout message
|
||
|
|
}
|
||
|
|
|
||
|
|
// Record failed attempt
|
||
|
|
loginRateLimiter.recordAttempt(username);
|
||
|
|
|
||
|
|
// Reset on successful login
|
||
|
|
loginRateLimiter.resetAttempts(username);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Validation Schemas
|
||
|
|
|
||
|
|
### Login Schema
|
||
|
|
```tsx
|
||
|
|
import { loginSchema } from "@/types/auth";
|
||
|
|
|
||
|
|
const schema = z.object({
|
||
|
|
username: z
|
||
|
|
.string()
|
||
|
|
.min(1, { message: "Username is required" })
|
||
|
|
.min(3, { message: "Username must be at least 3 characters" })
|
||
|
|
.max(50, { message: "Username must be less than 50 characters" }),
|
||
|
|
password: z
|
||
|
|
.string()
|
||
|
|
.min(1, { message: "Password is required" })
|
||
|
|
.min(6, { message: "Password must be at least 6 characters" })
|
||
|
|
.max(100, { message: "Password must be less than 100 characters" }),
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Email Validation Schema
|
||
|
|
```tsx
|
||
|
|
import { emailValidationSchema } from "@/types/auth";
|
||
|
|
|
||
|
|
const schema = z.object({
|
||
|
|
oldEmail: z
|
||
|
|
.string()
|
||
|
|
.min(1, { message: "Old email is required" })
|
||
|
|
.email({ message: "Please enter a valid email address" }),
|
||
|
|
newEmail: z
|
||
|
|
.string()
|
||
|
|
.min(1, { message: "New email is required" })
|
||
|
|
.email({ message: "Please enter a valid email address" }),
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### OTP Schema
|
||
|
|
```tsx
|
||
|
|
import { otpSchema } from "@/types/auth";
|
||
|
|
|
||
|
|
const schema = z.object({
|
||
|
|
otp: z
|
||
|
|
.string()
|
||
|
|
.length(6, { message: "OTP must be exactly 6 digits" })
|
||
|
|
.regex(/^\d{6}$/, { message: "OTP must contain only numbers" }),
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices Implemented
|
||
|
|
|
||
|
|
### 1. **Component Design**
|
||
|
|
- Single responsibility principle
|
||
|
|
- Props interface for type safety
|
||
|
|
- Default props where appropriate
|
||
|
|
- Proper error boundaries
|
||
|
|
|
||
|
|
### 2. **State Management**
|
||
|
|
- Custom hooks for business logic
|
||
|
|
- Local state for UI concerns
|
||
|
|
- Context for global auth state
|
||
|
|
- Proper loading states
|
||
|
|
|
||
|
|
### 3. **Error Handling**
|
||
|
|
- Consistent error patterns
|
||
|
|
- User-friendly error messages
|
||
|
|
- Proper error boundaries
|
||
|
|
- Toast notifications
|
||
|
|
|
||
|
|
### 4. **Performance**
|
||
|
|
- Memoized components where needed
|
||
|
|
- Efficient re-renders
|
||
|
|
- Proper dependency arrays
|
||
|
|
- Lazy loading for large components
|
||
|
|
|
||
|
|
### 5. **Security**
|
||
|
|
- Rate limiting for login attempts
|
||
|
|
- Input validation and sanitization
|
||
|
|
- Secure cookie handling
|
||
|
|
- XSS protection
|
||
|
|
|
||
|
|
### 6. **Accessibility**
|
||
|
|
- ARIA labels and descriptions
|
||
|
|
- Keyboard navigation
|
||
|
|
- Screen reader support
|
||
|
|
- Focus management
|
||
|
|
- Error announcements
|
||
|
|
|
||
|
|
## Migration Guide
|
||
|
|
|
||
|
|
### From Old LoginForm to New Components
|
||
|
|
|
||
|
|
**Before:**
|
||
|
|
```tsx
|
||
|
|
import LoginForm from "@/components/partials/auth/login-form";
|
||
|
|
|
||
|
|
<LoginForm />
|
||
|
|
```
|
||
|
|
|
||
|
|
**After:**
|
||
|
|
```tsx
|
||
|
|
import { AuthLayout, LoginForm } from "@/components/auth";
|
||
|
|
|
||
|
|
<AuthLayout>
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={handleSuccess}
|
||
|
|
onError={handleError}
|
||
|
|
/>
|
||
|
|
</AuthLayout>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Adding Custom Validation
|
||
|
|
|
||
|
|
**Before:**
|
||
|
|
```tsx
|
||
|
|
// Validation logic mixed with component
|
||
|
|
const schema = z.object({
|
||
|
|
username: z.string().min(1, { message: "Judul diperlukan" }),
|
||
|
|
password: z.string().min(4, { message: "Password must be at least 4 characters." }),
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**After:**
|
||
|
|
```tsx
|
||
|
|
// Centralized validation schemas
|
||
|
|
import { loginSchema } from "@/types/auth";
|
||
|
|
|
||
|
|
const {
|
||
|
|
register,
|
||
|
|
handleSubmit,
|
||
|
|
formState: { errors },
|
||
|
|
} = useForm<LoginFormData>({
|
||
|
|
resolver: zodResolver(loginSchema),
|
||
|
|
mode: "onChange",
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
The new components are designed to be easily testable:
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
// Example test for LoginForm
|
||
|
|
import { render, screen, fireEvent } from "@testing-library/react";
|
||
|
|
import { LoginForm } from "@/components/auth";
|
||
|
|
|
||
|
|
test("renders login form", () => {
|
||
|
|
const mockOnSuccess = jest.fn();
|
||
|
|
const mockOnError = jest.fn();
|
||
|
|
|
||
|
|
render(
|
||
|
|
<LoginForm
|
||
|
|
onSuccess={mockOnSuccess}
|
||
|
|
onError={mockOnError}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(screen.getByLabelText(/username/i)).toBeInTheDocument();
|
||
|
|
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Future Enhancements
|
||
|
|
|
||
|
|
1. **Multi-factor Authentication**: Support for additional MFA methods
|
||
|
|
2. **Social Login**: Integration with Google, Facebook, etc.
|
||
|
|
3. **Password Strength Indicator**: Visual feedback for password strength
|
||
|
|
4. **Remember Me**: Persistent login functionality
|
||
|
|
5. **Session Management**: Better session handling and timeout
|
||
|
|
6. **Audit Logging**: Track login attempts and security events
|
||
|
|
|
||
|
|
## Conclusion
|
||
|
|
|
||
|
|
The refactored auth system provides:
|
||
|
|
- Better code organization and maintainability
|
||
|
|
- Improved type safety and error handling
|
||
|
|
- Enhanced user experience and accessibility
|
||
|
|
- Better security with rate limiting
|
||
|
|
- Reusable components for future development
|
||
|
|
- Comprehensive documentation and testing support
|
||
|
|
|
||
|
|
This foundation makes it easier to add new features, maintain the codebase, and provide a better user experience.
|