Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirect non-authenticated users, AWS Amplify

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;
like image 899
supinie Avatar asked Sep 13 '25 21:09

supinie


1 Answers

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.

like image 85
Jlove Avatar answered Sep 16 '25 11:09

Jlove