React Token Auth

2.3.8 · active · verified Wed Apr 22

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.

Common errors

Warnings

Install

Imports

Quickstart

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

import { createAuthProvider } from 'react-token-auth';
import React, { FormEvent, useEffect } from 'react';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'; // Assuming react-router-dom for routing context

// Define the shape of your session object
type Session = { accessToken: string; refreshToken: string };

// 1. Create the auth provider instance
export const { useAuth, authFetch, login, logout } = createAuthProvider<Session>({
    getAccessToken: session => session.accessToken,
    // Use localStorage for web applications. Ensure it's available (e.g., client-side).
    storage: typeof window !== 'undefined' ? localStorage : undefined,
    onUpdateToken: async (token: { refreshToken: string }) => {
        // This function is called when the accessToken needs to be refreshed.
        // It must return a new session object { accessToken: string; refreshToken: string }
        try {
            const response = await fetch('/update-token', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ refreshToken: token.refreshToken }),
            });
            if (!response.ok) {
                const errorData = await response.json();
                console.error('Token refresh failed:', errorData);
                throw new Error('Failed to refresh token');
            }
            return response.json();
        } catch (error) {
            console.error('Network or server error during token refresh:', error);
            throw error;
        }
    },
    // Optional: Callback when a session is loaded from storage (hydration)
    onHydratation: session => {
        console.log('Session hydrated:', session);
    },
});

// A dummy component for registration
const Register = () => <div>Register Page</div>;

// 2. Example Login Component
const Login = () => {
    const onSubmit = async (e: FormEvent) => {
        e.preventDefault();
        // Simulate a login API call
        try {
            const response = await fetch('/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username: 'testuser', password: 'password' }), // Replace with actual credentials
            });
            if (!response.ok) {
                const errorData = await response.json();
                console.error('Login failed:', errorData);
                alert('Login failed!');
                return;
            }
            const session = await response.json();
            login(session); // Save the new session
            alert('Logged in successfully!');
        } catch (error) {
            console.error('Network error during login:', error);
            alert('Network error during login!');
        }
    };

    return (
        <form onSubmit={onSubmit}>
            <h2>Login</h2>
            <input type="text" placeholder="Username" />
            <input type="password" placeholder="Password" />
            <button type="submit">Login</button>
        </form>
    );
};

// A dummy dashboard component
const Dashboard = () => {
    const handleLogout = () => {
        logout();
        alert('Logged out!');
    };

    // Example of using authFetch to make authenticated requests
    const fetchData = async () => {
        try {
            const response = await authFetch('/api/protected-data');
            const data = await response.json();
            console.log('Protected data:', data);
            alert('Fetched protected data: ' + JSON.stringify(data));
        } catch (error) {
            console.error('Failed to fetch protected data:', error);
            alert('Failed to fetch protected data. Maybe token expired or invalid.');
        }
    };

    return (
        <div>
            <h2>Dashboard</h2>
            <button onClick={handleLogout}>Logout</button>
            <button onClick={fetchData}>Fetch Protected Data</button>
        </div>
    );
};

// 3. Main Router component using useAuth hook to manage routing based on auth state
const AppRouter = () => {
    const [logged, session] = useAuth(); // Get current auth state

    useEffect(() => {
        console.log('Auth state changed:', logged, session);
    }, [logged, session]);

    return (
        <BrowserRouter>
            <Switch>
                {!logged ? (
                    <>
                        <Route path="/register" component={Register} />
                        <Route path="/login" component={Login} />
                        <Redirect to="/login" />
                    </>
                ) : (
                    <>
                        <Route path="/dashboard" component={Dashboard} exact />
                        <Redirect to="/dashboard" />
                    </>
                )}
            </Switch>
        </BrowserRouter>
    );
};

// To run this example in a real React app, you would render <AppRouter /> in your ReactDOM.render()
// For demonstration purposes, we omit the ReactDOM.render() call.

view raw JSON →