Search code examples
node.jsexpressjwtpassport.js

Should I query the database for the JWT user in the middleware in Express.js?"


I have a Node.js application where I'm using Passport.js with JWT strategy. After providing a JWT to the client, the user may become a canceled user. However, since they obtained the JWT beforehand, they can continue making requests to other endpoints until the JWT expires. In this case, is it reasonable to check the user's status in the middleware layer of Passport.js by making an additional database query, or is there a different approach?

const passportStrategy = (passport: {
  use: (arg0: passportJWT.Strategy) => void;
}) => {
  passport.use(
    new JWTStrategy(
      {
        jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
        secretOrKey: process.env.JWT_AUTH_KEY!,
        passReqToCallback: true,
      },
      async (
        req: Request,
        payload: any,
        done: passportJWT.VerifiedCallback
      ) => {
        const src = DataSourceFactory.source;
         //db query in here

        try {
          
          });

          
          return done(null, user);
        } catch (findUserError) {
          return done(findUserError);
        }
      }
    )
  );
};

Solution

  • It might seem like a right approach but doing a db query in the middleware defeats the purpose of making it sessionless. There are alternative standard solutions depending on your use case and the severity of the issue -

    1. Use two tokens, refresh tokens with higher expiry time and access token with smaller expiry In this approach, when user/client tries to fetch a new access token (JWT token) using the refresh token, you can return error based on db query. In this approach, db calls happen only when a new access token is fetched.

    2. Maintain a list of cancelled users in a cache -

             async (
               req: Request,
               payload: any,
               done: passportJWT.VerifiedCallback
             ) => {
               const src = DataSourceFactory.source;
               const cacheUser = redisCache.get("cancelled_user_" + "<user id from token>")
               if (cacheUser) {
                  // throw error
               }
               // Else do your stuff  
                 return done(null, user);
               } catch (findUserError) {
                 return done(findUserError);
               }
             }
      

    In the second approach, you're just querying over cancelled users and that too from cache. The expiry of the cache could be same as the expiry if the token.