npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

eazy-auth

v0.7.1

Published

Easy auth stuff with redux

Downloads

17

Readme

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.