eazy-auth: Redux-based Authentication for React

0.7.1 · active · verified Wed Apr 22

eazy-auth is a JavaScript library designed to streamline common authentication tasks within React applications that leverage Redux and Redux-Saga. Currently at version 0.7.1, it provides a 'battery-included' solution for token-based authentication, handling login, token refresh, logout, and user data management. It integrates a Redux reducer, Redux-Saga flows for side effects (like API calls and token refreshing), and React components/HOCs for UI integration, alongside `react-router-dom` for protecting routes. The package allows developers to define custom API calls for authentication, making it adaptable to various backend systems while abstracting away much of the boilerplate. Its pre-1.0.0 status suggests ongoing development, though the README presents it as a functional solution. There is also `use-eazy-auth` which provides similar functionality using React hooks without the strong Redux/Redux-Saga dependency.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the full integration of `eazy-auth` into a React, Redux, and Redux-Saga application. It sets up the Redux reducer and saga middleware, configures the `authFlow` with mock API calls for login, refresh, and user data, shows how to initiate a login, and protects a route using `AuthRoute` for authenticated users. The `authCall` effect is also demonstrated for making authenticated API requests within sagas.

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import createSagaMiddleware from 'redux-saga';
import { fork, call, put } from 'redux-saga/effects';
import { BrowserRouter as Router, Switch } from 'react-router-dom';
import { makeAuthReducer, makeAuthFlow, AuthRoute, login, getAuthUser } from 'eazy-auth';

// 1. Redux Reducer Setup
const rootReducer = combineReducers({
  auth: makeAuthReducer(),
  // ... other reducers
});

// 2. Redux Saga Setup
const loginMockCall = async (credentials) => {
  console.log('Attempting login with:', credentials);
  if (credentials.username === 'test' && credentials.password === 'password') {
    return { access_token: 'fake-access-token', refresh_token: 'fake-refresh-token' };
  }
  throw new Error('Invalid credentials');
};

const refreshTokenMockCall = async (refreshToken) => {
  console.log('Attempting token refresh with:', refreshToken);
  if (refreshToken === 'fake-refresh-token') {
    return { access_token: 'new-fake-access-token', refresh_token: 'new-fake-refresh-token' };
  }
  throw new Error('Invalid refresh token');
};

const meMockCall = async (token) => {
  console.log('Fetching user data with token:', token);
  if (token && token.startsWith('fake-')) {
    return { id: 'user-123', username: 'testuser', email: 'test@example.com' };
  }
  throw new Error('Unauthorized');
};

const { authFlow, authCall } = makeAuthFlow({
  loginCall: loginMockCall,
  refreshTokenCall: refreshTokenMockCall,
  meCall: meMockCall,
});

function* mainSaga() {
  yield fork(authFlow);
  // Example of using authCall for an authenticated API call
  try {
    const userData = yield authCall(async (token) => {
      console.log('Authenticated API call with token:', token);
      // Simulate an API call
      return new Promise(resolve => setTimeout(() => resolve({ message: `Data for ${token}` }), 100));
    });
    yield put({ type: 'API_CALL_SUCCESS', payload: userData });
  } catch (error) {
    yield put({ type: 'API_CALL_FAILURE', error: error.message });
  }
}

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mainSaga);

// 3. React UI Setup
import { connect } from 'react-redux';

const LoginPage = ({ login, user }) => {
  const handleSubmit = (e) => {
    e.preventDefault();
    login({ username: 'test', password: 'password' });
  };

  return (
    <div>
      <h2>Login</h2>
      {user ? (
        <p>Logged in as: {user.username}</p>
      ) : (
        <form onSubmit={handleSubmit}>
          <button type="submit">Log In (test/password)</button>
        </form>
      )}
    </div>
  );
};

const ConnectedLoginPage = connect(
  (state) => ({ user: getAuthUser(state) }),
  { login }
)(LoginPage);

const ProfilePage = ({ user }) => (
  <div>
    <h2>Profile</h2>
    {user ? (
      <p>Welcome, {user.username}!</p>
    ) : (
      <p>Please log in to view your profile.</p>
    )}
  </div>
);

const App = () => (
  <Provider store={store}>
    <Router>
      <ConnectedLoginPage />
      <Switch>
        <AuthRoute path="/profile" component={ProfilePage} exact />
        {/* Other routes */}
      </Switch>
    </Router>
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

view raw JSON →