GoogleActions Account not linked yet 错误
GoogleActions Account not linked yet error
我正在尝试在我的 nodejs Google 使用(DialogFlow 或 API.ai 和 google 操作开发的助手应用程序上实施 oauth2 身份验证。
所以我遵循了这个。但我总是收到 "It looks like your test oauth account is not linked yet. " 错误。当我尝试打开调试选项卡上显示的 url 时,它显示 500 broken url 错误。
Dialogflow 实现
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'
}
]
}
]
}
}
}
};
实际上我部署的代码存在于对话流内联编辑器中。但是不知道如何实现 oauth 端点,它应该是一个单独的云功能还是必须包含在现有的功能中。而且我对 oauth 授权代码流的实际工作方式感到非常困惑。假设我们在 Assistant 应用程序上,一旦用户说 "talk to foo app",它会自动打开 Web 浏览器进行 oauth 代码交换过程吗?
answer you referenced 于 10 月 25 日发布了更新,表明他们已采取措施阻止您输入 google.com 端点作为帐户链接的授权提供商。他们似乎可能采取了其他措施来防止以这种方式使用 Google 的身份验证服务器。
如果您使用自己的授权服务器,错误 500 表示您的 oauth 服务器有错误,您应该检查您的 oauth 服务器是否有错误。
更新 来回答您的一些其他问题。
But don't know how to implement an oauth endpoint
Google 提供了有关使用 Implicit Flow or the Authorization Code Flow 和如何测试它的最小 OAuth 服务需要做什么的指导(但不是代码)。
whether it should be a separate cloud function or it has to be included within the existing one
它应该是分开的——甚至可以说它必须是分开的。在隐式流程和授权代码流程中,您需要提供一个 URL 端点,用户将被重定向到该端点以登录您的服务。对于授权代码流,您还需要一个额外的网络钩子,助手将使用它来交换令牌。
这些背后的功能需要与您为 Dialogflow webhook 所做的非常不同。虽然有人可能会创建一个函数来处理所有不同的任务——但没有必要这样做。您将单独提供 OAuth URL。
但是,您的 Dialogflow webhook 确实 与您的 OAuth 服务器有某种关系。特别是,OAuth 服务器交给助手的令牌将交还给 Dialogflow webhook,因此 Dialogflow 需要一些方法来根据该令牌获取用户信息。有很多方法可以做到这一点,但仅列出几种:
令牌可以是 JWT,并包含 body 中声明的用户信息。 Dialogflow webhook 应该使用 public 键来验证令牌是否有效并且需要知道声明的格式。
OAuth 服务器和 Dialogflow webhook 可以使用共享帐户数据库,OAuth 服务器将令牌存储为用户帐户的密钥并删除过期密钥。 Dialogflow webhook 然后可以使用它获得的令牌作为查找用户的密钥。
OAuth 服务器可能有一个(另一个)webhook,Dialogflow 可以在其中请求用户信息,将密钥作为授权 header 传递并获得回复。 (例如,Google 就是这样做的。)
具体的解决方案取决于您的需求以及您可以使用的资源。
And also I am so confused with how oauth authorization code flow will actually work.. Let's assume we are on the Assistant app, once the user say "talk to foo app", does it automatically opens a web browser for oauth code exchange process?
从广义上讲 - 是的。细节各不相同(并且可以更改),但不要太拘泥于细节。
如果您在扬声器上使用 Google 助理,系统会提示您打开 Home 应用,该应用应该会显示一张卡片,说明哪些操作需要获得许可。单击卡片将打开浏览器或 Web 视图以访问 Actions 网站以开始流程。
如果您在移动设备上使用智能助理,它会直接提示您,然后打开浏览器或 Web 视图访问 Actions 网站以开始流程。
认证流程主要涉及:
- 如有必要,让用户验证自己。
- 让用户授权智能助理代表用户访问您的资源。
- 然后使用 one-time 代码重定向到 Google 的服务器。
- Google 的服务器然后获取代码...并关闭 window。这是用户看到的范围。
在幕后,Google 获取此代码,并且由于您使用的是授权代码流,因此在令牌交换器 URL 将其交换为授权令牌和刷新令牌。
然后,每当用户使用您的 Action 时,它将连同请求的其余部分一起发送到您的服务器。
Plz suggest the necessary package for OAuth2 configuration
我做不到。对于初学者 - 这完全取决于您的其他资源和要求。 (这就是为什么 Whosebug 不喜欢人们征求这样的建议。)
有一些软件包(您可以搜索它们)可以让您设置 OAuth2 服务器。我敢肯定有人提供了 OAuth-as-a-service,尽管我不知道有什么随手可得的。最后,如上所述,您可以使用 Google.
中的指南编写最小的 OAuth2 服务器
正在尝试为 Google 的 OAuth 创建代理……可能……not as easy as it first seems... likely not as secure as anyone would be happy with... and possibly (but not necessarily, IANAL) a violation of Google's Terms of Service.
can't we store the user's email address by this approach?
好吧,您可以在用户帐户中存储任何您想要的内容。但这是您的 Action 的用户帐户。
例如,您可以代表您的用户访问 Google API,以获取他们的电子邮件地址或他们授权您使用 Google 执行的任何其他操作。您拥有的用户帐户可能会存储您用来访问 Google 的服务器的 OAuth 令牌。但是从逻辑上讲,您应该将其与助手用来访问您的服务器的代码分开考虑。
我实现的最小 oauth2 服务器(适用于隐式流但不存储用户会话)。
取自https://developers.google.com/identity/protocols/OAuth2UserAgent。
function oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
//Get the state and redirect_uri parameters from the request
var searchParams = new URLSearchParams(window.location.search);
var state = searchParams.get("state");
var redirect_uri = searchParams.get("redirect_uri");
//var client_id = searchParams.get("client_id");
// Parameters to pass to OAuth 2.0 endpoint.
var params = {
'client_id': YOUR_CLIENT_ID,
'redirect_uri': redirect_uri,
'scope': 'email',
'state': state,
'response_type': 'token',
'include_granted_scopes': 'true'
};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
此实现不是很安全,但它是我作为 Google 助理的 OAuth 服务器工作的唯一代码。
好久没搞定了。我们必须先启用 webhook,我们可以在对话流程实现文档中看到如何启用 webhook 如果我们要使用 Google 助手,那么我们必须在首先是集成。然后按照下面提到的步骤在 google:-
上进行帐户链接操作
转到 google 云控制台 -> API 和服务 -> 凭据 -> OAuth 2.0 客户端 ID -> Web 客户端 -> 从那里记下客户端 ID,客户端密码 -> 下载 JSON - 从 json 记下项目 ID,auth_uri,token_uri -> 授权重定向 URI -> 将我们应用程序的 URL 列入白名单 -> 在此 URL 中修复部分是 https://oauth-redirect.googleusercontent.com/r/ 并在 URL 中附加项目 ID -> 保存更改
在 Google 上的操作 -> 帐户 linking 设置 1. 授予类型 = 授权码 2. 客户端信息 1. 填写客户端 ID、客户端密码、auth_uri、token_uri 2. 输入 https://www.googleapis.com/auth and token_uri as https://www.googleapis.com/token 3. Save and run 4. It will show an error while running on the google assistant, but dont worry 5. Come back to the account linking section in the assistant settings and enter auth_uri as https://accounts.google.com/o/oauth2/auth and token_uri as https://accounts.google.com/o/oauth2/token 6. Put the scopes as https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email 作为 auth uri,我们就可以开始了。 7. 保存更改。
在托管服务器(heroku)日志中,我们可以看到访问令牌值,通过访问令牌,我们可以获得有关电子邮件地址的详细信息。
将访问令牌附加到此 link“https://www.googleapis.com/oauth2/v1/userinfo?access_token=”,我们可以在生成的 json 页面中获得所需的详细信息。
`accessToken = req.get("originalRequest").get("data").get("user").get("accessToken")
r = requests.get(link)
print("Email Id= " + r.json()["email"])
print("Name= " + r.json()["name"])`
我正在尝试在我的 nodejs Google 使用(DialogFlow 或 API.ai 和 google 操作开发的助手应用程序上实施 oauth2 身份验证。
所以我遵循了这个
Dialogflow 实现
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'
}
]
}
]
}
}
}
};
实际上我部署的代码存在于对话流内联编辑器中。但是不知道如何实现 oauth 端点,它应该是一个单独的云功能还是必须包含在现有的功能中。而且我对 oauth 授权代码流的实际工作方式感到非常困惑。假设我们在 Assistant 应用程序上,一旦用户说 "talk to foo app",它会自动打开 Web 浏览器进行 oauth 代码交换过程吗?
answer you referenced 于 10 月 25 日发布了更新,表明他们已采取措施阻止您输入 google.com 端点作为帐户链接的授权提供商。他们似乎可能采取了其他措施来防止以这种方式使用 Google 的身份验证服务器。
如果您使用自己的授权服务器,错误 500 表示您的 oauth 服务器有错误,您应该检查您的 oauth 服务器是否有错误。
更新 来回答您的一些其他问题。
But don't know how to implement an oauth endpoint
Google 提供了有关使用 Implicit Flow or the Authorization Code Flow 和如何测试它的最小 OAuth 服务需要做什么的指导(但不是代码)。
whether it should be a separate cloud function or it has to be included within the existing one
它应该是分开的——甚至可以说它必须是分开的。在隐式流程和授权代码流程中,您需要提供一个 URL 端点,用户将被重定向到该端点以登录您的服务。对于授权代码流,您还需要一个额外的网络钩子,助手将使用它来交换令牌。
这些背后的功能需要与您为 Dialogflow webhook 所做的非常不同。虽然有人可能会创建一个函数来处理所有不同的任务——但没有必要这样做。您将单独提供 OAuth URL。
但是,您的 Dialogflow webhook 确实 与您的 OAuth 服务器有某种关系。特别是,OAuth 服务器交给助手的令牌将交还给 Dialogflow webhook,因此 Dialogflow 需要一些方法来根据该令牌获取用户信息。有很多方法可以做到这一点,但仅列出几种:
令牌可以是 JWT,并包含 body 中声明的用户信息。 Dialogflow webhook 应该使用 public 键来验证令牌是否有效并且需要知道声明的格式。
OAuth 服务器和 Dialogflow webhook 可以使用共享帐户数据库,OAuth 服务器将令牌存储为用户帐户的密钥并删除过期密钥。 Dialogflow webhook 然后可以使用它获得的令牌作为查找用户的密钥。
OAuth 服务器可能有一个(另一个)webhook,Dialogflow 可以在其中请求用户信息,将密钥作为授权 header 传递并获得回复。 (例如,Google 就是这样做的。)
具体的解决方案取决于您的需求以及您可以使用的资源。
And also I am so confused with how oauth authorization code flow will actually work.. Let's assume we are on the Assistant app, once the user say "talk to foo app", does it automatically opens a web browser for oauth code exchange process?
从广义上讲 - 是的。细节各不相同(并且可以更改),但不要太拘泥于细节。
如果您在扬声器上使用 Google 助理,系统会提示您打开 Home 应用,该应用应该会显示一张卡片,说明哪些操作需要获得许可。单击卡片将打开浏览器或 Web 视图以访问 Actions 网站以开始流程。
如果您在移动设备上使用智能助理,它会直接提示您,然后打开浏览器或 Web 视图访问 Actions 网站以开始流程。
认证流程主要涉及:
- 如有必要,让用户验证自己。
- 让用户授权智能助理代表用户访问您的资源。
- 然后使用 one-time 代码重定向到 Google 的服务器。
- Google 的服务器然后获取代码...并关闭 window。这是用户看到的范围。
在幕后,Google 获取此代码,并且由于您使用的是授权代码流,因此在令牌交换器 URL 将其交换为授权令牌和刷新令牌。
然后,每当用户使用您的 Action 时,它将连同请求的其余部分一起发送到您的服务器。
Plz suggest the necessary package for OAuth2 configuration
我做不到。对于初学者 - 这完全取决于您的其他资源和要求。 (这就是为什么 Whosebug 不喜欢人们征求这样的建议。)
有一些软件包(您可以搜索它们)可以让您设置 OAuth2 服务器。我敢肯定有人提供了 OAuth-as-a-service,尽管我不知道有什么随手可得的。最后,如上所述,您可以使用 Google.
中的指南编写最小的 OAuth2 服务器正在尝试为 Google 的 OAuth 创建代理……可能……not as easy as it first seems... likely not as secure as anyone would be happy with... and possibly (but not necessarily, IANAL) a violation of Google's Terms of Service.
can't we store the user's email address by this approach?
好吧,您可以在用户帐户中存储任何您想要的内容。但这是您的 Action 的用户帐户。
例如,您可以代表您的用户访问 Google API,以获取他们的电子邮件地址或他们授权您使用 Google 执行的任何其他操作。您拥有的用户帐户可能会存储您用来访问 Google 的服务器的 OAuth 令牌。但是从逻辑上讲,您应该将其与助手用来访问您的服务器的代码分开考虑。
我实现的最小 oauth2 服务器(适用于隐式流但不存储用户会话)。
取自https://developers.google.com/identity/protocols/OAuth2UserAgent。
function oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
//Get the state and redirect_uri parameters from the request
var searchParams = new URLSearchParams(window.location.search);
var state = searchParams.get("state");
var redirect_uri = searchParams.get("redirect_uri");
//var client_id = searchParams.get("client_id");
// Parameters to pass to OAuth 2.0 endpoint.
var params = {
'client_id': YOUR_CLIENT_ID,
'redirect_uri': redirect_uri,
'scope': 'email',
'state': state,
'response_type': 'token',
'include_granted_scopes': 'true'
};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
此实现不是很安全,但它是我作为 Google 助理的 OAuth 服务器工作的唯一代码。
好久没搞定了。我们必须先启用 webhook,我们可以在对话流程实现文档中看到如何启用 webhook 如果我们要使用 Google 助手,那么我们必须在首先是集成。然后按照下面提到的步骤在 google:-
上进行帐户链接操作转到 google 云控制台 -> API 和服务 -> 凭据 -> OAuth 2.0 客户端 ID -> Web 客户端 -> 从那里记下客户端 ID,客户端密码 -> 下载 JSON - 从 json 记下项目 ID,auth_uri,token_uri -> 授权重定向 URI -> 将我们应用程序的 URL 列入白名单 -> 在此 URL 中修复部分是 https://oauth-redirect.googleusercontent.com/r/ 并在 URL 中附加项目 ID -> 保存更改
在 Google 上的操作 -> 帐户 linking 设置 1. 授予类型 = 授权码 2. 客户端信息 1. 填写客户端 ID、客户端密码、auth_uri、token_uri 2. 输入 https://www.googleapis.com/auth and token_uri as https://www.googleapis.com/token 3. Save and run 4. It will show an error while running on the google assistant, but dont worry 5. Come back to the account linking section in the assistant settings and enter auth_uri as https://accounts.google.com/o/oauth2/auth and token_uri as https://accounts.google.com/o/oauth2/token 6. Put the scopes as https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email 作为 auth uri,我们就可以开始了。 7. 保存更改。
在托管服务器(heroku)日志中,我们可以看到访问令牌值,通过访问令牌,我们可以获得有关电子邮件地址的详细信息。
将访问令牌附加到此 link“https://www.googleapis.com/oauth2/v1/userinfo?access_token=”,我们可以在生成的 json 页面中获得所需的详细信息。
`accessToken = req.get("originalRequest").get("data").get("user").get("accessToken")
r = requests.get(link)
print("Email Id= " + r.json()["email"])
print("Name= " + r.json()["name"])`