{"version":3,"file":"useCSRFToken.mjs","sources":["../../../../src/lib/security/hooks/useCSRFToken.ts"],"sourcesContent":["/**\n * @fileoverview useCSRFToken Hook\n * @module @/lib/security/hooks/useCSRFToken\n *\n * React hook for CSRF token management with automatic\n * header generation and form input props.\n *\n * @author Harbor Security Team\n * @version 1.0.0\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport type React from 'react';\nimport type { UseCSRFTokenResult } from '../types';\nimport { CSRFProtection } from '../csrf-protection';\n\n/**\n * Options for the useCSRFToken hook\n */\nexport interface UseCSRFTokenOptions {\n  /** Auto-refresh token at this interval (ms) */\n  refreshInterval?: number;\n  /** Whether to auto-initialize CSRF protection */\n  autoInitialize?: boolean;\n}\n\n/**\n * Hook for CSRF token management\n *\n * Provides CSRF token values, headers for fetch requests,\n * and form input props for traditional form submissions.\n *\n * @param options - Hook options\n * @returns CSRF token operations and values\n *\n * @example\n * ```tsx\n * function SecureForm() {\n *   const { token, formInputProps, headers, regenerate } = useCSRFToken();\n *\n *   const handleSubmit = async (e: FormEvent) => {\n *     e.preventDefault();\n *     const formData = new FormData(e.currentTarget);\n *\n *     await fetch('/api/submit', {\n *       method: 'POST',\n *       headers,\n *       body: formData,\n *     });\n *   };\n *\n *   return (\n *     <form onSubmit={handleSubmit}>\n *       <input {...formInputProps} />\n *       <input type=\"text\" name=\"data\" />\n *       <button type=\"submit\">Submit</button>\n *     </form>\n *   );\n * }\n * ```\n */\nexport function useCSRFToken(options: UseCSRFTokenOptions = {}): UseCSRFTokenResult {\n  const { refreshInterval, autoInitialize = true } = options;\n\n  // State\n  const [token, setToken] = useState<string>('');\n  const [isInitialized, setIsInitialized] = useState(false);\n\n  // Get configuration\n  const config = CSRFProtection.getConfig();\n  const { headerName } = config;\n  const { fieldName } = config;\n\n  // Initialize CSRF protection\n  useEffect(() => {\n    if (!autoInitialize) {\n      return;\n    }\n\n    const init = (): void => {\n      if (!CSRFProtection.isInitialized()) {\n        CSRFProtection.initialize();\n      }\n\n      const currentToken = CSRFProtection.getToken();\n      setToken(currentToken);\n      setIsInitialized(true);\n    };\n\n    init();\n  }, [autoInitialize]);\n\n  // Regenerate token function\n  const regenerate = useCallback(async (): Promise<string> => {\n    const newToken = CSRFProtection.regenerateToken();\n    setToken(newToken);\n    return Promise.resolve(newToken);\n  }, []);\n\n  // Set up auto-refresh interval\n  useEffect(() => {\n    if (\n      !isInitialized ||\n      refreshInterval === undefined ||\n      refreshInterval === null ||\n      refreshInterval <= 0\n    ) {\n      return;\n    }\n\n    const intervalId = setInterval(() => {\n      void regenerate();\n    }, refreshInterval);\n\n    return () => clearInterval(intervalId);\n  }, [isInitialized, refreshInterval, regenerate]);\n\n  // Memoized headers object\n  const headers = useMemo(\n    () => ({\n      [headerName]: token,\n    }),\n    [headerName, token]\n  );\n\n  // Memoized form input props\n  const formInputProps = useMemo(\n    () => ({\n      type: 'hidden' as const,\n      name: fieldName,\n      value: token,\n    }),\n    [fieldName, token]\n  );\n\n  return {\n    token,\n    headerName,\n    fieldName,\n    headers,\n    regenerate,\n    formInputProps,\n  };\n}\n\n/**\n * Hook for creating secure fetch with automatic CSRF token injection\n *\n * Note: This hook intentionally returns a raw fetch wrapper because:\n * 1. This IS the CSRF security layer that wraps fetch with token injection\n * 2. Users may need drop-in fetch replacement for existing code\n * 3. The apiClient already has CSRF protection built-in\n *\n * For new API calls, prefer using apiClient which has CSRF built-in.\n * @see {@link @/lib/api/api-client} for the recommended API client\n *\n * @example\n * ```tsx\n * function DataFetcher() {\n *   const secureFetch = useSecureFetch();\n *\n *   const submitData = async (data: object) => {\n *     const response = await secureFetch('/api/data', {\n *       method: 'POST',\n *       body: JSON.stringify(data),\n *       headers: {\n *         'Content-Type': 'application/json',\n *       },\n *     });\n *\n *     return response.json();\n *   };\n *\n *   // ...\n * }\n * ```\n */\nexport function useSecureFetch(): typeof fetch {\n  const { headers: csrfHeaders } = useCSRFToken();\n\n  return useCallback(\n    async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n      const method = init?.method?.toUpperCase() ?? 'GET';\n\n      // Only add CSRF token for state-changing methods\n      if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n        const headers = new Headers(init?.headers);\n\n        // Add CSRF headers\n        for (const [key, value] of Object.entries(csrfHeaders)) {\n          if (!headers.has(key)) {\n            headers.set(key, value);\n          }\n        }\n\n        // Raw fetch is intentional - this hook injects CSRF tokens\n        return fetch(input, {\n          ...init,\n          headers,\n        });\n      }\n\n      // Raw fetch is intentional - this wrapper applies CSRF protection\n      return fetch(input, init);\n    },\n    [csrfHeaders]\n  );\n}\n\n/**\n * Hook for creating a secure form submission handler\n *\n * @example\n * ```tsx\n * function ContactForm() {\n *   const { handleSubmit, isSubmitting, error } = useSecureFormSubmit({\n *     action: '/api/contact',\n *     onSuccess: () => console.log('Submitted!'),\n *   });\n *\n *   return (\n *     <form onSubmit={handleSubmit}>\n *       <input name=\"email\" type=\"email\" />\n *       <button disabled={isSubmitting}>Send</button>\n *       {error && <p className=\"error\">{error}</p>}\n *     </form>\n *   );\n * }\n * ```\n */\nexport function useSecureFormSubmit(options: {\n  action: string;\n  method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n  onSuccess?: (response: Response) => void;\n  onError?: (error: Error) => void;\n}): {\n  handleSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;\n  isSubmitting: boolean;\n  error: string | null;\n} {\n  const { action, method = 'POST', onSuccess, onError } = options;\n  const { headers: csrfHeaders, formInputProps } = useCSRFToken();\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n\n  const handleSubmit = useCallback(\n    async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {\n      event.preventDefault();\n      setIsSubmitting(true);\n      setError(null);\n\n      try {\n        const form = event.currentTarget;\n        const formData = new FormData(form);\n\n        // Ensure CSRF token is in form data\n        if (!formData.has(formInputProps.name)) {\n          formData.append(formInputProps.name, formInputProps.value);\n        }\n\n        // Raw fetch is intentional - this hook handles CSRF protection for forms\n        const response = await fetch(action, {\n          method,\n          body: formData,\n          headers: csrfHeaders,\n        });\n\n        if (!response.ok) {\n          throw new Error(`Request failed: ${response.status}`);\n        }\n\n        onSuccess?.(response);\n      } catch (err) {\n        const errorMessage = err instanceof Error ? err.message : 'Form submission failed';\n        setError(errorMessage);\n        onError?.(err instanceof Error ? err : new Error(errorMessage));\n      } finally {\n        setIsSubmitting(false);\n      }\n    },\n    [action, method, csrfHeaders, formInputProps, onSuccess, onError]\n  );\n\n  return {\n    handleSubmit,\n    isSubmitting,\n    error,\n  };\n}\n"],"names":["useCSRFToken","options","refreshInterval","autoInitialize","token","setToken","useState","isInitialized","setIsInitialized","config","CSRFProtection","headerName","fieldName","useEffect","currentToken","regenerate","useCallback","newToken","intervalId","headers","useMemo","formInputProps","useSecureFetch","csrfHeaders","input","init","method","key","value","useSecureFormSubmit","action","onSuccess","onError","isSubmitting","setIsSubmitting","error","setError","event","form","formData","response","err","errorMessage"],"mappings":"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"}