Redux Token Auth
Redux Token Auth is a specialized JavaScript library designed to facilitate client-side authentication for React and Redux applications integrating with Rails backends that utilize Devise Token Auth. Currently at version 0.19.0, it provides a comprehensive set of Redux Thunk actions for core authentication flows, including user registration, sign-in, sign-out, and token verification, alongside a dedicated Redux reducer to manage the authentication state. It also offers a higher-order component for safeguarding application routes, requiring `react-router v4.0.0+` for proper functionality. The library mandates the presence of `redux-thunk` middleware and manages user credentials by leveraging `localStorage` in browser environments, with `AsyncStorage` support for React Native applications planned for future releases. Its primary differentiator lies in its tailored API and conventions that align directly with the Devise Token Auth ecosystem.
Common errors
-
Error: Actions may not have an undefined 'type' property. Have you misspelled a constant?
cause This error often occurs when Redux Thunk middleware is not correctly applied to the Redux store, preventing `redux-token-auth`'s asynchronous actions from being properly handled.fixEnsure `redux-thunk` is installed and passed to `applyMiddleware` when creating your Redux store, e.g., `createStore(rootReducer, applyMiddleware(thunk))`. -
TypeError: reduxTokenAuthReducer is not a function
cause Attempting to use `require` for `reduxTokenAuthReducer` in an ESM-first environment, or incorrect destructuring of the named export.fixUse ESM `import { reduxTokenAuthReducer } from 'redux-token-auth';` and ensure it's correctly included in `combineReducers`. -
Uncaught TypeError: Cannot read properties of undefined (reading 'currentUser')
cause The initial state for the `reduxTokenAuth` reducer slice is not correctly defined in your Redux store setup.fixEnsure your Redux store's initial state includes `reduxTokenAuth: { currentUser: { isLoading: false, isSignedIn: false, attributes: {} } }` or similar structure as shown in the documentation. -
Failed to compile: Can't resolve 'redux-token-auth'
cause The `redux-token-auth` package has not been installed or there's a typo in the import path.fixRun `npm install --save redux-token-auth` or `yarn add redux-token-auth` to install the package, and double-check your import statements.
Warnings
- gotcha The `generateRequireSignInWrapper` component (for protected routes) explicitly requires `react-router` version 4.0.0 or higher to function correctly. Using older versions or no `react-router` will lead to errors.
- gotcha The library's asynchronous actions are built upon Redux Thunk middleware. If `redux-thunk` is not integrated into your Redux store setup using `applyMiddleware`, the actions generated by `generateAuthActions` will not dispatch correctly.
- gotcha `redux-token-auth` currently supports token persistence only via `localStorage` for web applications. `AsyncStorage` for React Native environments is explicitly roadmapped but not yet implemented, meaning it's not suitable for React Native projects without custom adaptations.
- gotcha The `generateAuthActions` function requires a configuration object, including `apiBase`, `signInPath`, `signUpPath`, and `signOutPath`, to correctly interact with your Devise Token Auth backend. Incorrect or missing configuration will lead to failed API requests.
Install
-
npm install redux-token-auth -
yarn add redux-token-auth -
pnpm add redux-token-auth
Imports
- reduxTokenAuthReducer
const { reduxTokenAuthReducer } = require('redux-token-auth');import { reduxTokenAuthReducer } from 'redux-token-auth'; - generateAuthActions
const generateAuthActions = require('redux-token-auth').generateAuthActions;import { generateAuthActions } from 'redux-token-auth'; - generateRequireSignInWrapper
import generateRequireSignInWrapper from 'redux-token-auth/generateRequireSignInWrapper';
import { generateRequireSignInWrapper } from 'redux-token-auth';
Quickstart
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { thunk } from 'redux-thunk'; // Standard ESM import for redux-thunk
import { reduxTokenAuthReducer, generateAuthActions } from 'redux-token-auth';
// 1. Redux Store Setup
const rootReducer = combineReducers({
reduxTokenAuth: reduxTokenAuthReducer,
// ... potentially other reducers
});
// Define a minimal initial state structure matching redux-token-auth's expectations
const initialState = {
reduxTokenAuth: {
currentUser: {
isLoading: false,
isSignedIn: false,
attributes: {}, // Example: firstName: null
},
},
// ... any other initial state needed by other reducers
};
// Create the Redux store
// Note: In a modern React/Redux app, you'd typically use configureStore from @reduxjs/toolkit
const store = createStore(rootReducer, initialState as any, applyMiddleware(thunk));
// 2. Configure redux-token-auth and generate actions
const config = {
apiBase: 'https://api.example.com/v1', // IMPORTANT: Replace with your actual Devise Token Auth API base URL
signOutPath: '/auth/sign_out',
signInPath: '/auth/sign_in',
signUpPath: '/auth',
userAttributes: {
firstName: 'first_name',
lastName: 'last_name',
email: 'email',
},
storage: typeof window !== 'undefined' ? localStorage : null, // Use localStorage in browser, null otherwise
// You can also add authProviderPaths, etc., as needed
};
// Generate the auth actions and the verifyCredentials helper
const {
registerUser,
signInUser,
signOutUser,
verifyToken,
verifyCredentials,
} = generateAuthActions(config);
console.log('Redux Store initialized and auth actions generated.');
// 3. Dispatch verifyCredentials on application load
// This ensures the app checks for existing user tokens on startup
if (config.storage) { // Only attempt if localStorage is available (i.e., not server-side rendering)
store.dispatch(verifyCredentials() as any);
console.log('Dispatched verifyCredentials on app startup.');
}
// Example: Simulate a sign-in attempt (in a real app, this would be triggered by a user action)
async function simulateSignIn() {
console.log('\n--- Simulating User Sign-In ---');
try {
const signInPayload = { email: 'user@example.com', password: 'password123' };
// Dispatch the signInUser thunk action
// Note: Type assertion `as any` is used here for simplicity in quickstart,
// in a real app, proper ThunkAction types would be used.
await store.dispatch(signInUser(signInPayload) as any);
console.log('Sign-in successful. Current auth state:', store.getState().reduxTokenAuth.currentUser);
} catch (error: any) {
console.error('Sign-in failed:', error.response?.data || error.message);
}
}
// Call the simulation
if (typeof window !== 'undefined') {
// Only run this client-side for demonstration
simulateSignIn();
}
// To use generateRequireSignInWrapper, you would typically use it within a React component:
// import { generateRequireSignInWrapper } from 'redux-token-auth';
// const MyProtectedComponent = () => <div>Protected Content!</div>;
// const RequireSignIn = generateRequireSignInWrapper({
// redirectPath: '/login', // Path to redirect if not signed in
// WrappedComponent: MyProtectedComponent,
// });
// In your router: <Route path="/dashboard" component={RequireSignIn} />
console.log('\nQuickstart complete. Check console for output.');