export function createBotFrameworkAuthenticationMiddleware()

in packages/app/main/src/server/routes/handlers/botFrameworkAuthentication.ts [45:175]


export function createBotFrameworkAuthenticationMiddleware(fetch: any) {
  const openIdMetadata = new OpenIdMetadata(fetch, authentication.openIdMetadata);
  const usGovOpenIdMetadata = new OpenIdMetadata(fetch, usGovernmentAuthentication.openIdMetadata);

  return async (req: Restify.Request, res: Restify.Response, next: Restify.Next) => {
    const authorization = req.header('Authorization');

    if (!authorization) {
      next();

      return;
    }

    const [authMethod, token] = authorization.trim().split(' ');

    // Verify token
    const decoded: any = /^bearer$/i.test(authMethod) && token && jwt.decode(token, { complete: true });

    if (!decoded) {
      // Token not provided so
      res.status(401);
      res.end();

      return;
    }

    if (decoded.payload.aud === usGovernmentAuthentication.botTokenAudience) {
      // We are talking to a US Gov hosted bot so do validation with that context
      const key = await usGovOpenIdMetadata.getKey(decoded.header.kid);

      let issuer;

      if (decoded.payload.ver === '1.0') {
        issuer = usGovernmentAuthentication.tokenIssuerV1;
      } else if (decoded.payload.ver === '2.0') {
        issuer = usGovernmentAuthentication.tokenIssuerV2;
      } else {
        // unknown token format
        res.status(401);
        res.end();

        return;
      }

      try {
        (req as any).jwt = jwt.verify(token, key, {
          audience: usGovernmentAuthentication.botTokenAudience,
          clockTolerance: 300,
          issuer,

          // TODO: "jwtId" is a typo, it should be "jwtid"
          //       But when we enable "jwtid", it will fail the verification
          //       because the payload does not specify "jti"
          //       When we comment out "jwtId", it also works (because it is a typo)

          // jwtId: botId
        });
      } catch (err) {
        res.status(401);
        res.end();

        return;
      }
    } else {
      // We are talking to a Public Azure host bot so do public Azure verification steps

      const key = await openIdMetadata.getKey(decoded.header.kid);

      if (!key) {
        res.status(500);
        res.end();

        return;
      }

      let issuer;

      if (decoded.payload.ver === '1.0') {
        issuer = v32Authentication.tokenIssuerV1;
      } else if (decoded.payload.ver === '2.0') {
        issuer = v32Authentication.tokenIssuerV2;
      } else {
        // unknown token format
        res.status(401);
        res.end();

        return;
      }

      try {
        // TODO: Turn jwt.verify into async call for better performance
        // first try 3.2 token characteristics
        (req as any).jwt = jwt.verify(token, key, {
          audience: authentication.botTokenAudience,
          clockTolerance: 300,
          issuer,

          // TODO: "jwtId" is a typo, it should be "jwtid"
          //       But when we enable "jwtid", it will fail the verification
          //       because the payload does not specify "jti"
          //       When we comment out "jwtId", it also works (because it is a typo)

          // jwtId: botId
        });
      } catch (err) {
        try {
          // then try v3.1 token characteristics
          (req as any).jwt = jwt.verify(token, key, {
            audience: authentication.botTokenAudience,
            clockTolerance: 300,
            issuer: v31Authentication.tokenIssuer,

            // TODO: "jwtId" is a typo, it should be "jwtid"
            //       But when we enable "jwtid", it will fail the verification
            //       because the payload does not specify "jti"
            //       When we comment out "jwtId", it also works (because it is a typo)

            // jwtId: botId
          });
        } catch (err) {
          res.status(401);
          res.end();

          return;
        }
      }
    }

    next();
  };
}