{"id":16670,"library":"react-token-auth","title":"React Token Auth","description":"The `react-token-auth` library, currently at version 2.3.8, offers a specialized solution for managing JWT `accessToken` and `refreshToken` pairs within React applications. Its primary function is to abstract the complexities of token storage (e.g., `localStorage` for web or custom async storage for React Native), synchronize the application's authentication state, and automatically handle token expiration and refreshing. A key feature is its `onUpdateToken` callback, which allows developers to integrate with their backend's token refresh endpoint, and its mechanism to prevent concurrent token update requests. While the author acknowledges that cookie-based sessions are generally preferred for web security, `react-token-auth` addresses specific use cases where client-side JWT storage is a requirement. The library provides a concise API including `createAuthProvider` to configure the system, `useAuth` to access the authentication state in components, and `authFetch` to automatically attach tokens to requests. The project appears actively maintained with regular updates and ships with TypeScript type definitions, making it suitable for modern React development practices.","status":"active","version":"2.3.8","language":"javascript","source_language":"en","source_url":"https://github.com/obabichev/react-token-auth","tags":["javascript","typescript"],"install":[{"cmd":"npm install react-token-auth","lang":"bash","label":"npm"},{"cmd":"yarn add react-token-auth","lang":"bash","label":"yarn"},{"cmd":"pnpm add react-token-auth","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Primarily designed for ESM and TypeScript; CommonJS require() pattern is incorrect for modern usage.","wrong":"const { createAuthProvider } = require('react-token-auth');","symbol":"createAuthProvider","correct":"import { createAuthProvider } from 'react-token-auth';"},{"note":"Destructured from the return value of `createAuthProvider` but typically imported directly for convenience.","symbol":"useAuth","correct":"import { useAuth } from 'react-token-auth';"},{"note":"Similarly, `login`, `logout`, and `authFetch` are exposed directly for module-level usage after `createAuthProvider` is called.","symbol":"login","correct":"import { login } from 'react-token-auth';"}],"quickstart":{"code":"import { createAuthProvider } from 'react-token-auth';\nimport React, { FormEvent, useEffect } from 'react';\nimport { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'; // Assuming react-router-dom for routing context\n\n// Define the shape of your session object\ntype Session = { accessToken: string; refreshToken: string };\n\n// 1. Create the auth provider instance\nexport const { useAuth, authFetch, login, logout } = createAuthProvider<Session>({\n    getAccessToken: session => session.accessToken,\n    // Use localStorage for web applications. Ensure it's available (e.g., client-side).\n    storage: typeof window !== 'undefined' ? localStorage : undefined,\n    onUpdateToken: async (token: { refreshToken: string }) => {\n        // This function is called when the accessToken needs to be refreshed.\n        // It must return a new session object { accessToken: string; refreshToken: string }\n        try {\n            const response = await fetch('/update-token', {\n                method: 'POST',\n                headers: { 'Content-Type': 'application/json' },\n                body: JSON.stringify({ refreshToken: token.refreshToken }),\n            });\n            if (!response.ok) {\n                const errorData = await response.json();\n                console.error('Token refresh failed:', errorData);\n                throw new Error('Failed to refresh token');\n            }\n            return response.json();\n        } catch (error) {\n            console.error('Network or server error during token refresh:', error);\n            throw error;\n        }\n    },\n    // Optional: Callback when a session is loaded from storage (hydration)\n    onHydratation: session => {\n        console.log('Session hydrated:', session);\n    },\n});\n\n// A dummy component for registration\nconst Register = () => <div>Register Page</div>;\n\n// 2. Example Login Component\nconst Login = () => {\n    const onSubmit = async (e: FormEvent) => {\n        e.preventDefault();\n        // Simulate a login API call\n        try {\n            const response = await fetch('/login', {\n                method: 'POST',\n                headers: { 'Content-Type': 'application/json' },\n                body: JSON.stringify({ username: 'testuser', password: 'password' }), // Replace with actual credentials\n            });\n            if (!response.ok) {\n                const errorData = await response.json();\n                console.error('Login failed:', errorData);\n                alert('Login failed!');\n                return;\n            }\n            const session = await response.json();\n            login(session); // Save the new session\n            alert('Logged in successfully!');\n        } catch (error) {\n            console.error('Network error during login:', error);\n            alert('Network error during login!');\n        }\n    };\n\n    return (\n        <form onSubmit={onSubmit}>\n            <h2>Login</h2>\n            <input type=\"text\" placeholder=\"Username\" />\n            <input type=\"password\" placeholder=\"Password\" />\n            <button type=\"submit\">Login</button>\n        </form>\n    );\n};\n\n// A dummy dashboard component\nconst Dashboard = () => {\n    const handleLogout = () => {\n        logout();\n        alert('Logged out!');\n    };\n\n    // Example of using authFetch to make authenticated requests\n    const fetchData = async () => {\n        try {\n            const response = await authFetch('/api/protected-data');\n            const data = await response.json();\n            console.log('Protected data:', data);\n            alert('Fetched protected data: ' + JSON.stringify(data));\n        } catch (error) {\n            console.error('Failed to fetch protected data:', error);\n            alert('Failed to fetch protected data. Maybe token expired or invalid.');\n        }\n    };\n\n    return (\n        <div>\n            <h2>Dashboard</h2>\n            <button onClick={handleLogout}>Logout</button>\n            <button onClick={fetchData}>Fetch Protected Data</button>\n        </div>\n    );\n};\n\n// 3. Main Router component using useAuth hook to manage routing based on auth state\nconst AppRouter = () => {\n    const [logged, session] = useAuth(); // Get current auth state\n\n    useEffect(() => {\n        console.log('Auth state changed:', logged, session);\n    }, [logged, session]);\n\n    return (\n        <BrowserRouter>\n            <Switch>\n                {!logged ? (\n                    <>\n                        <Route path=\"/register\" component={Register} />\n                        <Route path=\"/login\" component={Login} />\n                        <Redirect to=\"/login\" />\n                    </>\n                ) : (\n                    <>\n                        <Route path=\"/dashboard\" component={Dashboard} exact />\n                        <Redirect to=\"/dashboard\" />\n                    </>\n                )}\n            </Switch>\n        </BrowserRouter>\n    );\n};\n\n// To run this example in a real React app, you would render <AppRouter /> in your ReactDOM.render()\n// For demonstration purposes, we omit the ReactDOM.render() call.","lang":"typescript","description":"This quickstart demonstrates the core functionality: initializing the auth provider, implementing login/logout, using the `useAuth` hook for conditional rendering, and making authenticated API calls with `authFetch`."},"warnings":[{"fix":"While `react-token-auth` facilitates `localStorage` usage, consider using HTTP-only cookies for web applications where possible. If `localStorage` is necessary, implement robust Content Security Policies (CSPs) and regularly audit for XSS vulnerabilities.","message":"Storing JWT tokens in `localStorage` is generally considered less secure than HTTP-only cookie sessions due to vulnerability to Cross-Site Scripting (XSS) attacks. If an attacker injects malicious JavaScript, they can access and steal the tokens.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure your `onUpdateToken` implementation includes thorough error handling for network issues and non-2xx HTTP responses. Validate the structure of the returned session object (`{ accessToken: string; refreshToken: string }`) and gracefully handle parsing failures.","message":"The `onUpdateToken` function must correctly handle API responses and always return a new session object with `accessToken` and `refreshToken`. Incorrect responses (e.g., malformed JSON, missing tokens) can lead to an unauthenticated state or errors.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement retry logic with exponential backoff and a maximum number of retries for `onUpdateToken`. If refresh consistently fails, force a logout or redirect to the login page, informing the user about the issue.","message":"Failing to properly handle errors or network issues within the `onUpdateToken` callback can lead to an infinite loop of refresh attempts or leave the user in a permanently unauthenticated state without clear recovery.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Verify that your `Session` type correctly matches the structure of tokens returned by your backend and that `getAccessToken` accurately points to the access token. Ensure `login()` is called with a valid session object.","cause":"The `getAccessToken` property in `createAuthProvider` is misconfigured, or the session object passed to `login` is `null`/`undefined` or lacks the expected `accessToken` field.","error":"TypeError: Cannot read properties of undefined (reading 'accessToken')"},{"fix":"Check your network connection and server status. Verify the URLs used in `fetch` calls. Ensure your server correctly handles CORS preflight requests and has the expected endpoints (`/login`, `/update-token`, etc.).","cause":"A network request initiated by `onUpdateToken`, `login`, `logout`, or `authFetch` failed due to network connectivity issues, incorrect URL, CORS policies, or an unreachable server.","error":"Uncaught (in promise) TypeError: Failed to fetch"},{"fix":"When using `react-token-auth` in SSR or React Native, provide a custom `storage` implementation to `createAuthProvider` that defers to `localStorage` only on the client or uses an appropriate async storage solution.","cause":"The library is configured to use `localStorage` but is being run in a server-side rendering (SSR) environment or React Native without a compatible storage shim.","error":"ReferenceError: localStorage is not defined"},{"fix":"Review the `onUpdateToken` implementation to ensure it has robust error handling and a clear exit strategy for persistent failures, preventing recursive calls without resolution. Implement safeguards like retry limits.","cause":"Often indicative of an infinite loop within the token refresh logic. This can happen if `onUpdateToken` repeatedly fails in a way that triggers itself again without a circuit breaker or proper error exit.","error":"RangeError: Maximum call stack size exceeded"}],"ecosystem":"npm"}