mediahub-fe/docs/AUTH_REFACTOR.md

9.8 KiB

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.

import { AuthLayout } from "@/components/auth";

<AuthLayout showSidebar={true}>
  {/* Auth form content */}
</AuthLayout>

FormField

Reusable form input component with built-in validation and accessibility.

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.

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.

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.

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.

import { useAuth } from "@/hooks/use-auth";

const { login, logout, isAuthenticated, user, loading, error } = useAuth();

useEmailValidation

Hook for email validation step.

import { useEmailValidation } from "@/hooks/use-auth";

const { validateEmail, loading, error } = useEmailValidation();

useEmailSetup

Hook for email setup step.

import { useEmailSetup } from "@/hooks/use-auth";

const { setupEmail, loading, error } = useEmailSetup();

useOTPVerification

Hook for OTP verification.

import { useOTPVerification } from "@/hooks/use-auth";

const { verifyOTP, loading, error } = useOTPVerification();

Utilities

Auth Utilities

Centralized utility functions for common auth operations.

import { 
  setAuthCookies, 
  setProfileCookies, 
  clearAllCookies,
  isUserEligible,
  getNavigationPath,
  showAuthError,
  showAuthSuccess,
  loginRateLimiter
} from "@/lib/auth-utils";

Rate Limiting

Built-in rate limiting to prevent brute force attacks.

// 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

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

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

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:

import LoginForm from "@/components/partials/auth/login-form";

<LoginForm />

After:

import { AuthLayout, LoginForm } from "@/components/auth";

<AuthLayout>
  <LoginForm
    onSuccess={handleSuccess}
    onError={handleError}
  />
</AuthLayout>

Adding Custom Validation

Before:

// 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:

// 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:

// 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.