wd-David / snowball

A simple side project for personal accounting.
snowball-front-end.vercel.app
4 stars 1 forks source link

Early return the possible errors on passport-local authentication process #22

Open AdrieneTZ opened 1 year ago

AdrieneTZ commented 1 year ago

Now the practice of authenticating errors is in user-controller.logIn. I want to move those processes to passport.js because going into the user-controller without passing authentication is so weird. Also, it's not logical that let the controller check the possible errors. The only work user-controller/logIn should do is ask JWT to sign a token and return this token.

In ./config/passport.js, there is no argument called res that does the early return process when any possible error happens, and it's unable to customize error conditions in passport-local strategy. I'm still figuring out how to add an early return process and meanwhile not necessarily create too many middlewares (functions).

AdrieneTZ commented 1 year ago

related question on stackoverflow

AdrieneTZ commented 1 year ago

about passport local link 1

wd-David commented 1 year ago

This should work after I check the stackoverflow issue & official doc:

In case of authentication failure, the verify callback supplies a message, via the message option, describing why authentication failed. This will be displayed to the user when they are re-prompted to sign in, informing them of what went wrong.

back-end/config/passport.js

-if (!user) return callbackFn(null, false)
+if (!user) return callbackFn(null, false, { message: 'User not found.' })
...
-isValidPassword ? callbackFn(null, user) : callbackFn(null, false)
+isValidPassword ? callbackFn(null, user) : callbackFn(null, false, { message: 'Password is wrong. '})

By passing a third arg: { message: 'YOUR_ERROR_MESSAGE' } to callbackFn, we can catch it like this:

back-end/routes/modules/user.js

router.post(
  '/logIn',
-passport.authenticate('local', { session: false }),
-userController.logIn
+(req, res, next) => {
+  passport.authenticate('local', { session: false }, (err, user, info) => {
+    if (!user) return res.status(401).json(info)
+    // Sign a token
+    const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '7d' })
+    return res.status(200).json({ token })
+  })(req, res, next)
+}
)

Simply remove the userController.logIn and replace the second arg passport.authenticate('local', { session: false }) with an anonymous arrow function:

(req, res, next) => {
  passport.authenticate('local', { session: false }, (err, user, info) => {
    // (err, user, info) here is corresponding to the 
    // callbackFn(null, user) or callbackFn(null, false, { message: "wrrrrr" })

    // Below will reply error message like "User not found." or "Password is wrong."
    // Depends on your local strategy in "passport.js"
    if (!user) return res.status(401).json(info)

    // Sign a token
    const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '7d' })
    return res.status(200).json({ token })
  })(req, res, next)
}