Socket
Socket
Sign inDemoInstall

eazy-auth

Package Overview
Dependencies
36
Maintainers
2
Versions
34
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    eazy-auth

Easy auth stuff with redux


Version published
Weekly downloads
40
increased by566.67%
Maintainers
2
Install size
5.44 MB
Created
Weekly downloads
 

Readme

Source

eazy-auth

Easy auth stuff \w redux

Battery included but extractable package to automate auth boring stuff.

The stack is redux, react for UI, redux-saga for side effects and react-router-dom for routing.

Example App

Install

yarn add eazy-auth

or

npm install eazy-auth --save

Cheat sheet


// Redux
// Make the auth reducer using auth as reducer key
import { makeAuthReducer } from 'eazy-auth'
const rootReducer = combineReducers({
  auth: makeAuthReducer(),
})

// Redux saga
// Make the auth flow (managed using redux-saga)
// you get authFlow a saga to fork to bootstrap auth
// authCall a version of redux-saga call function but with auth token curried
import { makeAuthFlow } from 'eazy-auth'
const { authFlow, authCall } = makeAuthFlow({
  loginCall: credentials => /* promise that resolve ({ access_token, refresh_token )}  */,
  refreshTokenCall: refreshToken => /* promise that resolve refreshed ({ access_token, refresh_token )} */,
  meCall: token => /* promise that resolve object with user data  */,
})

function *mainSaga() {
  // Bootstrap the auth
  // when user logged in for the first time eazy-auth save the tokens in local storage
  // when user return to app eazy-auth take tokens from local storage and perform a me call
  // (try to refresh tokens if function is provided) if all is ok user data are stored in redux
  // and update tokens in local storage if needed otherwise local storage is cleared
  yield fork(authFlow)

  // authCall is a enhance version of redux-saga call https://redux-saga.js.org/docs/api/#callfn-args
  // but it call the function with the access token curried so you can do authenticated api call
  // plus if the api reject and the exception contains a status key with 401 eazy-auht try to refresh token and
  // retrying the call if fail again or no refresh function is provided logout and clear state and local storage
  try {
    const data = yield call(token => Api.fetchUser(token), action.payload.id)
    yield put({type: "FETCH_SUCCEEDED", data})
  } catch (error) {
    yield put({type: "FETCH_FAILED", error})
  }

  // \\\TIP///
  // for call api i personally use superagent https://github.com/visionmedia/superagent
  // for automate the injection of token you can use a helper like that or do a similar stuff \w other fetching libraries
  const withToken = (token, baseRequest) =>
    (token ? baseRequest.set('Authorization', `Bearer ${token}`) : baseRequest)

  const fetchUser = token => id =>
    withToken(token, request.get(`/api/users/${id}`))
}

// React
// What you get?

// Action creators
import {
  // Login using credentials
  // login({ username, password })
  login,
  // Logout and clear local storage
  // logout()
  logout,
  // Clear login erro in state
  clearLoginError,
  // A helper to update user in state
  // updateUser({ username, age, ... })
  updateUser,
} from 'eazy-auth'

// Selectors
import {
  isLoginLoading,
  getLoginError,
  getAuthUser,
  getAuthAccessToken,
  getAuthRefreshToken,
} from 'eazy-auth'

// ... And if you want o covenient high order component for login
import { withAuthLogin } from 'eazy-auth'

let Login = ({ handleSubmit, credentials: { email, password }, error, loading }) => (
  <form onSubmit={handleSubmit}>
    <div>
      <input type='email' {...email} />
    </div>
    <div>
      <input type='password' {...password} />
    </div>
    <div>
      <input type='submit' />
    </div>
    {loading && <div>Login...</div>}
    {error && <div>Bad credentials</div>}
  </form>
)

login = withAuthLogin({
  // Defaults
  credentials: ['email', 'password'],
  shouldclearErrorOnChange: true,
})(Login)


// React router v4
import { AuthRoute } from 'eazy-auth'

const App = () => (
  <Provider store={store}>
    <Router>
      <Switch>
        {/*  Redirect user to another route if not authenticated */}
        <AuthRoute
          path='/profile'
          exact
          component={Old}
          {/* Path to redirect */}
          redirectTo={'/login'}
          {/* Spinner to use while loading */}
          spinner={null},
          {/* Remeber referrer when redirect guests? */}
          rememberReferrer={true}
          {/* Additional function to check permission on user and redirect */}
          redirectTest={user => user.age > 27 ? false : '/'}
        />
        {/*  Redirect user to another route if authenticated */}
        <GuestRoute
          path='/login'
          exact
          component={Login}
          spinner={null}
          redirectTo={'/'}
          {/* Redirect to referrer if user logged in? */}
          redirectToReferrer={true}
        />
        {/*
          Simply if user has a token in local storage and not yet me has been
          performed wait until me complete before mounting component
          optionally show a spinner if you want
        */}
        <MaybeAuthRoute
          path='/home'
          exact
          component={Home}
          spinner={null},
        />
      </Switch>
    </Router>
  </Provider>
)

authMiddleware

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.

This is why the authMiddleware was created.

Create auth middleware:

const { authFlow, authCall, makeAuthMiddleware } = makeAuthFlow({
  /* Normal configuration, see above */
})
const authMiddleware = makeAuthMiddleware()
const sagaMiddleware = createSagaMiddleware()

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
  rootReducer,
  undefined,
  composeEnhancers(
    applyMiddleware(sagaMiddleware, authMiddleware),
  )
)
// IMPORTATION run authMiddleware before the sagaMiddleware 
export const { callAuthApiObservable, callAuthApiPromise } = authMiddleware.run()
sagaMiddleware.run(mainSaga)

export default store

callAuthApiPromise(apiCall, ...args)

apiCall: (accessToken)(...args) => Promise

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

Return a Promise.

callAuthApiObservable(apiCall, ...args)

apiCall: (accessToken)(...args) => Promise|Observable

Same as above but implement with observables.

Return a Observable.

Keywords

FAQs

Last updated on 13 Jun 2019

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc