I am new to react and I am trying to create a secure area. My app's file structure is as follows:
build
node_modules
public
src
AboutScreen
AdminDash
ContactScreen
HomeScreen
LoginScreen
SignUpScreen
WorkRequestScreen
App.js
index.js
routes.js
serviceWorker.js
package.json
package-lock.json
My routing is done in App.js. This file looks like this:
import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
import Home from './HomeScreen/Home';
import WorkReq from './WorkRequestScreen/WorkReq';
import About from './AboutScreen/About';
import Contact from './ContactScreen/Contact';
import Login from './LoginScreen/Login';
import Signup from './SignUpScreen/Signup';
import adminDash from './AdminDash/AdminDash';
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
signout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)} />
)
class App extends Component {
render() {
return (
<div>
<PrivateRoute path='/adminDash' component={adminDash} />
<Route exact path="/workReq" render={(props) => <WorkReq {...props}/>} />
<Route exact path="/about" component={About}/>
<Route exact path="/contact" component={Contact}/>
<Route exact path="/login" component={Login}/>
<Route exact path="/signup" component={Signup}/>
<Route exact path="/" component={Home}/>
</div>
);
}
}
export default App;
My Login component looks like this:
import App from '../App';
class LoginScreen extends Component {
super(props);
this.state = {
redirectToReferrer: false,
};
login = () => {
App.fakeAuth.authenticate(() => {
this.setState(() => ({
redirectToReferrer: true
}))
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer === true) {
return <Redirect to={from} />
}
return (
<div className='buttonCont'>
<button size='lg' type='button' onClick={this.login.bind(this)}>HIT IT!</button>
</div>
);
}
}
Currently this does not redirect the user to another page but will redirect them to AdminDev. The error I am getting is:
TypeError: Cannot read property 'authenticate' of undefined (login.js)
The prolem line is: App.fakeAuth.authenticate(() => {. The thing I would like to know is am I doing this right or am I missing the idea completly?
I have been following these two articles:
https://tylermcginnis.com/react-router-protected-routes-authentication/
https://auth0.com/blog/react-router-4-practical-tutorial/
You are getting this error beacause fakeAuth is not exported from App.js file. Put the fakeAuth object into a separate file i.e. auth.js and export it from there.
// auth.js
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
signout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
export { fakeAuth }
Then, import it in App.js and Login.js component like this.
import { fakeAuth } from './path-to-auth-js-file';
Related
I am making a basic routing app in react and redux and i am getting an error when i want to redirect to the referrer.
Error happens in the line:
this.referrer = props.location.state.referrer || "/";
This is my Login component.
import React, { Component, createRef } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import axios from "axios";
import { updateAuthTokens, updateLoggedin } from "./redux/actions/actions";
class Login extends Component {
constructor(props) {
super(props);
this.idRef = createRef();
this.pwdRef = createRef();
this.state = {
isLoggedIn: false,
isError: false,
identifier: "",
password: "",
};
this.postLogin = this.postLogin.bind(this);
this.referrer = props.location.state.referrer || "/";
}
postLogin(e) {
e.preventDefault();
const identifier = this.idRef.current.value;
const password = this.pwdRef.current.value;
axios
.post(process.env.REACT_APP_BASE_URL + "/auth/local", {
identifier,
password,
})
.then((result) => {
if (result.status === 200) {
this.props.ul(true);
this.props.uat(result.jwt);
this.setState({ isLoggedIn: true });
} else {
this.setState({ isError: true });
}
})
.catch((e) => {
this.setState({ isError: true });
});
}
render() {
if (this.state.isLoggedIn) {
return <Redirect to={this.referrer} />;
} else {
return (
<div>
<form>
<input type="username" ref={this.idRef} placeholder="email" />
<input type="password" ref={this.pwdRef} placeholder="password" />
<button onClick={this.postLogin}>Sign In</button>
</form>
{this.state.isError && (
<p>The username or password provided were incorrect!</p>
)}
</div>
);
}
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
const mapDispatchToProps = {
ul: updateLoggedin,
uat: updateAuthTokens,
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Redirect happens from this component:
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
class PrivateRoute extends React.Component {
render() {
const { isLoggedIn, children, ...rest } = this.props;
if (isLoggedIn) {
return <Route {...rest}>{children}</Route>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: { referrer: this.props.location }
}}
/>
);
}
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
export default connect(mapStateToProps, null)(PrivateRoute);
And my routes are here:
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { connect } from "react-redux";
import Home from "./Home";
import Admin from "./Admin";
import Members from "./Members";
import Login from "./Login";
import PrivateRoute from "./PrivateRoute";
import "./App.css";
class App extends Component {
render() {
return (
<Router><div>
<ul>
<li>
<Link to="/">Home Page</Link>
</li>
<li>
<Link to="/members">Members</Link>
</li>
<li>
<Link to="/admin">Admin</Link>
</li>
</ul>
</div>
<Switch>
<Route path="/login">
<Login />
</Route>
<Route exact path="/">
<Home />
</Route>
<PrivateRoute path="/members">
<Members />
</PrivateRoute>
<PrivateRoute path="/admin">
<Admin />
</PrivateRoute>
</Switch>
</Router>
);
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
export default connect(mapStateToProps, null)(App);
I haven't been able to get it to work and have had to comment it out which means i always end up in the home page instead of my intended location.
Any help/insight would be appreciated.
for accessing location as props change:
<Route path="/login">
<Login />
</Route>
<Route exact path="/">
<Home />
</Route>
<PrivateRoute path="/members">
<Members />
</PrivateRoute>
<PrivateRoute path="/admin">
<Admin />
</PrivateRoute>
to
<Route path="/login" component={Login} />
<Route exact path="/" component={Home} />
<PrivateRoute path="/members" component={Members} />
<PrivateRoute path="/admin" component={Admin} />
You are not going to always redirect so change:
this.referrer = props.location.state.referrer || "/";
to
this.referrer = props.location.state ? props.location.state.referrer : "/";
Hi there i have created a protected route in and passing navbar as parent component. then i passing child components in protected rout i want to hide navbar in some component on some action
Here is my protected.router.js code
import React from "react";
import { Route, Redirect } from "react-router-dom";
import Navbar from './component/Layouts/Navbar';
export const ProtectedRoute = ({
component: Component,
...rest
}) => {
return (
<Route
{...rest}
render={props => {
if (localStorage.getItem("loggedAsli") === "1") {
return <div> <Navbar/>
<Component {...props} /> </div>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: {
from: props.location
}
}}
/>
);
}
}}
/>
);
};
And this my App.js code
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import { ProtectedRoute } from "./protected.route";
import Learn from "./component/Learn/Learn";
class App extends React.Component {
constructor(props) {
super();
this.updateUserLoginSetting = this.updateUserLoginSetting.bind(this);
this.state = {
userId: "",
instituteId: ""
};
}
updateUserLoginSetting = (userId, instituteId) => {
this.setState({ userId: userId, instituteId: instituteId });
};
render() {
return (
<BrowserRouter>
<div className="App">
<Route render={props => <Login />} exact path="/login" />
<ProtectedRoute exact path="/Learn" component={Learn} />
</div>
</BrowserRouter>
);
}
}
export default App;
How i can hide navbar in Learn Component
Please guide me.
is there any global state handling
You could just pass a prop (e.g. showNav) and use it inside ProtectedRoute
export const ProtectedRoute = ({
component: Component,
showNav = true, // added showNav prop
...rest
}) => {
return (
<Route
{...rest}
render={props => { // check if should showNav
if (localStorage.getItem("loggedAsli") === "1" && showNav) {
return <div> <Navbar/>
<Component {...props} /> </div>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: {
from: props.location
}
}}
/>
);
}
}}
/>
);
};
And pass false when it's Learn
// pass showNav as false to hide nav bar
<ProtectedRoute showNav={false} exact path="/Learn" component={Learn} />
import React, { Component } from "react";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import { library } from "#fortawesome/fontawesome-svg-core";
import {
faHome,
faClock,
faTasks,
faStickyNote,
faCalendarWeek
} from "#fortawesome/free-solid-svg-icons";
import { connect } from "react-redux";
import store from "./store";
import { loadUser } from "./actions/authActions";
import Home from "./Home";
import SideNav from "./Components/SideNav";
import Recent from "./Components/Recent";
import TopBar from "./Components/TopBar";
import AddNote from "./AddNote";
import LogIn from "./Components/LogIn/LogIn.js";
import Register from "./Components/Register/Register";
import ToDo from "./Components/ToDo/ToDo";
import { timingSafeEqual } from "crypto";
library.add(faHome, faClock, faTasks, faStickyNote, faCalendarWeek);
class App extends Component {
componentDidMount() {
store.dispatch(loadUser());
}
componentDidUpdate(prevProps) {
if(this.props != this.prevProps) {
console.log("hello")
}
}
LogInContainer = () => {
return <Route path="/login" component={LogIn} />;
};
RegisterContainer = () => {
return <Route path="/register" component={Register} />;
};
DefaultContainer = () => {
return (
<div className="app_container">
<SideNav />
<TopBar />
<Route exact path="/" component={Home} />
<Route path="/recent" component={Recent} />
<Route path="/AddNote" component={AddNote} />
<Route path="/ToDo" component={ToDo} />
</div>
);
};
// Check for authenticaition
AuthRoute = ({ component: Component, props, ...rest }) => {
return (
<Route
{...rest}
render={props => {
if (this.props.auth.isAuthenticated) {
return <Component {...props} />;
}
else {
return (
<Redirect
to={{
pathname: "/login",
state: { from: this.props.location }
}}
/>
);
}
}}
/>
);
};
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/login" component={this.LogInContainer} />
<Route exact path="/register" component={this.RegisterContainer} />
<this.AuthRoute component={this.DefaultContainer} />
</Switch>
</BrowserRouter>
);
}
}
const mapStateToProps = (state) => ({
auth: state.auth
})
export default connect(mapStateToProps)(App);
How can app.js receive the new state from redux after logging in? The initial fetch it will get isAuthenticated = false. User then log ins but app.js isn't getting the new state. Am I implementing authenitcation wrong? comonentDidUpdate is throwing an error when trying to update props but feel like this is a bad way of doing it anyways
I have a React front-end and Python back-end with user session management using flask-login's HttpOnly Session Cookies. How can I restrict react-router-dom routes based on this type of session management? I've created a ProtectedRoute component:
import { Route, Redirect } from 'react-router-dom';
class ProtectedRoute extends Component {
constructor(props) {
super(props);
this.state = {
authenticated: false,
}
}
render() {
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.state.authenticated ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
export default ProtectedRoute;
Is it possible to set this.setState({authenticated: true}) based on the existing session?
Why not pass authenticated (or isEnabledin my example) as a prop? ProtectedRoute will rerender when its props change. This is what I use in my React applications:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const ProtectedRoute = ({isEnabled, ...props}) => {
return (isEnabled) ? <Route {...props} /> : <Redirect to="/login"/>;
};
export default ProtectedRoute;
And then you can just use this like so:
<ProtectedRoute path="/dashboard" isEnabled={isAuthenticated()} />
I know this question is old but I was looking for the same thing. Here's my routes.js file:
import auth from './services/auth'
const PrivateRoute = ({isAuthenticated, ...props}) => {
return (isAuthenticated) ? <Route {...props} /> : <Redirect to="/login"/>;
};
class Routes extends React.Component {
constructor(){
super();
this.state = {
isAuthenticated: false
}
}
componentDidMount(){
auth.get('')
.then( async (response) => {
const status = await response.status
if (status === 200) {
this.setState({isAuthenticated: true})
} else {
this.setState({isAuthenticated: false})
}
})
.catch( async (error) => console.log(error))
}
render() {
return (
<BrowserRouter>
<Switch>
<Route path="/login" exact component={Login} />
<PrivateRoute isAuthenticated={this.state.isAuthenticated} path="/" component={() => "barra"}/>
<PrivateRoute isAuthenticated={this.state.isAuthenticated} path="/home" component={() => "home"}/>
<PrivateRoute isAuthenticated={this.state.isAuthenticated} path="/profile" component={() => "profile"}/>
</Switch>
</BrowserRouter>
)
};
}
And auth import is:
const axios = require('axios');
axios.defaults.withCredentials = true;
const auth = axios.create({
baseURL: "http://localhost:5000/auth"
})
export default auth;
So, basically I have a Flask app with Flask-Login running on another local server (with CORS enabled THIS IS VERY IMPORTANT) and if 200 is returned, user on react is authenticated.
When a user tries to view a private page(/products/1/edit), he will be redirected to the login component.
After login i want to redirect the user to the same product edit page. **The problem is that I am unable to get the location props in the component. It is returning as undefined.
** The code is as follows.
App.js
class App extends React.Component {
render() {
return (
<div className="App">
<AppRouter />
</div>
);
}
}
export default App;
AppRouter.js
import { ConnectedRouter } from 'react-router-redux';
...
class AppRouter extends Component {
componentDidMount() {
this.props.checkAlreadyLoggedIn();
}
render() {
const { token, location } = this.props;
return ((
<ConnectedRouter history={history}>
<Layout style={{ minHeight: '100vh' }}>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" render={ () => token && <Redirect to=
{
location.state && location.state.from ? location.state.from.pathname :
"/dashboard"
} /> || <Login />} />
<PrivateRoute path="/dashboard" component={Dashboard} />
...
</Switch>
</Layout>
</Layout>
</ConnectedRouter>
));
}
}
history.js
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
export default history;
i am already defined the private route as follows
class PrivateRoute extends PureComponent {
static propTypes = {
token: PropTypes.string,
component: PropTypes.any
}
render() {
const { component: Component, ...rest } = this.props;
let token = utils.getUserToken();
return (
<Route
{...rest}
render={ () =>
(token && <Component {...this.props} />) ||
<Redirect to={{
pathname: '/login',
state: { from: this.props.location }
}}/>
}
/>
)
}
}
Only if i get the location.state.from.pathname, i will be able to redirect the user to the page that he tried to access prior to login.
Any idea on how to fix this?
This is the PrivateRoute implementation I use and it performs redirect:
import React from 'react';
import { Route, Redirect } from "react-router-dom";
import { isLoggedIn } from '../../utils/authHelper';
class PrivateRoute extends React.Component {
renderComponent = () => {
const { path, component: Component } = this.props;
return (
isLoggedIn()
? <Component {...this.props} />
: <Redirect to={{
pathname: '/login',
state: { from: path }
}} />
)
}
render (){
const { component: Component, ...rest } = this.props;
return (
<Route {...rest} render={this.renderComponent} />
)
}
}
export default PrivateRoute;
And usage is like:
<Switch>
<Route path='/login' component={LoginScreen} />
<PrivateRoute path='/profile' component={ProfileComponent} />
</Switch>
And on /login route you will get the original url via this.props.location.state.from:
...
doLogin(username, password)
.then(() => {
window.location.href = `/${this.props.location.state.from}`;
});
Using react hooks useHistory() method history.goBack()
...
login(userData);
history.goBack();