node.jsreduxpassport.jsfetch-apipassport-google-oauth2

Endpoints not authenticating with Fetch API calls (using passport-google-oauth2)


I have passport set up to use the Google strategy and can direct to the /auth/google great. I currently have it so that when you log in using the google authentication oauth2, my endpoints will authenticate by checking for a req.user. This works when I'm just getting to the endpoints in my browser. If I go to /auth/google and then /questions, I'll be able to make that get request. However when I try to make a fetch request from redux, I will get an error message saying Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0. It comes up because the fetch API tries to get to my /questions endpoint, goes through my loggedIn middleware and then doesn't meet the if (!req.user) and gets re-directed instead. Any ideas on how to authenticate from the Fetch API with PassportJS and passport-google-oauth2?

The loggedIn function:

function loggedIn(req, res, next) {
  if (req.user) {
    next();
  } else {
    res.redirect('/');
  }
}

Here is my code for my 'GET' endpoint.

router.get('/', loggedIn, (req, res) => {
  const userId = req.user._id;

  User.findById(userId, (err, user) => {
    if (err) {
      return res.status(400).json(err);
    }

    Question.findById(user.questions[0].questionId, (err, question) => {
      if (err) {
        return res.status(400).json(err);
      }

      const resQuestion = {
        _id: question._id,
        question: question.question,
        mValue: user.questions[0].mValue,
        score: user.score,
      };

      return res.status(200).json(resQuestion);
    });
  });
});

The redux fetch request:

function fetchQuestion() {
  return (dispatch) => {
    let url = 'http://localhost:8080/questions';
    return fetch(url).then((response) => {  
      if (response.status < 200 || response.status >= 300) {
        let error = new Error(response.statusText);
        error.response = response;
        throw error;
      }
      return response.json();
    }).then((questions) => {
      return dispatch(fetchQuestionsSuccess(questions));
    }).catch((error) => {
      return dispatch(fetchQuestionsError(error));
    }  
  };
}

Solution

  • The Fetch API doesn't send cookies by default, which Passport needs to confirm the session. Try adding the credentials flag to ALL your fetch requests like so:

    fetch(url, { credentials: 'include' }).then...

    or if you aren't doing CORS requests:

    fetch(url, { credentials: 'same-origin' }).then...