Unable to pull token from auth middleware in node.js

Viewed 30

I am building a simple flashcard program using the MERN stack that allows users to add, edit and remove flashcards. Everything is working fine, except my deleteCards function isn't working and is throwing an error.

I can't for the life of me work out why, though, because the function is structured exactly the same as all of the others.

The problem seems to be in my auth middleware, but as far as I can tell it's being passed the same header authorization details.

The middleware function in question is this, and you can see that it's causing the error in the no token conditional:

const protect = asyncHandler(async (req, res, next) => {
    let token

    if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
        try {
            //get token from header
            token = req.headers.authorization.split(' ')[1]
            //verify token
            const decoded = jwt.verify(token, process.env.JWT_SECRET)
            //get user from the token
            req.user = await User.findById(decoded.id).select('-password')
            next()
        } catch (error) {
            console.log(error)
            res.status(401)
            throw new Error('not authorised')
        }
    }

    if (!token) {
        res.status(401)
        throw new Error('not authorised, no token')
    }
})

However, if I put console logs in, I can see that the first try/catch is finding the token, so I don't understand why the second error is being called at all.

The backend function works fine when I call it in postman, so my assumption is that something is going wrong on the front end when it's passed to the server.

That is happening with this slice:

//delete user card
export const deleteCard = createAsyncThunk(
  "cards/deleteCard",
  async (cardData, thunkAPI) => {    
    try {
      const token = thunkAPI.getState().auth.user.token;
      return await cardService.deleteCard(cardData, token);
    } catch (error) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString();
      return thunkAPI.rejectWithValue(message);
    }
  }
);

And then this service:

//delete card
const deleteCard = async (cardData, token) => {
  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  };

  const response = await axios.delete(API_URL, cardData, config);

  return response.data;
};

If I console.log either config in the later, or token and cardData in the former, it displays exactly what I'd expect.

And as I say, I have an almost identical 'updateCard' function that works absolutely fine - so I can't tell what is different. That one, for reference, is here:

//slice
export const updateCard = createAsyncThunk(
  "cards/updateCard",
  async (cardData, thunkAPI) => {
    try {
      const token = thunkAPI.getState().auth.user.token;
      return await cardService.updateCard(cardData, token);
    } catch (error) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString();
      return thunkAPI.rejectWithValue(message);
    }
  }
);

//service
const updateCard = async (cardData, token) => {
  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  };

  const response = await axios.put(API_URL, cardData, config);

  return response.data;
};

The full code is on GitHub here if it seems like there's maybe something going on somewhere else! https://github.com/gordonmaloney/cairtean

1 Answers

Note: this is not answer, but more of helping you to find where exactly its throwing error.

First replace protect middle-ware with this

// protect middleare
async function isAuthenticated(req, res, next) {
  try {
    const authHeader = req.get('Authorization')
    if (!authHeader) throw new Error('missing auth header')
    if (!authHeader.startsWith('Bearer ')) throw new Error('invalid authorization scheme')

    const token = authHeader.split(' ')[1]
    const decoded = jwt.verify(token, process.env.JWT_SECRET)
    if (!decoded.id) throw new Error('unexpected error in token')

    req.user = await User.findById(decoded.id).select('-password')
    next()
  } catch (error) {
    console.error(error)
    req.user = null
    res.status(401).json({ message: 'unauthorized' })
  }
}
// usage: app.use(isAuthenticated), no need to  use `asyncHandler`

Then handle your back-end error better, with error middleware.


// NOTE: dont convert this middleare to arrow function or changing number of arguments. 
// express looks for `function.arguments` which must be equal `4` but in arrow functions it will be `undefined`
// Also this must be last middlware, so call it at end.

function finalErrorMiddleware(error, req, res, next) {
  console.log('finalErrorMiddleware has caught some error')
  console.error(error)
  if (error instanceof Error)
    return res.status(400).json({ name: error.name, message: error.message })
  return res.status(500).json({ name: 'error', message: 'server_error' })
}

// usage: app.use(finalErrorMiddleware)

now you should be seeing all missing errors in your console.

Related