I am trying to build a web app with AWS Amplify. I have authentication configured, but I want certain pages to be for authenticated users only e.g. anyone can see the home page, but only logged in users should see "/dashboard"
. I am currently using AWS Amplify as my backend and a React front-end, using react-router
v6 to route between pages.
Currently, my routing code is very naive (it is my first time using React), and is in App.js:
import React from 'react';
import {
BrowserRouter,
Route,
Routes,
} from 'react-router-dom';
import Login from './pages/Login';
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import ErrorPage from './pages/ErrorPage';
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="*" element={<ErrorPage />} />
</Routes>
</BrowserRouter>
);
}
export default App;
I first tried wrapping the page I want gated with withAuthenticator
, but this just caused a loop of seeing the login box.
function Dashboard({ signOut, user }) {
return (
<>
<h1>Hello {user.username}, this is still in development.</h1>
<button onClick={signOut}> Sign out</button>
</>
);
}
export default withAuthenticator(Dashboard);
I have also tried to add a function to check if the user is authenticated, and return different bodies, but this just shows a white screen for both an authenticated and non-authenticated users. I believe it is because it is async
, but I am not familiar enough with react to understand why or how to fix it.
async function isAuthed() {
try {
await Auth.currentAuthenticatedUser();
return true;
} catch(e) {
return false;
}
}
async function Dashboard() {
if (await isAuthed()) {
return (
<>
<h1>Hello, this is still in development.</h1>
</>
);
} else {
return (
<>
<h1>Please login to view this page.</h1>
</>
)
}
}
I have also tried to see if there is some method of asynchronous routing, but not sure how I would implement that.
Edit:
@Jlove's solution has worked as intended, my updated App.js
routing code is now:
import React, { useState, useEffect } from 'react';
import {
BrowserRouter,
Route,
Routes,
useNavigate,
} from 'react-router-dom';
import { Amplify, Auth } from 'aws-amplify'
import Login from './pages/Login';
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import ErrorPage from './pages/ErrorPage';
import Unauthenticated from './pages/Unauthenticated';
function RequireAuth({ children }) {
const navigate = useNavigate();
const [isAuth, setIsAuth] = useState(null);
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(() => setIsAuth(true))
.catch(() => {
navigate("/unauthenticated")
})
}, [])
return isAuth && children;
}
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route
path="/dashboard"
element={
<RequireAuth>
<Dashboard />
</RequireAuth>
}
/>
<Route path="*" element={<ErrorPage />} />
<Route path="/unauthenticated" element={<Unauthenticated />} />
</Routes>
</BrowserRouter>
);
}
export default App;