{"id":23446315,"url":"https://github.com/inmagik/eazy-auth","last_synced_at":"2026-05-07T03:40:20.344Z","repository":{"id":28901152,"uuid":"111924984","full_name":"inmagik/eazy-auth","owner":"inmagik","description":"Easy auth stuff \\w redux","archived":false,"fork":false,"pushed_at":"2023-01-03T16:50:20.000Z","size":1573,"stargazers_count":4,"open_issues_count":27,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-10T00:49:28.913Z","etag":null,"topics":["authentication","eazy","react","react-router-v4","redux","redux-saga"],"latest_commit_sha":null,"homepage":"https://inmagik.github.io/eazy-auth","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inmagik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-24T14:07:14.000Z","updated_at":"2019-06-13T20:16:56.000Z","dependencies_parsed_at":"2023-01-14T13:45:53.714Z","dependency_job_id":null,"html_url":"https://github.com/inmagik/eazy-auth","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmagik%2Feazy-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmagik%2Feazy-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmagik%2Feazy-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmagik%2Feazy-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inmagik","download_url":"https://codeload.github.com/inmagik/eazy-auth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137998,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","eazy","react","react-router-v4","redux","redux-saga"],"created_at":"2024-12-23T20:32:24.544Z","updated_at":"2026-05-07T03:40:15.321Z","avatar_url":"https://github.com/inmagik.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eazy-auth\n\nEasy auth stuff \\w redux\n\nBattery included but extractable package to automate auth boring stuff.\n\nThe stack is `redux`, `react` for UI, `redux-saga` for side effects and `react-router-dom` for routing.\n\n\n[Example App](https://inmagik.github.io/eazy-auth)\n\n## Install\n```\nyarn add eazy-auth\n```\nor\n```\nnpm install eazy-auth --save\n```\n\n## Cheat sheet\n```js\n\n// Redux\n// Make the auth reducer using auth as reducer key\nimport { makeAuthReducer } from 'eazy-auth'\nconst rootReducer = combineReducers({\n  auth: makeAuthReducer(),\n})\n\n// Redux saga\n// Make the auth flow (managed using redux-saga)\n// you get authFlow a saga to fork to bootstrap auth\n// authCall a version of redux-saga call function but with auth token curried\nimport { makeAuthFlow } from 'eazy-auth'\nconst { authFlow, authCall } = makeAuthFlow({\n  loginCall: credentials =\u003e /* promise that resolve ({ access_token, refresh_token )}  */,\n  refreshTokenCall: refreshToken =\u003e /* promise that resolve refreshed ({ access_token, refresh_token )} */,\n  meCall: token =\u003e /* promise that resolve object with user data  */,\n})\n\nfunction *mainSaga() {\n  // Bootstrap the auth\n  // when user logged in for the first time eazy-auth save the tokens in local storage\n  // when user return to app eazy-auth take tokens from local storage and perform a me call\n  // (try to refresh tokens if function is provided) if all is ok user data are stored in redux\n  // and update tokens in local storage if needed otherwise local storage is cleared\n  yield fork(authFlow)\n\n  // authCall is a enhance version of redux-saga call https://redux-saga.js.org/docs/api/#callfn-args\n  // but it call the function with the access token curried so you can do authenticated api call\n  // plus if the api reject and the exception contains a status key with 401 eazy-auht try to refresh token and\n  // retrying the call if fail again or no refresh function is provided logout and clear state and local storage\n  try {\n    const data = yield call(token =\u003e Api.fetchUser(token), action.payload.id)\n    yield put({type: \"FETCH_SUCCEEDED\", data})\n  } catch (error) {\n    yield put({type: \"FETCH_FAILED\", error})\n  }\n\n  // \\\\\\TIP///\n  // for call api i personally use superagent https://github.com/visionmedia/superagent\n  // for automate the injection of token you can use a helper like that or do a similar stuff \\w other fetching libraries\n  const withToken = (token, baseRequest) =\u003e\n    (token ? baseRequest.set('Authorization', `Bearer ${token}`) : baseRequest)\n\n  const fetchUser = token =\u003e id =\u003e\n    withToken(token, request.get(`/api/users/${id}`))\n}\n\n// React\n// What you get?\n\n// Action creators\nimport {\n  // Login using credentials\n  // login({ username, password })\n  login,\n  // Logout and clear local storage\n  // logout()\n  logout,\n  // Clear login erro in state\n  clearLoginError,\n  // A helper to update user in state\n  // updateUser({ username, age, ... })\n  updateUser,\n} from 'eazy-auth'\n\n// Selectors\nimport {\n  isLoginLoading,\n  getLoginError,\n  getAuthUser,\n  getAuthAccessToken,\n  getAuthRefreshToken,\n} from 'eazy-auth'\n\n// ... And if you want o covenient high order component for login\nimport { withAuthLogin } from 'eazy-auth'\n\nlet Login = ({ handleSubmit, credentials: { email, password }, error, loading }) =\u003e (\n  \u003cform onSubmit={handleSubmit}\u003e\n    \u003cdiv\u003e\n      \u003cinput type='email' {...email} /\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cinput type='password' {...password} /\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n      \u003cinput type='submit' /\u003e\n    \u003c/div\u003e\n    {loading \u0026\u0026 \u003cdiv\u003eLogin...\u003c/div\u003e}\n    {error \u0026\u0026 \u003cdiv\u003eBad credentials\u003c/div\u003e}\n  \u003c/form\u003e\n)\n\nlogin = withAuthLogin({\n  // Defaults\n  credentials: ['email', 'password'],\n  shouldclearErrorOnChange: true,\n})(Login)\n\n\n// React router v4\nimport { AuthRoute } from 'eazy-auth'\n\nconst App = () =\u003e (\n  \u003cProvider store={store}\u003e\n    \u003cRouter\u003e\n      \u003cSwitch\u003e\n        {/*  Redirect user to another route if not authenticated */}\n        \u003cAuthRoute\n          path='/profile'\n          exact\n          component={Old}\n          {/* Path to redirect */}\n          redirectTo={'/login'}\n          {/* Spinner to use while loading */}\n          spinner={null},\n          {/* Remeber referrer when redirect guests? */}\n          rememberReferrer={true}\n          {/* Additional function to check permission on user and redirect */}\n          redirectTest={user =\u003e user.age \u003e 27 ? false : '/'}\n        /\u003e\n        {/*  Redirect user to another route if authenticated */}\n        \u003cGuestRoute\n          path='/login'\n          exact\n          component={Login}\n          spinner={null}\n          redirectTo={'/'}\n          {/* Redirect to referrer if user logged in? */}\n          redirectToReferrer={true}\n        /\u003e\n        {/*\n          Simply if user has a token in local storage and not yet me has been\n          performed wait until me complete before mounting component\n          optionally show a spinner if you want\n        */}\n        \u003cMaybeAuthRoute\n          path='/home'\n          exact\n          component={Home}\n          spinner={null},\n        /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  \u003c/Provider\u003e\n)\n```\n## authMiddleware\n\n`eazy-auth` was originally built with `redux-saga` in mind but in certain situation you need to hook the auth \"side effects\" outside the `redux-saga` environment for example directly in react components.\n\nThis is why  the `authMiddleware` was created.\n\nCreate auth middleware:\n\n```js\nconst { authFlow, authCall, makeAuthMiddleware } = makeAuthFlow({\n  /* Normal configuration, see above */\n})\nconst authMiddleware = makeAuthMiddleware()\nconst sagaMiddleware = createSagaMiddleware()\n\nconst composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose\nconst store = createStore(\n  rootReducer,\n  undefined,\n  composeEnhancers(\n    applyMiddleware(sagaMiddleware, authMiddleware),\n  )\n)\n// IMPORTATION run authMiddleware before the sagaMiddleware \nexport const { callAuthApiObservable, callAuthApiPromise } = authMiddleware.run()\nsagaMiddleware.run(mainSaga)\n\nexport default store\n```\n\n### callAuthApiPromise(apiCall, ...args)\n\n`apiCall: (accessToken)(...args) =\u003e Promise`\n\nCurry the `accessToken` if any, logout on rejection matches `{ status: 401|403 }` and try refresh if a refresh call is given, if refresh is good re-try the `apiCall` otherwis rejects.\n\nReturn a Promise.\n\n### callAuthApiObservable(apiCall, ...args)\n\n`apiCall: (accessToken)(...args) =\u003e Promise|Observable`\n\nSame as above but implement with observables.\n\nReturn a Observable.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finmagik%2Feazy-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finmagik%2Feazy-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finmagik%2Feazy-auth/lists"}