https://github.com/inmagik/eazy-auth
Easy auth stuff \w redux
https://github.com/inmagik/eazy-auth
authentication eazy react react-router-v4 redux redux-saga
Last synced: 10 months ago
JSON representation
Easy auth stuff \w redux
- Host: GitHub
- URL: https://github.com/inmagik/eazy-auth
- Owner: inmagik
- License: mit
- Created: 2017-11-24T14:07:14.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2023-01-03T16:50:20.000Z (about 3 years ago)
- Last Synced: 2025-04-10T00:49:28.913Z (10 months ago)
- Topics: authentication, eazy, react, react-router-v4, redux, redux-saga
- Language: JavaScript
- Homepage: https://inmagik.github.io/eazy-auth
- Size: 1.5 MB
- Stars: 4
- Watchers: 0
- Forks: 1
- Open Issues: 27
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
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](https://inmagik.github.io/eazy-auth)
## Install
```
yarn add eazy-auth
```
or
```
npm install eazy-auth --save
```
## Cheat sheet
```js
// 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 }) => (
{loading && Login...}
{error && Bad credentials}
)
login = withAuthLogin({
// Defaults
credentials: ['email', 'password'],
shouldclearErrorOnChange: true,
})(Login)
// React router v4
import { AuthRoute } from 'eazy-auth'
const App = () => (
{/* Redirect user to another route if not authenticated */}
user.age > 27 ? false : '/'}
/>
{/* Redirect user to another route if authenticated */}
{/*
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
*/}
)
```
## 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:
```js
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.