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;
Here's one way to go about this by wrapping your component route in an authorization component:
<Route
path="/somePathToProtect"
element={
<RequireAuth>
<Dashboard />
</RequireAuth>
}
/>
export function RequireAuth({children}) {
const navigate = useNavigate();
const [isAuth, setIsAuth] = useState(null);
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(
() => setIsAuth(true)
)
.catch(() => {
navigate('/routeToCatchNonAuth')
})
}, [])
return isAuth && children;
}
The goal here is to gatekeep your route based on what Auth
returns. If Auth
takes the catch route, utilize the router to navigate the user to wherever you want unauthorized users to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With