首页 > 解决方案 > 无法从 Dialogflow 意图处理程序进行 APEX Web 服务标注

问题描述

我有一个 Express 应用程序(托管在 Heroku 上),我用它来处理来自 Dialogflow 的意图,并对 APEX REST Webservice 类进行标注(从 Salesforce 获取数据),然后在 Google Assistant 上显示结果。

对于身份验证,我正在尝试实施 OAuth,因此我在 Salesforce 上创建了 Connected App。在帐户链接下的 Google 操作中,我提到了“授权 URL”作为快速应用程序 URL(类似于https://testBot.herokuapp.com/authorization)和“您的操作向 Google 发出的客户端 ID”作为 Salesforce 的消费者密钥连接的应用程序和最后的“客户端密码”作为 Salesforce 连接的应用程序消费者密码。此外,我的令牌 URL 类似于https://testBot.herokuapp.com/token

在 Express 上,我创建了路由,首先处理进入授权的请求(获取授权码),然后处理回调路由(这是 Salesforce Connected App 上的回调 URL),如实施 OAuth 帐户链接i'已重定向到 redirect_uri(格式为https://oauth-redirect.googleusercontent.com/r/MY_PROJECT_ID),其中授权代码和状态作为参数。这是 uri 的样子https://oauth-redirect.googleusercontent.com/r/MY_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING。现在在第三条路线(https://testBot.herokuapp.com/token),编写逻辑以交换访问令牌和刷新令牌的授权代码。请注意,令牌交换端点响应 POST 请求。

现在根据官方文档,Google 会为用户存储访问令牌和刷新令牌。因此,这意味着 Conversation 或 conv 对象应该保存访问令牌值,但是当我尝试访问相同然后调用 APEX Web 服务时,我可以看到 conv.user.accessToken 给出未定义,因此标注是即使在成功验证后也不成功(错误:INVALID_SESSION_ID: Session expired or invalid )。

我的问题是为什么我没有从CONV获得访问令牌,如果这是预期的(我是否错误地阅读了文档)我应该如何获得访问令牌?

这是快递代码:

const express = require('express');
const bodyParser = require('body-parser');
const jsforce = require('jsforce');
const { dialogflow } = require('actions-on-google');
const {
  SimpleResponse,
  BasicCard,
  SignIn,
  Image,
  Suggestions,
  Button
} = require('actions-on-google');

var options;
var timeOut = 3600;

var port = process.env.PORT || 3000;
var conn = {};

const expApp = express().use(bodyParser.json());
expApp.use(bodyParser.urlencoded());
//app instance
const app = dialogflow({
  debug: true
});

const oauth2 = new jsforce.OAuth2({
    clientId: process.env.SALESFORCE_CONSUMER_KEY,
    clientSecret: process.env.SALESFORCE_CONSUMER_SECRET,
    redirectUri: 'https://testbot.herokuapp.com/callback'
});

expApp.get('/authorize', function(req, res) {
    var queryParams = req.query;
    console.log('this is the first request: '+req);
    res.redirect(oauth2.getAuthorizationUrl({ state: queryParams.state }));

});

expApp.get('/callback', function(req,res) {
    var queryParams = req.query;
    console.log('Request came for access callback');
    console.log('Query params in callback uri is ', req.query);
    let redirectUri = `${process.env.GOOGLE_REDIRECT_URI}?code=${queryParams.code}&state=${queryParams.state}`;
    console.log('Google redirecturi is ', redirectUri);
    res.redirect(redirectUri);
});


expApp.post('/token', function(req, res) {
    console.log('Request came for accesstoken');

    console.log('query params are-->', req.body);
    console.log('req query-->', req.query);

    res.setHeader('Content-Type', 'application/json');
    if (req.body.client_id != process.env.SALESFORCE_CONSUMER_KEY) {
        console.log('Invalid Client ID');
        return res.status(400).send('Invalid Client ID');
    }
    if (req.body.client_secret != process.env.SALESFORCE_CONSUMER_SECRET) {
        console.log('Invalid Client Ksecret');
        return res.status(400).send('Invalid Client ID');
    }
    if (req.body.grant_type) {
        if (req.body.grant_type == 'authorization_code') {
            console.log('Fetching token from salesforce');
            oauth2.requestToken(req.body.code, (err, tokenResponse) => {
                if (err) {
                    console.log(err.message);
                    return res.status(400).json({ "error": "invalid_grant" });
                }
                console.log('Token respons: ',tokenResponse);

                var googleToken = {
                    token_type: tokenResponse.token_type,
                    access_token: tokenResponse.access_token,
                    refresh_token: tokenResponse.refresh_token,
                    expires_in: timeOut
                };
                console.log('Token response for auth code', googleToken);

                res.status(200).json(googleToken);

            });
        } 
        else if (req.body.grant_type == 'refresh_token') {
            console.log('Fetching refresh token from salesforce');
            oauth2.refreshToken(req.body.refresh_token, (err, tokenResponse) => {
                if (err) {
                    console.log(err.message);
                    return res.status(400).json({ "error": "invalid_grant" });
                }
                console.log('Token response in refresh token: ',tokenResponse);
                var googleToken = { token_type: tokenResponse.token_type, access_token: tokenResponse.access_token, expires_in: timeOut };

                console.log('Token response for auth code', googleToken);

                res.status(200).json(googleToken);
            });
        }
    } else {
        res.send('Invalid parameter');
    }
});

var createTask = function(oppName,taskSubject,taskPriority,conFName,conn){
    return new Promise((resolve,reject)=>{
        conn.apex.get("/createTask?oppName="+oppName+"&taskSubject="+taskSubject+"&taskPriority="+taskPriority+"&contactFirstName="+conFName,function(err, res){
            if (err) {
                console.log('error is --> ',err);
                reject(err);
            }
            else{
                console.log('res is --> ',res);
                resolve(res);
            }
        });
    });
};

app.intent('Default Welcome Intent', (conv) => {

    console.log('Request came for account link flow start');    
        if(!conv.user.accessToken){
            conv.ask(new SignIn());
        }
        else{
            conv.ask('You are already signed in ');
        }

});

app.intent('Get SignIn Info', (conv, params, signin) => {    
    console.log('Sign in info Intent');    
    console.log('Sign in content-->',signin);       
    if (signin.status === 'OK') {         
        conv.ask('Hola, thanks for signing in! What do you want to do next?')       ;
    } 
    else {         
        conv.ask('Something went wrong in the sign in process');       
    }     
}); 

app.intent('Create Task on Opportunity', (conv, {oppName,taskSubject,taskPriority,contactFirstName} ) => {
    console.log('conv: ',conv);
    //this logs undefined
    console.log('Access token from conv inside intent: ',conv.user.accessToken);
    const opName = conv.parameters['oppName'];
    const tskSbj = conv.parameters['taskSubject'];
    const tskPr = conv.parameters['taskPriority'];
    const conFName = conv.parameters['contactFirstName'];
    console.log('Instance URL as stored in heroku process variable: ',process.env.INSTANCE_URL);
    conn = new jsforce.Connection({
      instanceUrl : process.env.INSTANCE_URL,
      accessToken : conv.user.accessToken
    });
    return createTask(opName,tskSbj,tskPr,conFName,conn).then((resp) => {
        conv.ask(new SimpleResponse({
            speech:resp,
            text:resp,
        }));
    });
});

expApp.get('/', function (req, res) {
    res.send('Hello World!');
});
expApp.listen(port, function () {
    expApp.post('/fulfillment', app);
    console.log('Example app listening on port !');
});

标签: authenticationoauthoauth-2.0salesforcedialogflow-es

解决方案


所以,在记录conv.user时,我知道conv.user.access.token是正确的,而不是conv.user.accessToken。因此,现在连接实例看起来像:

conn = new jsforce.Connection({
  instanceUrl : process.env.INSTANCE_URL,
  accessToken : conv.user.acces.token
});

现在,在 apex Web 服务上获取请求确实会发送预期的响应!


推荐阅读