I'm trying to implement basic authentication in my react application. Where I sent the email
and password
in the header while making a GET
request to my /users
endpoint and according to the response we decide if the login is successful or not. If the login is successful (i.e the user exists at the endpoint) then we push to the <projects>
component. But I want the user to only be able to access the /projects
url if he's a valid user. How do I apply private routes
This is how my login page code looks function LogIn() {
const [password, setPassword] = React.useState("");
const [email, setEmail] = React.useState("");
const [err, setErr] = React.useState(null);
const history = useHistory();
const handleSubmit = async (event, password, email) => {
event.preventDefault();
var myHeaders = new Headers();
myHeaders.set('Authorization', 'Basic ' + encode(email + ":" + password));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let response;
try {
response = await fetch (`${APIlink}/users`, requestOptions)
} catch (err) {
setErr("Incorrect Password. Please Retry.");
return;
}
const result = await response.text();
console.log(result);
const json = JSON.parse(result);
console.log(json);
console.log(response);
if (response.status===200) {
setErr(null);
history.push("/Projects"); //valid user is redirected but /Projects is accessible by just
//writing the url as well
} else {
setErr(json.error);
console.log(json.error);
}
};
This is how my json object looks after sending the right credentials (email: abc, password: test
)
{
"password": "test",
"rollno": "18am200",
"email": "abc",
"name": "mkyong",
"user-uid": "7e7199247de1125a6dc1518dd78ba554"
}
This is how my response looks like
Response { type: "cors", url: "{APIlink/users", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: true }
App.js
function App() {
return (
<div >
<HashRouter basename ='/'>
<Switch>
<Route path="/" component = {Home} exact/>
<Route path="/Login" component = {LogIn}/>
<Route path="/Register" component = {Register}/>
<Route path="/Projects" component = {ProjectComponent} />
<Route path="/Application" component = {Project2Component} />
<Route path="/Demo1" component = {Project3Component} />
<Route path="/Demo2" component = {Project4Component} />
</Switch>
</HashRouter>
</div>
)
}
export default App
You can set a persistent value (e.g. isAuthenticated = true
) to your LocalStorage after a successful response of login. But make sure to remove the value after user logs out (e.g. isAuthenticated = false
). Then you can check the value each time the user changes his/her route.
I have added below some basic example for you -
// Login.js
/* other codes ... */
if (response.status===200) {
localStorage.setItem('isAuthenticated', true);
history.push("/Projects");
};
/* other codes ... */
// Logout.js
localStorage.removeItem('isAuthenticated');
// AuthRequired.js
import React, { Fragment } from "react"
import { Redirect } from "react-router-dom"
export default function AuthRequired(props){
const isAuthenticated = localStorage.getItem('isAuthenticated')
if(isAuthenticated){
return props.orComponent;
} else {
return <Redirect to="/Login"/>
}
}
// App.js
import { Route, Switch } from "react-router-dom"
import AuthRequired from "./AuthRequired"
/* Your other codes and imports.. */
const publicRoutes = [
{
path: "/Login",
exact: true,
component: LogIn
},
{
path: "/",
exact: true,
component: Home
},
];
const authRequiredRoutes = [
{
path: "/Projects",
exact: true,
component: <ProjectComponent/>
},
// ... other routes
]
const pathsForLayout = routes => routes.map(route => route.path)
function App() {
return (
<div >
<HashRouter basename ='/'>
<Switch>
<Route exact path={pathsForLayout(publicRoutes)}>
<Switch>
{
publicRoutes.map((route,index) => (
<Route
key={index}
exact={route.exact}
path={route.path}
component={route.component}
/>
))
}
</Switch>
</Route>
<Route exact path={pathsForLayout(authRequiredRoutes)}>
<Switch>
{
authRequiredRoutes.map((route,index) => (
<Route
key={index}
exact={route.exact}
path={route.path}
render={() => (
<AuthRequired
{...props}
orComponent={route.component}
/>
)}
/>
))
}
</Switch>
</Route>
<Route component={NotFound} /> {/* Your custom 404 page */}
</Switch>
</HashRouter>
</div>
)
}
export default App
Note - I've followed your naming convention for route. That's why I also kept these as PascalCase. Though the recommended way to to declare path
of <Route/>
is by using kebab-case. (e.g. path="/login"
).