0

I am using Passport and OAuth2 to authenticate a Google user, which returns Google related data in the 'profile' scope format. This is all working fine.

I want to be able to limit who can access my web app, via restricting their access to routes, to only those Google account users who also exist as system users in my database.

I have setup my React app.js with a UseEffect function which firstly does the authentication with Google via a fetch GET method, which in turn returns a Google account ID number. For testing, I have maintained that same ID number against a user record in my database, so I next use an Axios get method to lookup whether that Google id exists in database. If it does exist, it returns the ID number, and if it doesn't exist, it returns (what shows up in console log) as an empty array: both of these scenarios update the validatedUser const.

As an aside, and pre-empt any questions, I know it makes no sense having both fetch and axios get methods, but I followed a tutorial for the Google OAuth functionality which included the fetch method, and I simply haven't worked out how to convert that to axios syntax yet.

My useEffect function then returns routes and (for testing purposes) I have setup conditional logic in the route associated with /members. This logic is meant to check the content of validatedUser and if it is not null, it should allow access to the Members route; and if it is null, it should redirect the user to the 'Login' route.

What is actually happening:

a) If a Google user, that is also maintained in my database, authenticates via Google, validatedUser is updated with their ID as planned. They then have access to the member route. I can see this in console logged as:

     `Validated User =[{"google_id":"*id number here*"}]`

b) If a Google user, that is NOT also maintained in my database, authenticates via Google, my code still runs and 'validatedUser' is updated with []. This empty array is apparently not being interpreted as null by my route-related conditional logic though, so the user also has access to the member route. I can see this console logged as:

     `Validated User =[]`

Below is my code. The following is the relevant part of my app.jsx file:


function App() {
  const client = new QueryClient(); //used for react-query

//Functionality to authenticate google id and return authentication token/object 
  const [user,setUser] = useState(null)
  const [validatedUser, setValidatedUser] = useState([])
 
  useEffect(() => {
    const getUser = () => {
      fetch("http://localhost:3001/auth/login/success", {
        method: "GET",
        credentials: "include",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          "Access-Control-Allow-Credentials": true,
        },
      })
        .then((response) => {
          if (response.status === 200) return response.json();
          throw new Error("Authentication failed");
        })
        .then((resObject) => {
          setUser(resObject.user.id);
        })
        .catch((err) => {
          console.log(err);
        });
    };
    getUser();

//Compares google_id from OAuth with record in my database
    axios
      .get(`http://localhost:3001/auth/user/${user}`)
      .then((res) => res.data)
      .then((data) => setValidatedUser(data));
  }, [user]);

  console.log('User =' + user)
  console.log('Validated User =' + JSON.stringify(validatedUser))

  return (
    <>
      <div className="flex">
        <QueryClientProvider client={client}>
          <Navbar />
          <Routes>
            <Route path="/" element={<Login />} />
            <Route path="/notifications" element={user ? <Notifications /> : <Login />} />       
            <Route path="/members" element={validatedUser !== null ?  <Members /> : <Login />} />
            <Route path="/events" element={user ? <Events /> : <Login />} />
            <Route path="/memberevents" element={user ? <MemberEvents /> : <Login />} />
            <Route path="/staff" element={user ? <Staff />: <Login />} />
            <Route path="/services" element={user ? <Services /> : <Login />} />
            <Route path="/reports" element={user ? <Reports /> : <Login />} />
            <Route path="/settings" element={user ? <Settings /> : <Login />} />
            <Route path="/login/success" element={<LoginSuccess />} />
            <Route path="/login/failed" element={<LoginFailed />} />
            <Route path="/logout" element={<Logout />} />
          </Routes>
        </QueryClientProvider>
      </div>
    </>
  );
}
export default App;

The following is the relevant part of the routes that are called:


    router.get("/user/:id", async (req, res) => {
  const id = req.params.id;
  const validatedUser = await Users.findAll({                 
    where: { google_id: id },
    attributes: ['google_id'],
  })
  res.json(validatedUser);
  //res.json({(validatedUser !== null) ? 'True' : 'False'});
})

router.get("/login/failed", (req, res) => {
  res.status(401).json({
    success: false,
    message: "Logon failure",
  });
});

router.get("/login/success", (req, res) => {
  if (req.user) {
    res.status(200).json({
      success: true,
      message: "Logon successful",
      user: req.user,
      // cookies: req.user About 56 mins in video, talks about sending to cookies - might be useful later.  Or JWT could be written here too.
    });
  }
});

router.get("/logout", (req, res) => {
  req.logout();
  res.redirect("http://localhost:3000");
});

router.get("/google", passport.authenticate("google", { scope: ["profile"] }));

router.get(
  "/google/callback",
  passport.authenticate("google", {
    successRedirect: "http://localhost:3000/login/success",
    failureRedirect: "login/failed",
  })
);

I'm guessing that my issue relates to the empty array being returned to the validatedUser variable not being interpreted as null by my router conditional logic, but I'm not sure how address this. I think it would make most sense if, in the route, after performing the validation against the database record, it conditionally returned true or false - and then I used those values of true/false in the route conditional logic. I tried this approach with the commented out line res.json({(validatedUser !== null) ? 'True' : 'False'}) in that route, but that code just resulted in linting syntax errors - so I presume I'm attempting to do something there in an incorrect way.

Jess
  • 151
  • 12

0 Answers0