GoogleActions帐户尚未关联错误

Avi*_*Raj 3 node.js google-oauth google-oauth2 actions-on-google dialogflow-es

我正在尝试使用(DialogFlow或API.ai和google操作)开发的我的nodejs Google智能助理应用程序实施oauth2身份验证.

所以我按照这个答案.但我总是得到"看起来你的测试oauth帐户尚未链接."错误.当我尝试打开调试选项卡上显示的URL时,它显示500个损坏的URL错误.

Dialogflow fullfillment

index.js

'use strict';

const functions = require('firebase-functions'); // Cloud Functions for Firebase library
const DialogflowApp = require('actions-on-google').DialogflowApp; // Google Assistant helper library

const googleAssistantRequest = 'google'; // Constant to identify Google Assistant requests

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));

  // An action is a string used to identify what needs to be done in fulfillment
  let action = request.body.result.action; // https://dialogflow.com/docs/actions-and-parameters

  // Parameters are any entites that Dialogflow has extracted from the request.
  const parameters = request.body.result.parameters; // https://dialogflow.com/docs/actions-and-parameters

  // Contexts are objects used to track and store conversation state
  const inputContexts = request.body.result.contexts; // https://dialogflow.com/docs/contexts

  // Get the request source (Google Assistant, Slack, API, etc) and initialize DialogflowApp
  const requestSource = (request.body.originalRequest) ? request.body.originalRequest.source : undefined;
  const app = new DialogflowApp({request: request, response: response});

  // Create handlers for Dialogflow actions as well as a 'default' handler
  const actionHandlers = {
    // The default welcome intent has been matched, welcome the user (https://dialogflow.com/docs/events#default_welcome_intent)
    'input.welcome': () => {
      // Use the Actions on Google lib to respond to Google requests; for other requests use JSON
      //+app.getUser().authToken
      if (requestSource === googleAssistantRequest) {
        sendGoogleResponse('Hello, Welcome to my Dialogflow agent!'); // Send simple response to user
      } else {
        sendResponse('Hello, Welcome to my Dialogflow agent!'); // Send simple response to user
      }
    },
    // The default fallback intent has been matched, try to recover (https://dialogflow.com/docs/intents#fallback_intents)
    'input.unknown': () => {
      // Use the Actions on Google lib to respond to Google requests; for other requests use JSON
      if (requestSource === googleAssistantRequest) {
        sendGoogleResponse('I\'m having trouble, can you try that again?'); // Send simple response to user
      } else {
        sendResponse('I\'m having trouble, can you try that again?'); // Send simple response to user
      }
    },
    // Default handler for unknown or undefined actions
    'default': () => {
      // Use the Actions on Google lib to respond to Google requests; for other requests use JSON
      if (requestSource === googleAssistantRequest) {
        let responseToUser = {
          //googleRichResponse: googleRichResponse, // Optional, uncomment to enable
          //googleOutputContexts: ['weather', 2, { ['city']: 'rome' }], // Optional, uncomment to enable
          speech: 'This message is from Dialogflow\'s Cloud Functions for Firebase editor!', // spoken response
          displayText: 'This is from Dialogflow\'s Cloud Functions for Firebase editor! :-)' // displayed response
        };
        sendGoogleResponse(responseToUser);
      } else {
        let responseToUser = {
          //richResponses: richResponses, // Optional, uncomment to enable
          //outputContexts: [{'name': 'weather', 'lifespan': 2, 'parameters': {'city': 'Rome'}}], // Optional, uncomment to enable
          speech: 'This message is from Dialogflow\'s Cloud Functions for Firebase editor!', // spoken response
          displayText: 'This is from Dialogflow\'s Cloud Functions for Firebase editor! :-)' // displayed response
        };
        sendResponse(responseToUser);
      }
    }
  };

  // If undefined or unknown action use the default handler
  if (!actionHandlers[action]) {
    action = 'default';
  }

  // Run the proper handler function to handle the request from Dialogflow
  actionHandlers[action]();

  // Function to send correctly formatted Google Assistant responses to Dialogflow which are then sent to the user
  function sendGoogleResponse (responseToUser) {
    if (typeof responseToUser === 'string') {
      app.ask(responseToUser); // Google Assistant response
    } else {
      // If speech or displayText is defined use it to respond
      let googleResponse = app.buildRichResponse().addSimpleResponse({
        speech: responseToUser.speech || responseToUser.displayText,
        displayText: responseToUser.displayText || responseToUser.speech
      });

      // Optional: Overwrite previous response with rich response
      if (responseToUser.googleRichResponse) {
        googleResponse = responseToUser.googleRichResponse;
      }

      // Optional: add contexts (https://dialogflow.com/docs/contexts)
      if (responseToUser.googleOutputContexts) {
        app.setContext(...responseToUser.googleOutputContexts);
      }

      app.ask(googleResponse); // Send response to Dialogflow and Google Assistant
    }
  }

  // Function to send correctly formatted responses to Dialogflow which are then sent to the user
  function sendResponse (responseToUser) {
    // if the response is a string send it as a response to the user
    if (typeof responseToUser === 'string') {
      let responseJson = {};
      responseJson.speech = responseToUser; // spoken response
      responseJson.displayText = responseToUser; // displayed response
      response.json(responseJson); // Send response to Dialogflow
    } else {
      // If the response to the user includes rich responses or contexts send them to Dialogflow
      let responseJson = {};

      // If speech or displayText is defined, use it to respond (if one isn't defined use the other's value)
      responseJson.speech = responseToUser.speech || responseToUser.displayText;
      responseJson.displayText = responseToUser.displayText || responseToUser.speech;

      // Optional: add rich messages for integrations (https://dialogflow.com/docs/rich-messages)
      responseJson.data = responseToUser.richResponses;

      // Optional: add contexts (https://dialogflow.com/docs/contexts)
      responseJson.contextOut = responseToUser.outputContexts;

      response.json(responseJson); // Send response to Dialogflow
    }
  }
});

// Construct rich response for Google Assistant
const app = new DialogflowApp();
const googleRichResponse = app.buildRichResponse()
  .addSimpleResponse('This is the first simple response for Google Assistant')
  .addSuggestions(
    ['Suggestion Chip', 'Another Suggestion Chip'])
    // Create a basic card and add it to the rich response
  .addBasicCard(app.buildBasicCard(`This is a basic card.  Text in a
 basic card can include "quotes" and most other unicode characters
 including emoji .  Basic cards also support some markdown
 formatting like *emphasis* or _italics_, **strong** or __bold__,
 and ***bold itallic*** or ___strong emphasis___ as well as other things
 like line  \nbreaks`) // Note the two spaces before '\n' required for a
                        // line break to be rendered in the card
    .setSubtitle('This is a subtitle')
    .setTitle('Title: this is a title')
    .addButton('This is a button', 'https://assistant.google.com/')
    .setImage('https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
      'Image alternate text'))
  .addSimpleResponse({ speech: 'This is another simple response',
    displayText: 'This is the another simple response ' });

// Rich responses for both Slack and Facebook
const richResponses = {
  'slack': {
    'text': 'This is a text response for Slack.',
    'attachments': [
      {
        'title': 'Title: this is a title',
        'title_link': 'https://assistant.google.com/',
        'text': 'This is an attachment.  Text in attachments can include \'quotes\' and most other unicode characters including emoji .  Attachments also upport line\nbreaks.',
        'image_url': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
        'fallback': 'This is a fallback.'
      }
    ]
  },
  'facebook': {
    'attachment': {
      'type': 'template',
      'payload': {
        'template_type': 'generic',
        'elements': [
          {
            'title': 'Title: this is a title',
            'image_url': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
            'subtitle': 'This is a subtitle',
            'default_action': {
              'type': 'web_url',
              'url': 'https://assistant.google.com/'
            },
            'buttons': [
              {
                'type': 'web_url',
                'url': 'https://assistant.google.com/',
                'title': 'This is a button'
              }
            ]
          }
        ]
      }
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

实际上我部署的代码存在于对话框流内联编辑器中.但是不知道如何实现oauth端点,它是应该是一个单独的云功能还是必须包含在存在的功能中.而且我对oauth授权代码流实际如何工作非常困惑.让我们假设我们在Assistant应用程序上,一旦用户说"与foo app交谈",它是否会自动打开web浏览器进行oauth代码交换过程?

Pri*_*ner 5

您引用答案在10月25日发布了更新,表明他们已采取措施阻止您作为帐户关联的身份验证提供程序进入google.com端点.他们似乎可能已采取其他措施来阻止以这种方式使用Google的auth服务器.

如果您使用自己的auth服务器,则错误500将指示您的oauth服务器上的错误,您应该检查您的oauth服务器是否有错误.

更新以回答您的其他一些问题.

但是不知道如何实现oauth端点

Google提供有关您需要为最小OAuth服务执行的操作的指导(但不是代码),使用隐式流授权代码流,以及如何测试它.

它是否应该是一个单独的云功能,或者它必须包含在现有的云功能中

应该是分开的 - 它甚至可以说它必须是分开的.在隐式流和授权代码流中,您需要提供一个URL端点,用户将被重定向以登录您的服务.对于授权代码流程,您还需要一个辅助用于交换令牌的附加webhook.

这些功能背后的功能需要与您为Dialogflow webhook所做的非常不同.虽然有人可能会创建一个处理所有不同任务的功能 - 但是没有必要.您将单独提供OAuth网址.

但是,您的Dialogflow webhook 确实与您的OAuth服务器有一些关系.特别是,OAuth服务器交给智能助理的令牌将被交还给Dialogflow webhook,因此Dialogflow需要某种方式来获取基于该令牌的用户信息.有很多方法可以做到这一点,但只列出几个:

  • 令牌可以是JWT,并包含用户信息作为正文中的声明.Dialogflow webhook应该使用公钥来验证令牌是否有效,并且需要知道声明的格式.

  • OAuth服务器和Dialogflow webhook可以使用共享帐户数据库,OAuth服务器将令牌存储为用户帐户的密钥并删除过期的密钥.然后,Dialogflow webhook可以使用它获取的令牌作为查找用户的密钥.

  • OAuth服务器可能有一个(另一个)webhook,Dialogflow可以在其中请求用户信息,将密钥作为Authorization标头传递并获得回复.(例如,Google就是这样做的.)

确切的解决方案取决于您的需求以及您可以使用的资源.

而且我对oauth授权代码流实际如何工作非常困惑.让我们假设我们在Assistant应用程序上,一旦用户说"与foo app交谈",它是否会自动打开web浏览器进行oauth代码交换过程?

从广义上讲 - 是的.细节有所不同(并且可以改变),但不要过于关注细节.

如果您在扬声器上使用智能助理,系统将提示您打开主应用程序,该应用程序应显示一张卡片,说明行动需要什么权限.单击该卡将打开浏览器或webview到Actions网站以开始流程.

如果您在移动设备上使用智能助理,它会直接提示您,然后打开浏览器或网页浏览到操作网站以开始流程.

认证流程主要涉及:

  • 如有必要,让用户进行身份验证.
  • 让用户授权助理代表用户访问您的资源.
  • 然后,它会使用一次性代码重定向到Google的服务器.
  • 谷歌的服务器然后采取代码...并关闭窗口.这是用户看到的程度.

在幕后,Google会使用此代码,并且由于您使用的是授权代码流,因此需要在令牌交换URL处将其替换为身份验证令牌和刷新令牌.

然后,只要用户使用您的Action,它就会将一个身份验证令牌以及其余请求发送到您的服务器.

Plz建议OAuth2配置的必要包

我不能这样做.对于初学者 - 它完全取决于您的其他资源和要求.(这就是为什么StackOverflow不喜欢人们要求这样的建议.)

有些软件包(您可以搜索它们),可以让您设置OAuth2服务器.我相信那里有人提供OAuth-as-a-service,虽然我不知道任何随便的.最后,如上所述,您可以使用Google的指南编写最小的OAuth2服务器.

试图为Google的OAuth创建一个代理......可能......可能不像最初看起来那么容易 ......可能不像任何人一样安心......并且可能(但不一定是IANAL)a违反了Google的服务条款.

我们不能通过这种方法存储用户的电子邮件地址吗?

那么,您可以在用户的​​帐户中存储您想要的任何内容.但这是用户对您的操作的帐户.

例如,您可以代表您的用户访问Google API,以获取他们的电子邮件地址或他们授权您与Google合作的其他任何内容.您拥有的用户帐户可能会存储您用于访问Google服务器的OAuth令牌.但是,您应该逻辑地将其视为与Assistant用于访问服务器的代码分开.