UNPKG

@csrf-armor/nextjs

Version:

CSRF protection middleware for Next.js applications

221 lines (219 loc) 6.1 kB
import { CsrfClientConfig } from "./client.js"; import * as react_jsx_runtime0 from "react/jsx-runtime"; //#region src/client/react.d.ts /** * Type definition for the CSRF context value provided by CsrfProvider. * * @public */ interface CsrfContextValue { /** Current CSRF token value, null if not available */ csrfToken: string | null; /** Function to manually refresh the token from storage */ updateToken: () => void; /** Enhanced fetch function with automatic CSRF token handling */ csrfFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>; } /** * React context provider for CSRF token management. * * Provides centralized CSRF token state management for React components, * with automatic token refresh on route changes and enhanced fetch capabilities. * * **Features:** * - Automatic token retrieval and state synchronization * - Route change detection with automatic token refresh * - Enhanced fetch function with built-in CSRF protection * - Browser history integration (back/forward navigation) * - Optimized re-renders with memoization * * **Best Practices:** * - Place near the root of your component tree * - Use with Next.js App Router for automatic route detection * - Combine with server-side CSRF middleware for complete protection * * @public * @param children - React components that will have access to CSRF context * @param config - Optional CSRF client configuration * * @example * ```tsx * // app/layout.tsx - Application-wide CSRF provider * import { CsrfProvider } from '@csrf-armor/nextjs/client'; * * export default function RootLayout({ * children, * }: { * children: React.ReactNode; * }) { * return ( * <html lang="en"> * <body> * <CsrfProvider config={{ * cookieName: 'csrf-token', * headerName: 'x-csrf-token' * }}> * {children} * </CsrfProvider> * </body> * </html> * ); * } * ``` * * @example * ```tsx * // components/ProtectedForm.tsx - Using CSRF in forms * import { useCsrf } from '@csrf-armor/nextjs/client'; * * export function ProtectedForm() { * const { csrfToken, csrfFetch } = useCsrf(); * * const handleSubmit = async (formData: FormData) => { * try { * const response = await csrfFetch('/api/submit', { * method: 'POST', * body: formData * }); * * if (response.ok) { * console.log('Form submitted successfully'); * } * } catch (error) { * console.error('Submission failed:', error); * } * }; * * return ( * <form action={handleSubmit}> * {csrfToken && ( * <input type="hidden" name="csrf_token" value={csrfToken} /> * )} * <button type="submit">Submit</button> * </form> * ); * } * ``` */ declare function CsrfProvider({ children, config }: Readonly<{ children: React.ReactNode; config?: CsrfClientConfig; }>): react_jsx_runtime0.JSX.Element; /** * React hook for accessing CSRF token and utilities. * * Provides access to the current CSRF token and related utilities from the * nearest CsrfProvider. Must be used within a component tree wrapped by CsrfProvider. * * **Return Value:** * - `csrfToken`: Current token string (null if not available) * - `updateToken`: Function to manually refresh token from storage * - `csrfFetch`: Enhanced fetch with automatic CSRF headers * * @public * @returns CSRF context value with token and utilities * @throws Error if used outside of CsrfProvider * * @example * ```tsx * import { useCsrf } from '@csrf-armor/nextjs/client'; * * function MyComponent() { * const { csrfToken, csrfFetch, updateToken } = useCsrf(); * * const handleSubmit = async (data: FormData) => { * try { * const response = await csrfFetch('/api/submit', { * method: 'POST', * body: data * }); * console.log('Success:', await response.json()); * } catch (error) { * console.error('Failed:', error); * // Optionally refresh token and retry * updateToken(); * } * }; * * return ( * <div> * <p>Current token: {csrfToken || 'Not available'}</p> * <button onClick={() => updateToken()}> * Refresh Token * </button> * </div> * ); * } * ``` * * @example * ```tsx * // Using with forms and hidden inputs * function ContactForm() { * const { csrfToken } = useCsrf(); * * return ( * <form method="post" action="/api/contact"> * {csrfToken && ( * <input type="hidden" name="csrf_token" value={csrfToken} /> * )} * <input name="email" type="email" required /> * <textarea name="message" required /> * <button type="submit">Send Message</button> * </form> * ); * } * ``` * * @example * ```tsx * // Error handling with automatic retry * function DataUploader() { * const { csrfFetch, updateToken } = useCsrf(); * * const uploadFile = async (file: File) => { * const formData = new FormData(); * formData.append('file', file); * * try { * const response = await csrfFetch('/api/upload', { * method: 'POST', * body: formData * }); * * if (!response.ok) { * throw new Error(`HTTP ${response.status}`); * } * * return await response.json(); * } catch (error) { * if (error.message.includes('403')) { * // CSRF token might be stale, refresh and retry once * updateToken(); * await new Promise(resolve => setTimeout(resolve, 100)); * return uploadFile(file); // Recursive retry * } * throw error; * } * }; * * return ( * <input * type="file" * onChange={(e) => { * if (e.target.files?.[0]) { * uploadFile(e.target.files[0]); * } * }} * /> * ); * } * ``` */ declare function useCsrf(): CsrfContextValue; //#endregion export { CsrfProvider, useCsrf }; //# sourceMappingURL=react.d.ts.map