У меня есть приложение Express (размещенное на Heroku), которое я использую для обработки намерений из Dialogflow и выполнения вызовов в классы веб-сервиса APEX REST (для получения данных из Salesforce), а затем отображаю результаты в Google Assistant.
Для аутентификации я пытаюсь реализовать OAuth, и поэтому я создал Connected App на Salesforce. В Google Actions в разделе «Связывание аккаунтов» я упомянул «URL-адрес авторизации» как Express URL-адрес приложения (что-то вроде https://testBot.herokuapp.com/authorization) и «Идентификатор клиента, выданный вашими действиями в Google» в качестве ключа потребителя от Salesforce. Подключенное приложение и, наконец, «Секрет клиента» в качестве секрета пользователя Salesforce Connected App. Кроме того, мой токен URL похож на https://testBot.herokuapp.com/token.
На Express Я создал маршруты, сначала обработав запрос на авторизацию (чтобы получить код авторизации), а затем во-вторых, на маршруте обратного вызова (это URL-адрес обратного вызова в приложении Salesforce Connected), как упоминалось в Реализация привязки учетной записи OAuth Я перенаправлен на 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. Теперь на 3-м маршруте (https://testBot.herokuapp.com/token) лог c записывается для обмена кодом авторизации на токен доступа и токен refre sh. Обратите внимание, что конечная точка обмена токенами отвечает на запросы POST.
Теперь, согласно официальной документации, Google хранит токен доступа и токен refre sh для пользователя. Итак, это означает, что объект Conversation или conv должен содержать значения токена доступа, однако, когда я пытаюсь получить доступ к нему и затем вызывать его в веб-сервис APEX, я мог видеть, что conv.user.accessToken дает неопределенное значение, и, следовательно, вызов также не удалось ( ошибка: INVALID_SESSION_ID: сеанс истек или недействителен ) даже после успешной аутентификации.
У меня вопрос, почему я не получаю токен доступа от CONV и если это ожидается (я неправильно читаю документацию), как я должен получить токен доступа?
Вот код express:
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 !');
});