Open Rinbo opened 5 years ago
@Rinbo Did you get this working?
@suyesh nope.
@RinboI tried everything and nothing worked. Going to good old create from scratch mode.
@suyesh Yeah, same here.
@Rinbo I would need your advice on how to solve this issue.
@tochman I threw out this module and implemented my own middleware instead:
import axios from "axios";
let HEADERS = ["access-token", "token-type", "client", "expiry", "uid"];
const tokenMiddleware = () => store => next => action => {
if (!action) {
action = { type: "" };
}
let customHeaders = []
let validateAction = "VALIDATE_TOKEN"
let logoutAction = "SIGN_OUT"
HEADERS = [...new Set([...HEADERS, ...customHeaders])];
if (action.type === validateAction) {
HEADERS.forEach(
token =>
(axios.defaults.headers.common[token] = localStorage.getItem(token))
);
} else if (action.type === logoutAction) {
HEADERS.forEach(token => localStorage.removeItem(token));
} else {
let { headers } = action;
if (headers) {
if (headers["access-token"]) {
HEADERS.forEach(token => {
axios.defaults.headers.common[token] = headers[token];
localStorage.setItem(token, headers[token]);
});
}
}
}
return next(action);
};
export default tokenMiddleware;
Then I applied it to store in root index.js:
...
import reduxThunk from "redux-thunk";
import tokenMiddelware from "./tokenMiddleware";
import reducers from "./reducers";
import App from "./components/App";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducers,
composeEnhancers(applyMiddleware(reduxThunk, tokenMiddelware()))
);
...
Then in authActions i made the following function:
export const validateUser = () => async dispatch => {
dispatch({ type: "VALIDATE_TOKEN" });
const headers = axios.defaults.headers.common;
try {
const response = await endpoint.get("api/v1/auth/validate_token", headers);
const user = response.data.data;
dispatch(setHeaders(response.headers));
dispatch({ type: "SIGN_IN", payload: user });
} catch (err) {
console.log(err, "Missing Token. Please Log in.");
dispatch({ type: "LOADED" });
}
};
The setHeaders function just relays the headers that gets returned from the rails app to the middleware (via the action key), so that they are always available to the axios module (needed for every request).
Then you can make your own custom protected routes (assuming you are using react-router-dom), in which I call the validateUser action in ComponentDidMount. For my part I made three custom routes. AdminRoute, ProtectedRoute (for signed in users) and AuthRoute (which automatically redirects a signed in user away from the signup page). The syntax is a little obscure but here is the ProtectedRoute.js:
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { validateUser } from "../actions/authActions";
export class ProtectedRoute extends React.Component {
componentDidMount = () => {
this.props.validateUser();
};
render() {
const {
isSignedIn,
isDOMLoaded,
component: Component,
...rest
} = this.props;
if (isDOMLoaded) {
return (
<Route
{...rest}
render={props =>
isSignedIn ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/auth", state: { from: props.location } }}
/>
)
}
/>
);
} else {
return null;
}
}
}
const MapStateToProps = state => {
return {
isSignedIn: state.currentUser.isSignedIn,
isDOMLoaded: state.currentUser.isDOMLoaded
};
};
export default connect(
MapStateToProps,
{ validateUser }
)(ProtectedRoute);
You can probably omit IsDOMLoaded. Don't remember why I included that.
Then in your app.js where you define your routes:
import React from "react";
import { Router, Switch, Route } from "react-router-dom";
import history from "../history";
...
import AdminRoute from "./AdminRoute";
import ProtectedRoute from "./ProtectedRoute";
import AuthRoute from "./AuthRoute";
import LandingPage from "./LandingPage";
import Signup from "./auth/Signup";
...
const Routes = () => (
<div className="ui container">
<Router history={history}>
<div>
<Header />
<Switch>
<ProtectedRoute path="/" exact component={LandingPage} />
<ProtectedRoute path="/profile" exact component={UserProfile} />
...
<AdminRoute path="/admin" exact component={Dashboard} />
...
<AuthRoute path="/auth" exact component={AuthPage} />
...
</Switch>
<Footer />
</div>
</Router>
</div>
);
const App = () => {
return Routes();
};
export default App;
Awesome solution.
Using the generateRequireSignInWrapper gives me this error message:
Could not find "store" in either the context or props of "Connect(GatedPage)". Either wrap the root component in a Provider, or explicitly pass "store" as a prop to "Connect(GatedPage)".
Here is my code:
index.js
app.js