Possible to return 401 instead of 403 for expired token?

282 Views Asked by At

I want to be able to return a 401 on JWT token expiry (which I believe is the correct response?) but it returns a 403 no matter what

here is where I tell swagger tools to use my bearer token verification logic:

swaggerTools.initializeMiddleware(swaggerObject, (middleware) => {
            // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
            app.use(middleware.swaggerMetadata());

            //enable cors
            app.use(
              cors({
                methods: ["GET", "POST", "PATCH", "DELETE"],
                origin: "*",
                preflightContinue: false,
                optionsSuccessStatus: 204,
              })
            );

            //verify bearer token for protected apis
            app.use(
              middleware.swaggerSecurity({
                Bearer: (req, authOrSecDef, token, callback) =>
                  //if this is a protected endpoint then token is verified to allow or deny access
                  securityService.verifyToken(
                    req,
                    authOrSecDef,
                    token,
                    callback
                  ),
              })
            );

and here is my verifyToken function..

verifyToken: (req, authOrSecDef, token, callback) => {
          const sendError = (error) => {
            if (error) {
              if (error.name === "TokenExpiredError") {
                return new Problem(401, error.message);
              }
            } else {
              return new Problem(403);
            }
          };

          if (token && token.toLowerCase().indexOf("bearer ") === 0) {
            var tokenString = token.split(" ")[1];

            let completeDecodedToken = jwt.decode(tokenString, { complete: true });

            if (!completeDecodedToken) {
              return callback(sendError());
            }

            //use header.kid to find correct cognito jwk to use
            let jwk = jwks.keys.find(
              (jwk) => jwk.kid == completeDecodedToken.header.kid
            );

            if (!jwk) {
              //must have passed in a token obtained from somewhere else
              return callback(sendError());
            }
            const pem = jwkToPem(jwk);

            jwt.verify(
              tokenString,
              pem,
              { algorithms: ["RS256"] },
              (verificationError, decodedToken) => {
                if (verificationError === null) {
                  // check if the issuer matches
                  var issuerMatch =
                    decodedToken.iss ===
                    `https://cognito-idp.${process.env.AWS_REGION}.amazonaws.com/${process.env.COGNITO_USER_POOL_ID}`;

                  if (issuerMatch) {
                    //add the token to the request so that we
                    //can access it downstream in endpoint if we need
                    req.auth = decodedToken;
                    req.tokenString = tokenString;
                    //if there is no error, just return null in the callback
                    return callback(null);
                  } else {
                    console.error("Issuer did not match");
                    //return the error in the callback if there is one
                    return callback(sendError());
                  }
                } else {
                  //return the error in the callback if the JWT was not verified
                  return callback(sendError(verificationError));
                }
              }
            );
          } else {
            return callback(sendError());
          }
        },

but actually when I look in the swagger-tools source (swagger-security.js) I only see 403 in there.. any advice ?

1

There are 1 best solutions below

0
On

You see 403 always because that is the else part and your error object is null, If you are able to debug then you can console log the error

Also your assumption is that error is returned from below line, which is highly likely wrong because error is null.

//return the error in the callback if the JWT was not verified
              return callback(sendError(verificationError));

In my opinion, error is returned from

else {
        return callback(sendError());
      }

If that is the case then you can send your custom "UnauthorizedError" object from the desired place.