How to restrict pages to logged in users only - reactjs

I want to protect some urls so users can't access them without login in first. This is my Auth.js
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
firebase.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider
value={{ currentUser }}
>
{children}
</AuthContext.Provider>
);
};
And this is my App.jsx
function App() {
return (
<AuthProvider>
<Router>
<div>
<Route path="/citas" exact>
<CitasPage />
</Route>
<Route path="/citasDetalladas" exact>
<CitaDetallada />
</Route>
<Route path="/crearCita" exact>
<CrearCitasPage />
</Route>
<PrivateRoute path="/index" exact >
<Index />
</PrivateRoute>
</div>
</Router>
</AuthProvider>
);
}
export default App;
Since I'm new to React, I've been following a tutorial for doing the login and logout using Firebase and React, but it didn't help with this issue. Any ideas?

You can use this auth flow to protect routes from unauthenticated users.
I have created global context with maintain currentUser.
By this currentUser, routes are decided.
AuthProvider.js
import React, { createContext, useState, useEffect } from "react";
import firebase from "./firebase";
export const AuthContext = createContext();
export function AuthProvider({ children }) {
useEffect(() => {
setIsLoading(true);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrentUser(user);
setIsLoading(false);
} else {
setIsLoading(false);
setCurrentUser(null);
}
});
}, [setCurrentUser, setRole, setIsLoading]);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
App.js
import React from "react";
import { HashRouter as Router } from "react-router-dom";
import { AuthProvider } from "./data/auth";
function App() {
return (
<AuthProvider>
<Router>
<Routes />
</Router>
</AuthProvider>
);
}
export default App;
Routes.js
import React, { useContext } from 'react'
import { Switch, Redirect, Route } from 'react-router-dom';
import { AuthContext } from "../data/auth";
const Routes = () => {
const { currentUser } = useContext(AuthContext);
if (currentUser) {
return (
<Switch>
<Route path='/dashboard' component={dashboard} />
<Route path='/report' component={report} />
<Route path='/profile' component={profile} />
<Redirect to='/report' />
</Switch>
)
} else {
return (
<Switch>
<Route path='/login' component={login} />
<Route path='/register' component={register} />
<Redirect to='/login' />
</Switch>
)
}
}
export default Routes;

You need to do something to determine whether the user is currently authenticated. I'm not sure exactly how firebase.auth() works but you need to check on each route (or use a higher-order component) whether the user is authenticated and if not, return <Redirect to='somewhereElse' />

Related

How to use Protected/Private Route in React Router v6 [duplicate]

This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 12 months ago.
I am using react router v6 and creating a private/protected route. Once the user is authenticated and when i refresh the page it shows the following error in console.
[ProtectedRoute] is not a component. All component children of must be a or <React.Fragment>
This is my ProtectedRoute.js file
import React, { Fragment } from "react";
import { useSelector } from "react-redux";
import { Navigate, Route } from "react-router-dom";
const ProtectedRoute = ({ element: Element, ...rest }) => {
const { loading, isAuthenticated, user } = useSelector((state) => state.user);
return (
<Fragment>
{!loading && (
<Route
{...rest}
render={(props) => {
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return <Element {...props} />;
}}
/>
)}
</Fragment>
);
};
export default ProtectedRoute;
And this is my App.js
import "./App.css";
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import WebFont from "webfontloader";
import Header from "./component/layout/Header/Header";
import Footer from "./component/layout/Footer/Footer";
import Home from "./component/Home/Home.js";
import ProductDetails from "./component/Product/ProductDetails.js";
import Products from "./component/Product/Products.js";
import LoginSignUp from "./component/user/LoginSignUp";
import Profile from "./component/user/Profile.js";
import store from "./store";
import { loadUser } from "./actions/userAction";
import UserOptions from "./component/layout/Header/UserOptions.js";
import { useSelector } from "react-redux";
import ProtectedRoute from "./component/Route/ProtectedRoute";
function App() {
const { isAuthenticated, user } = useSelector((state) => state.user);
React.useEffect(() => {
WebFont.load({
google: {
families: ["Roboto", "Droid Sans", "Chilanka"],
},
});
store.dispatch(loadUser());
}, []);
return (
<Router>
<Header />
{isAuthenticated && <UserOptions user={user} />}
<Routes>
<ProtectedRoute path="/account" element={<Profile />} />
<Route path="/login" element={<LoginSignUp />} />
<Route path="/products/:keyword" element={<Products />} />
<Route path="/products" element={<Products />} />
<Route path="/product/:id" element={<ProductDetails />} />
<Route path="/" element={<Home />} />
</Routes>
<Footer />
</Router>
);
}
export default App;
In Protected.js
import React, { Fragment } from "react";
import { useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
const ProtectedRoute = ({ children }) => {
const { loading, isAuthenticated } = useSelector((state) => state.user);
if (loading) return null;
return isAuthenticated ? children : <Navigate to="/login" replace />;
};
export default ProtectedRoute;
In App.js
<Route
path="/account"
element={
<ProtectedRoute>
<Profile />
</ProtectedRoute>
}
/>

Redirect not working with react router authenticated route

I'm attempting to create a private route based on the answers here, however it is not performing the redirect.
This is my private route component
import React, { Component, useContext } from "react";
import { Redirect, Route, RouteComponentProps, RouteProps } from "react-router-dom";
import { RootStoreContext } from "../stores/rootStore";
import { observer } from "mobx-react-lite";
const PrivateRoute: React.FC<RouteProps> = ({ children, ...rest }) => {
const rootStore = useContext(RootStoreContext);
const { isAuthenticated } = rootStore.msalStore;
return (
<Route
{...rest}
render={(props: RouteComponentProps<{}>) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: "/", state: { from: props.location } }} />
)
}
/>
);
};
export default observer(PrivateRoute);
And this is my App.tsx
//Imports
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
When I type /home in the browser, or create a test button that navigates to home, it loads the component even though isAuthenticated = false.
I know it's not related to the value of isAuthenticated in my mobx store as changing to const [isAuthenticated, setIsAuthenticated] = useState(false); doesn't work either. I also have no other instances of routing in my app.
What could be the issue here?
use Switch (from react-router-dom) to wrap routes,
import { Switch } from "react-router-dom";
const App: React.FC = () => {
return (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Switch>
<Route exact path="/" component={LoginPage} />
<Route path="/authcallback" component={AuthCallback} />
<PrivateRoute path="/home" component={HomePage} />
</Switch>
</ThemeProvider>
</BrowserRouter>
</>
);
};
export default App;
This discussion helped me
My solution...
https://codesandbox.io/s/modest-fire-6mj3i?file=/src/PrivateRoute.tsx:0-517
import * as React from "react";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import "./styles.css";
const Home: React.FC = () => {
return <div>Home</div>;
};
const Login: React.FC = () => {
return <div>Login</div>;
};
export default function App() {
return (
<BrowserRouter>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/">Login</Link>
</li>
</ul>
<Switch>
<Route exact path="/" component={Login} />
<PrivateRoute redirectPath="/login" path="/home" component={Home} />
</Switch>
</BrowserRouter>
);
}
import { Redirect, Route, RouteProps } from "react-router";
import React, { useState } from "react";
export interface IPrivateRouteProps extends RouteProps {
redirectPath: string;
}
const PrivateRoute: React.FC<IPrivateRouteProps> = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return isAuthenticated ? (
<Route {...props} component={props.component} render={undefined} />
) : (
<Redirect to={{ pathname: props.redirectPath }} />
);
};
export default PrivateRoute;
If I click Home I get redirected to /login and the Home component is hidden
useState is only used for testing the authenticated state from within the component.

react functional component not get initialised

I have logout component(stateless) in that I want to delete access_token,
But my component not get initialised and there is no routing error as well,
Routing :
<Route path="/logout" component={LogoutComponante} />
logout component:
import React from 'react'
import { connect } from 'react-redux'
import { LOGOUT } from '../constant/constant'
function LogoutComponante(props){ console.log('in logout');
// props.logoutmethod();
// props.history.push("/")
return <h1>abc</h1>;
}
const mapStateToProps = state => {
return {
access_token : state.access_token
}
}
const mapDispatchToProps = dispatch => {
return {
logoutmethod : () => {
dispatch({ type : LOGOUT })
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(LogoutComponante)
Routing details:
<Switch>
<Route exact path="/" component= {Home} />
<RouterManager/>
</Switch>
RouterManager component :
const RouterManager = (props) => {
if(props.access_token){
return (
<div>
<Route path="/logout" component={LogoutComponante} />
</div>
)
}else{
return (
<div>
<Route path="/register" component={Register} />
<Route path="/login" component={Register} />
</div>
)
}
}
when I visit /logout it returns nothing not even console.log('in logout'); is working.
What I am doing wrong?
Try wrapping your redux component with withRouter. Sometimes you will not see ok changes:
import { withRouter, connect } from 'react-redux'
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(LogoutComponante))

Updating redux state in react app.js file Authentication

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

Firebase onAuthStateChanged with React Context Not Updating App

The problem I'm running into is that currentUser in App.js is null when <PrivateRoute /> renders, which will cause it to render a <Redirect /> and navigate to /login.
However, if I view the React Dev Tools I can see that the value field in Context.Provider does correctly have a user object set.This prop should rerender the rest of the app but it does not.
Why doesn't currentUser updating update the routes?
Auth.js
import React, { useEffect, useState } from 'react';
import firebase from 'firebase';
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
firebase.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
};
App.js
import { AuthProvider } from './Auth';
import { AuthContext } from './Auth';
function App() {
const PrivateRoute = ({ component: Component, ...rest }) => {
const { currentUser } = useContext(AuthContext);
return (
<Route
{...rest}
render={props =>
currentUser ? (
<Component {...props} {...rest} />
) : (
<Redirect to={{ pathname: '/login' }} />
)
}
/>
);
};
return (
<AuthProvider>
<Router history={createBrowserHistory()}>
<Switch>
<PrivateRoute path="/" exact component={HomeScreen} />
<Route path="/login" exact component={LogIn} />
</Switch>
</Router>
</AuthProvider>
);
}

Resources