private verifyBotFramework()

in Node/core/src/bots/ChatConnector.ts [206:359]


    private verifyBotFramework(req: IWebRequest, res: IWebResponse, next: Function): void {
        var token: string;
        var isEmulator = req.body['channelId'] === 'emulator';
        var authHeaderValue = req.headers ? req.headers['authorization'] || req.headers['Authorization'] : null;
        if (authHeaderValue) {
            var auth = authHeaderValue.trim().split(' ');
            if (auth.length == 2 && auth[0].toLowerCase() == 'bearer') {
                token = auth[1];
            }
        }

        // Verify token
        if (token) {
            let decoded = jwt.decode(token, { complete: true }) as any;
            var verifyOptions: jwt.VerifyOptions;
            var openIdMetadata: OpenIdMetadata;
            const algorithms: string[] = ['RS256', 'RS384', 'RS512'];
            
            if(this.settings.enableSkills === true && SkillValidation.isSkillToken(authHeaderValue)) {
                const skillMsg = cloneDeep(req.body);
                this.prepIncomingMessage(skillMsg);

                // If the user has not provided an authConfiguration, use the default.
                // If the user has not provided allowedCallers then this will throw
                const authConfiguration = this.settings.authConfiguration || new DefaultAuthenticationConfiguration(this.settings.allowedCallers);

                JwtTokenValidation.authenticateRequest(
                    skillMsg, 
                    authHeaderValue,
                    new SimpleCredentialProvider(this.settings.appId, this.settings.appPassword),
                    req.body.serviceUrl,
                    authConfiguration
                ).then((claimsIdentity: IClaimIdentity) =>{
                    if (!claimsIdentity || !claimsIdentity.isAuthenticated) {
                        logger.error('ChatConnector: receive - invalid skill token.');
                        res.send(403);
                        res.end();
                        next();
                        return; 
                    }
                    const oauthScope = JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims);
                    const creds = new MicrosoftAppCredentials(this.settings.appId, this.settings.appPassword, oauthScope);
                    // cache creds in memory
                    this.credentialsCache[req.body.serviceUrl] = creds
                    this.dispatch(req.body, res, next);
                }).catch((err: any) => {
                    logger.error(`Unable to authenticate request: ${err}`)
                    res.send(401);
                    res.end();
                    next();
                    return; 
                });
            }
            else {
                if (isEmulator) {

                    // validate the claims from the emulator
                    if ((decoded.payload.ver === '2.0' && decoded.payload.azp !== this.settings.appId) ||
                        (decoded.payload.ver !== '2.0' && decoded.payload.appid !== this.settings.appId)) {
                        logger.error('ChatConnector: receive - invalid token. Requested by unexpected app ID.');
                        res.status(403);
                        res.end();
                        next();
                        return;
                    }

                    // the token came from the emulator, so ensure the correct issuer is used
                    let issuer: string;
                    if (decoded.payload.ver === '1.0' && decoded.payload.iss == this.settings.endpoint.emulatorAuthV31IssuerV1) {
                        // This token came from the emulator as a v1 token using the Auth v3.1 issuer
                        issuer = this.settings.endpoint.emulatorAuthV31IssuerV1;
                    } else if (decoded.payload.ver === '2.0' && decoded.payload.iss == this.settings.endpoint.emulatorAuthV31IssuerV2) {
                        // This token came from the emulator as a v2 token using the Auth v3.1 issuer
                        issuer = this.settings.endpoint.emulatorAuthV31IssuerV2;
                    } else if (decoded.payload.ver === '1.0' && decoded.payload.iss == this.settings.endpoint.emulatorAuthV32IssuerV1) {
                        // This token came from the emulator as a v1 token using the Auth v3.2 issuer
                        issuer = this.settings.endpoint.emulatorAuthV32IssuerV1;
                    } else if (decoded.payload.ver === '2.0' && decoded.payload.iss == this.settings.endpoint.emulatorAuthV32IssuerV2) {
                        // This token came from the emulator as a v2 token using the Auth v3.2 issuer
                        issuer = this.settings.endpoint.emulatorAuthV32IssuerV2;
                    }

                    if (issuer) {
                        openIdMetadata = this.emulatorOpenIdMetadata;
                        verifyOptions = {
                            algorithms: algorithms,
                            issuer: issuer,
                            audience: this.settings.endpoint.emulatorAudience,
                            clockTolerance: 300
                        };
                    }
                }

                if (!verifyOptions) {
                    // This is a normal token, so use our Bot Connector verification
                    openIdMetadata = this.botConnectorOpenIdMetadata;
                    verifyOptions = {
                        issuer: this.settings.endpoint.botConnectorIssuer,
                        audience: this.settings.endpoint.botConnectorAudience,
                        clockTolerance: 300
                    };
                }

                openIdMetadata.getKey(decoded.header.kid, key => {
                    if (key) {
                        try {
                            jwt.verify(token, key.key, verifyOptions);

                            // enforce endorsements in openIdMetadadata if there is any endorsements associated with the key
                            if (typeof req.body.channelId !== 'undefined' &&
                                typeof key.endorsements !== 'undefined' &&
                                key.endorsements.lastIndexOf(req.body.channelId) === -1) {
                                const errorDescription: string = `channelId in req.body: ${req.body.channelId} didn't match the endorsements: ${key.endorsements.join(',')}.`;
                                logger.error(`ChatConnector: receive - endorsements validation failure. ${errorDescription}`);
                                throw new Error(errorDescription);
                            }

                            // validate service url using token's serviceurl payload
                            if (typeof decoded.payload.serviceurl !== 'undefined' &&
                                typeof req.body.serviceUrl !== 'undefined' &&
                                decoded.payload.serviceurl !== req.body.serviceUrl) {
                                const errorDescription: string = `ServiceUrl in payload of token: ${decoded.payload.serviceurl} didn't match the request's serviceurl: ${req.body.serviceUrl}.`;
                                logger.error(`ChatConnector: receive - serviceurl mismatch. ${errorDescription}`);
                                throw new Error(errorDescription);
                            }
                        } catch (err) {
                            logger.error('ChatConnector: receive - invalid token. Check bot\'s app ID & Password.');
                            res.send(403, err);
                            res.end();
                            next();
                            return;
                        }

                        this.dispatch(req.body, res, next);
                    } else {
                        logger.error('ChatConnector: receive - invalid signing key or OpenId metadata document.');
                        res.status(500);
                        res.end();
                        next();
                        return;
                    }
                });
            }
        } else if (isEmulator && !this.settings.appId && !this.settings.appPassword) {
            // Emulator running without auth enabled
            logger.warn(req.body, 'ChatConnector: receive - emulator running without security enabled.');
            this.dispatch(req.body, res, next);
        } else {
            // Token not provided so
            logger.error('ChatConnector: receive - no security token sent.');
            res.status(401);
            res.end();
            next();
        }