Gathering detailed insights and metrics for redux-auth-wrapper
Gathering detailed insights and metrics for redux-auth-wrapper
Gathering detailed insights and metrics for redux-auth-wrapper
Gathering detailed insights and metrics for redux-auth-wrapper
npm install redux-auth-wrapper
Typescript
Module System
96.9
Supply Chain
77.7
Quality
75
Maintenance
100
Vulnerability
99.6
License
JavaScript (98.93%)
Shell (0.75%)
HTML (0.33%)
Total Downloads
8,772,045
Last Day
5,598
Last Week
27,553
Last Month
90,575
Last Year
1,141,274
2,193 Stars
393 Commits
240 Forks
37 Watching
17 Branches
31 Contributors
Minified
Minified + Gzipped
Latest Version
3.0.0
Package Id
redux-auth-wrapper@3.0.0
Size
6.21 kB
Publised On
03 Jan 2020
Cumulative downloads
Total Downloads
Last day
-23.5%
5,598
Compared to previous day
Last week
3.1%
27,553
Compared to previous week
Last month
23%
90,575
Compared to previous month
Last year
-16.9%
1,141,274
Compared to previous year
28
Decouple your Authentication and Authorization from your components!
npm install --save redux-auth-wrapper
Versioning Note: This project adheres to semver. Please view the Changelog for release notes.
At first, handling authentication and authorization seems easy in React-Router and Redux. After all, we have a handy onEnter method, shouldn't we use it?
onEnter
is great, and useful in certain situations. However, here are some common authentication and authorization problems onEnter
does not solve:
onChange
)An alternative approach is to use Higher Order Components.
A higher-order component is just a function that takes an existing component and returns another component that wraps it
Redux-auth-wrapper provides higher-order components for easy to read and apply authentication and authorization constraints for your components.
Usage with React-Router-Redux (Version 4.0)
1import React from 'react' 2import ReactDOM from 'react-dom' 3import { createStore, combineReducers, applyMiddleware, compose } from 'redux' 4import { Provider } from 'react-redux' 5import { Router, Route, browserHistory } from 'react-router' 6import { routerReducer, syncHistoryWithStore, routerActions, routerMiddleware } from 'react-router-redux' 7import { UserAuthWrapper } from 'redux-auth-wrapper' 8import userReducer from '<project-path>/reducers/userReducer' 9 10const reducer = combineReducers({ 11 routing: routerReducer, 12 user: userReducer 13}) 14 15const routingMiddleware = routerMiddleware(browserHistory) 16 17// Note: passing middleware as the last argument requires redux@>=3.1.0 18const store = createStore( 19 reducer, 20 applyMiddleware(routingMiddleware) 21) 22const history = syncHistoryWithStore(browserHistory, store) 23 24// Redirects to /login by default 25const UserIsAuthenticated = UserAuthWrapper({ 26 authSelector: state => state.user, // how to get the user state 27 redirectAction: routerActions.replace, // the redux action to dispatch for redirect 28 wrapperDisplayName: 'UserIsAuthenticated' // a nice name for this auth check 29}) 30 31ReactDOM.render( 32 <Provider store={store}> 33 <Router history={history}> 34 <Route path="/" component={App}> 35 <Route path="login" component={Login}/> 36 <Route path="foo" component={UserIsAuthenticated(Foo)}/> 37 <Route path="bar" component={Bar}/> 38 </Route> 39 </Router> 40 </Provider>, 41 document.getElementById('mount') 42)
And your userReducer looks something like:
1const userReducer = (state = {}, { type, payload }) => { 2 if (type === USER_LOGGED_IN) { 3 return payload 4 } 5 if (type === USER_LOGGED_OUT) { 6 return {} 7 } 8 return state 9}
When the user navigates to /foo
, one of the following occurs:
If The user data is null or an empty object:
The user is redirected to /login?redirect=%2foo
Notice the url contains the query parameter redirect
for sending the user back to after you log them into your app
Otherwise:
The <Foo>
component is rendered and passed the user data as a property
Any time the user data changes, the UserAuthWrapper will re-check for authentication.
Note: You still need to provide a mechanism for redirecting the user from the login page back to your component. You can also do that with redux-auth-wrapper! See the loading example for further details.
UserAuthWrapper(configObject)(DecoratedComponent)
authSelector(state, [ownProps]): authData
(Function): A state selector for the auth data. Just like mapToStateProps
.
ownProps will be null if isOnEnter is true because onEnter hooks cannot receive the component properties. Can be ignored when not using onEnter.authenticatingSelector(state, [ownProps]): Bool
(Function): A state selector indicating if the user is currently authenticating. Just like mapToStateProps
. Useful for async session loading.LoadingComponent
(Component): A React component to render while authenticatingSelector
is true
. Will be passed
all properties passed into the wrapped component, including children
.FailureComponent
(Component): A React component to render when authenticatingSelector
is false
. If specified, the wrapper will
not redirect. Can be set to null
to display nothing when the user is not authenticated/authorized.[failureRedirectPath]
(String | (state, [ownProps]): String): Optional path to redirect the browser to on a failed check. Defaults to /login
. Can also be a function of state and ownProps that returns a string.[redirectQueryParamName]
(String): Optional name of the query parameter added when allowRedirectBack
is true. Defaults to redirect
.[redirectAction]
(Function): Optional redux action creator for redirecting the user. If not present, will use React-Router's router context to perform the transition.[wrapperDisplayName]
(String): Optional name describing this authentication or authorization check.
It will display in React-devtools. Defaults to UserAuthWrapper
[predicate(authData): Bool]
(Function): Optional function to be passed the result of the authSelector
param.
If it evaluates to false the browser will be redirected to failureRedirectPath
, otherwise DecoratedComponent
will be rendered. By default, it returns false if authData
is {} or null.[allowRedirectBack]
(Bool | (location, redirectPath): Bool ): Optional bool on whether to pass a redirect
query parameter to the failureRedirectPath
. Can also be a function of location and the computed failureRedirectPath
passed above, that must return a boolean value. Defaults to true
.[propMapper]
(Function): Optional function that takes the props passed into the wrapped component and returns those props to pass to the DecoratedComponent, The LoadingComponent, and the FailureComponent.After applying the configObject, UserAuthWrapper
returns a function which can applied to a Component to wrap in authentication and
authorization checks. The function also has the following extra properties:
onEnter(store, nextState, replace)
(Function): Function to be optionally used in the onEnter property of a route.DecoratedComponent
(React Component): The component to be wrapped in the auth check. It will pass down all props given to the returned component as well as the prop authData
which is the result of the authSelector
.
The component is not modified and all static properties are hoisted to the returned component1/* Allow only users with first name Bob */
2const OnlyBob = UserAuthWrapper({
3 authSelector: state => state.user,
4 redirectAction: routerActions.replace,
5 failureRedirectPath: '/app',
6 wrapperDisplayName: 'UserIsOnlyBob',
7 predicate: user => user.firstName === 'Bob'
8})
9
10/* Admins only */
11
12// Take the regular authentication & redirect to login from before
13const UserIsAuthenticated = UserAuthWrapper({
14 authSelector: state => state.user,
15 redirectAction: routerActions.replace,
16 wrapperDisplayName: 'UserIsAuthenticated'
17})
18// Admin Authorization, redirects non-admins to /app and don't send a redirect param
19const UserIsAdmin = UserAuthWrapper({
20 authSelector: state => state.user,
21 redirectAction: routerActions.replace,
22 failureRedirectPath: '/app',
23 wrapperDisplayName: 'UserIsAdmin',
24 predicate: user => user.isAdmin,
25 allowRedirectBack: false
26})
27
28// Now to secure the component: first check if the user is authenticated, and then check if the user is an admin
29<Route path="foo" component={UserIsAuthenticated(UserIsAdmin(Admin))}/>
The ordering of the nested higher order components is important because UserIsAuthenticated(UserIsAdmin(Admin))
means that logged out admins will be redirected to /login
before checking if they are an admin.
Otherwise admins would be sent to /app
if they weren't logged in and then redirected to /login
, only to find themselves at /app
after entering their credentials.
The auth wrappers can be used for more than redirection. You can use the FailureComponent
parameter to hide a
component or display an alternative component when the user is not authorized. Keep in mind that wrappers that use
FailureComponent
will not redirect users.
Here is an example that hides a link from a non-admin user.
1const VisibleOnlyAdmin = UserAuthWrapper({ 2 authSelector: state => state.user, 3 wrapperDisplayName: 'VisibleOnlyAdmin', 4 predicate: user => user.isAdmin, 5 FailureComponent: null 6}) 7 8// Applying to a function component for simplicity but could be Class or createClass component 9const AdminOnlyLink = VisibleOnlyAdmin(() => <Link to='/admin'>Admin Section</Link>)
You can see an example of hiding links in the basic example.
Alternatively, you can specify a FailureComponent to display an alternative component, in this example we specify a new function for our returned HOC to make it more flexible to apply across the app.
1const AdminOrElse = (Component, FailureComponent) => UserAuthWrapper({ 2 authSelector: state => state.user, 3 wrapperDisplayName: 'AdminOrElse', 4 predicate: user => user.isAdmin, 5 FailureComponent 6})(Component) 7 8// Show Admin dashboard to admins and user dashboard to regular users 9<Route path='/dashboard' component={AdminOrElse(AdminDashboard, UserDashboard)} />
One benefit of the beginning example is that it is clear from looking at the Routes where the authentication & authorization logic is applied. There are a variety of other places to apply redux-auth-wrapper. Please review this section first to avoid incorrectly applying the HOC and causing bugs in your code.
Directly inside ReactDOM.render:
1ReactDOM.render( 2 <Provider store={store}> 3 <Router history={history}> 4 <Route path="/" component={App}> 5 <Route path="auth" component={UserIsAuthenticated(Foo)}/> 6 ... 7 </Route> 8 </Router> 9 </Provider>, 10 document.getElementById('mount') 11)
Separate route config file:
1const routes = ( 2 <Route path="/" component={App}> 3 <Route path="auth" component={UserIsAuthenticated(Foo)}/> 4 ... 5 </Route> 6) 7 8ReactDOM.render( 9 <Provider store={store}> 10 <Router history={history}> 11 {routes} 12 </Router> 13 </Provider>, 14 document.getElementById('mount') 15)
Applied in the component file (es7):
1@UserIsAuthenticated 2export default class MyComponent extends Component { 3 ... 4}
Applied in the component file (es6):
1class MyComponent extends Component { 2 ... 3} 4export default UserIsAuthenticated(MyComponent)
Applied outside the component file:
1import MyComponent from './component/mycomponent.js' 2 3const MyAuthComponent = UserIsAuthenticated(MyComponent)
The following are all not safe because they create a new component over and over again, preventing react from considering these the "same" component and causing mounting/unmounting loops.
Inside of render:
1import MyComponent from './component/MyComponent.js' 2 3class MyParentComponent extends Component { 4 render() { 5 const MyAuthComponent = UserIsAuthenticated(MyComponent) 6 return <MyAuthComponent /> 7 } 8}
Inside of any getComponent
:
1const routes = ( 2 <Route path="/" component={App}> 3 <Route path="auth" getComponent={(nextState, cb) => { 4 cb(null, UserIsAuthenticated(Foo)) 5 }} /> 6 ... 7 </Route> 8)
Because routes in React Router are not required to have paths, you can use nesting to protect multiple routes without applying the wrapper multiple times.
1const Authenticated = UserIsAuthenticated((props) => React.cloneElement(props.children, props)); 2 3<Route path='/' component={App}> 4 <IndexRedirect to="/login" /> 5 <Route path='login' component={Login} /> 6 <Route component={Authenticated}> 7 <Route path="foo" component={Foo} /> 8 <Route path="bar" component={Bar} /> 9 </Route> 10</Route>
You may want to dispatch an additional redux action when a redirect occurs. One example of this is to display a notification message
that the user is being redirected or don't have access to that protected resource. To do this, you can chain the redirectAction
parameter using redux-thunk
middleware. It depends slightly on if you are using a redux + routing solution or just React Router.
react-router-redux
or redux-router
and dispatching an extra redux action in the wrapper1import { replace } from 'react-router-redux'; // Or your redux-router equivalent
2import addNotification from './notificationActions';
3
4// Admin Authorization, redirects non-admins to /app
5const UserIsAdmin = UserAuthWrapper({
6 failureRedirectPath: '/app',
7 predicate: user => user.isAdmin,
8 redirectAction: (newLoc) => (dispatch) => {
9 dispatch(replace(newLoc));
10 dispatch(addNotification({ message: 'Sorry, you are not an administrator' }));
11 },
12 ...
13})
1import { browserHistory } from 'react-router';
2import addNotification from './notificationActions';
3
4// Admin Authorization, redirects non-admins to /app
5const UserIsAdmin = UserAuthWrapper({
6 failureRedirectPath: '/app',
7 predicate: user => user.isAdmin,
8 redirectAction: (newLoc) => (dispatch) => {
9 browserHistory.replace(newLoc);
10 dispatch(addNotification({ message: 'Sorry, you are not an administrator' }));
11 },
12 ...
13})
If your UserAuthWrapper
uses redirection, then you may need to use the onEnter
property
of a <Route>
to perform authentication and authorization checks for Server Side Rendering. (Note: If you are only using FailureComponent
and not redirecting in your UserAuthWrapper
, then you do not need to use onEnter
option described below.)
During onEnter, selectors such as authSelector
, authenticatingSelector
, and failureRedirectPath
(if you are using)
the function variation, will receive react-router's nextState
as their second argument instead of the component props.
You can access the onEnter
method of the UserAuthWrapper
after applying the config parameters:
1import { UserAuthWrapper } from 'redux-auth-wrapper'; 2 3const UserIsAuthenticated = UserAuthWrapper({ 4 authSelector: state => state.user, 5 redirectAction: routerActions.replace, 6 wrapperDisplayName: 'UserIsAuthenticated' 7}) 8 9const getRoutes = (store) => { 10 const connect = (fn) => (nextState, replaceState) => fn(store, nextState, replaceState); 11 12 return ( 13 <Route> 14 <Route path="/" component={App}> 15 <Route path="login" component={Login}/> 16 <Route path="foo" component={UserIsAuthenticated(Foo)} onEnter={connect(UserIsAuthenticated.onEnter)} /> 17 </Route> 18 </Route> 19 ); 20};
To implement SSR with nested wrappers, you will have to provide a function to chain onEnter
functions of each wrapper. To illustrate this, we can modify the example provided in the Authorization & Advanced Usage section above, wherein UserIsAuthenticated
is the parent wrapper and UserIsAdmin
is the child wrapper.
1import { UserAuthWrapper } from 'redux-auth-wrapper';
2
3const UserIsAuthenticated = UserAuthWrapper({
4 authSelector: state => state.user,
5 redirectAction: routerActions.replace,
6 wrapperDisplayName: 'UserIsAuthenticated'
7})
8
9// Admin Authorization, redirects non-admins to /app and don't send a redirect param
10const UserIsAdmin = UserAuthWrapper({
11 authSelector: state => state.user,
12 redirectAction: routerActions.replace,
13 failureRedirectPath: '/app',
14 wrapperDisplayName: 'UserIsAdmin',
15 predicate: user => user.isAdmin,
16 allowRedirectBack: false
17})
18
19const getRoutes = (store) => {
20 const connect = (fn) => (nextState, replaceState) => fn(store, nextState, replaceState);
21
22 //This executes the parent onEnter first, going from left to right.
23 // `replace` has to be wrapped because we want to stop executing `onEnter` hooks
24 // after the first call to `replace`.
25 const onEnterChain = (...listOfOnEnters) => (store, nextState, replace) => {
26 let redirected = false;
27 const wrappedReplace = (...args) => {
28 replace(...args);
29 redirected = true;
30 };
31 listOfOnEnters.forEach((onEnter) => {
32 if (!redirected) {
33 onEnter(store, nextState, wrappedReplace);
34 }
35 });
36 };
37
38 return (
39 <Route>
40 <Route path="/" component={App}>
41 <Route path="login" component={Login}/>
42 <Route path="foo"
43 component={UserIsAuthenticated(UserIsAdmin(Admin))}
44 onEnter={connect(onEnterChain(UserIsAuthenticated.onEnter, UserIsAdmin.onEnter))} />
45 </Route>
46 </Route>
47 );
48};
49
This library can be used with React Native >= 0.25.0
without any changes.
Using React Native and redux-auth-wrapper? Please help create an example for others to get started!
No vulnerabilities found.
Reason
all changesets reviewed
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
106 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More@types/redux-auth-wrapper
TypeScript definitions for redux-auth-wrapper
@cdmbase/redux-auth-wrapper
A utility library for handling authentication and authorization for redux and react-router
kjanoudi-redux-auth-wrapper
A utility library for handling authentication and authorization for redux and react-router
redux-auth-wrapper-any
A utility library for handling authentication and authorization for redux and react-router