mikenicholson / passport-jwt

Passport authentication using JSON Web Tokens
MIT License
1.97k stars 215 forks source link

JWT Strategy sending API call to incorrect routes #196

Closed alessandro-pianetta closed 4 years ago

alessandro-pianetta commented 4 years ago

I used this tutorial to implement auth in my server. For the authenticated GET and DELETE routes I have defined, it's working like a charm. However, when I try to send a POST or PATCH request to an authenticated route, for some strange reason it defaults to the first authenticated route I defined, a GET route. I then get a 401 error saying "Cannot GET [route for the POST request I initiated]".

I use some wrappers around my DB call using axios.[get/post/etc] to set the headers/have my database's URL pre-loaded in, and I've confirmed that the headers are correct right before I actually make the call. In my dev tools though, I don't see the Authorization header actually go through. I would think it's a problem of my headers not being properly set, but I use the exact same pattern for PATCH and DELETE (with the only difference being the axios method) and the headers don't show up in the request for the former, but do for the latter, so I'm kind of stuck.

Can anyone help me figure out what's going wrong?

const ExtractJwt = passportJWT.ExtractJwt;
const JwtStrategy = passportJWT.Strategy;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
jwtOptions.secretOrKey = 'tasmanianDevil';

passport.use(
  new JwtStrategy(jwtOptions, (jwt_payload, next) => {
    try {
      const { id } = jwt_payload;
      db.User.getUserById(id, (err, user) => {
        if (user) {
          next(null, user);
        } else {
          next(null, false);
        }
      });
    } catch (error) {
      console.warn(error);
    }
  })
);

/// /api/me is defaulted to on POST and PATCH requests

app.get(
  '/api/me',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    try {
      const { user } = res.req;
      const response = {
        _id: user._id,
        email: user.email,
        dungeons: user.dungeons,
      };
    } catch (error) {
      console.warn(error);
    }
  }
);

// This route defaults to GET request above

app.post(
  '/api/users/:userID/dungeons/',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    try {
      const { userID } = req.params;
      const { dungeonName, equipment } = req.body;
      const newDungeon = new db.Dungeon({
        dungeonName,
        equipment,
      });

      db.User.findById(userID, (err, existingUser) => {
        if (existingUser) {
          if (err) {
            res.status(500).send('Database Error');
          } else {
            const dungeonDoesExist = existingUser.dungeons.find(
              existingDungeon => existingDungeon.dungeonName === dungeonName
            );
            if (!dungeonDoesExist) {
              existingUser.dungeons.push(newDungeon);
              existingUser.save(() => {
                res
                  .status(200)
                  .send(`${dungeonName} added to ${existingUser.email}`);
              });
            } else {
              res
                .status(500)
                .send(`Database Error: ${dungeonName} already exists`);
            }
          }
        } else {
          res.status(404).send('User not found');
        }
      });
    } catch (error) {
      console.warn(error);
    }
  }
);

// DELETE route does work

app.delete(
  '/api/users/:userID/dungeons/:dungeonID',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    try {
      const { userID, dungeonID } = req.params;
      db.User.findById(userID, (err, existingUser) => {
        if (existingUser) {
          if (err) {
            res.status(500).send('Database Error');
          } else {
            const dungeonDoesExist = existingUser.dungeons.find(
              existingDungeon => existingDungeon.id === dungeonID
            );
            if (dungeonDoesExist) {
              const foundDungeon = existingUser.dungeons.id(dungeonID);
              foundDungeon.remove();
              existingUser.save((err, savedUser) => {
                res.status(200).send('Dungeon removed');
              });
            } else {
              res.status(404).send('Dungeon not found');
            }
          }
        } else {
          res.status(404).send('User not found');
        }
      });
    } catch (error) {
      console.warn(error);
    }
  }
);
mikenicholson commented 4 years ago

The issues your raising don't appear to have anything to do with this module. If you have found a reproducible problem with this module, please open an issue with a succinct code example and/or detailed instructions to reproduce.

For general debugging and coding help consider resources like Stack Overflow.