Skip to content

Frontend PR-2: CSRF Token Handling & Request Interceptor #211

@kevalyq

Description

@kevalyq

📦 Sub-Issue of Epic #208

Part of: #208 (httpOnly Cookie Authentication Migration)
Priority: High
Area: Frontend, Security, CSRF
Repository: frontend

Goal

Implement CSRF token handling for all state-changing requests (POST, PUT, PATCH, DELETE) by fetching and including CSRF tokens from Laravel Sanctum.

Acceptance Criteria

  • CSRF token fetched from /sanctum/csrf-cookie before login
  • CSRF token automatically included in state-changing requests
  • Request interceptor handles CSRF token refresh on 419 (CSRF token mismatch)
  • CSRF token stored in cookie (managed by browser, not localStorage)
  • Error handling for CSRF failures
  • TypeScript types for CSRF responses
  • Tests verify CSRF token flow
  • Tests verify 419 retry logic
  • TypeScript strict mode passes
  • ESLint passes
  • All tests pass

Implementation Details

Files to modify:

  • src/services/csrf.ts (new file - CSRF token management)
  • src/services/api.ts (add CSRF interceptor)
  • src/services/authApi.ts (call CSRF endpoint before login)

Implementation:

// src/services/csrf.ts
export async function fetchCsrfToken(): Promise<void> {
  await fetch(`${getApiBaseUrl()}/sanctum/csrf-cookie`, {
    credentials: 'include',
  });
  // Token stored in XSRF-TOKEN cookie by Laravel
}

// src/services/api.ts
async function fetchWithCsrf(url: string, options: RequestInit): Promise<Response> {
  const response = await fetch(url, {
    ...options,
    credentials: 'include',
    headers: {
      ...options.headers,
      'X-XSRF-TOKEN': getCsrfTokenFromCookie(), // Read from cookie
    },
  });

  // Retry on CSRF failure
  if (response.status === 419) {
    await fetchCsrfToken();
    return fetch(url, {
      ...options,
      credentials: 'include',
      headers: {
        ...options.headers,
        'X-XSRF-TOKEN': getCsrfTokenFromCookie(),
      },
    });
  }

  return response;
}

Dependencies

Testing

npm test -- --testNamePattern="csrf"
npm run typecheck
npm run lint

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions