DialogFlow/Actions:允许 Google 助理用户从 Actions 应用程序在 Google 日历中创建事件 [英] DialogFlow/Actions: Allow Google Assistant user to create Event in Google Calendar from Actions App

查看:31
本文介绍了DialogFlow/Actions:允许 Google 助理用户从 Actions 应用程序在 Google 日历中创建事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标/摘要:我有一个在 Google DialogFlow 中开发的 Actions 应用程序,我希望用户能够使用该应用程序(来自 Google Assistant)创建 Google 日历事件.换句话说,对用户进行身份验证以允许 my 应用使用 他的 日历来创建事件.

做了什么:

  1. 由于 Google Actions 不允许使用 Google 身份验证/令牌端点,因此我选择使用

    1. auth0.com 上创建了一个帐户(使用了我的 Google 帐户),创建了一个应用程序并使用其管理面板(Domain、CliendId 设置了以下值>ClientSecret 由 auth0 生成):

    1. 在 Google Cloud Console 凭据页面上创建了 OAuth 客户端 ID:

    1. 像这样配置操作帐户链接:

    1. 返回 auth0.com > 连接 > 社交 > 启用 Google:

    1. 在 DialogFlow > 集成 > Google 助理中选中需要登录":

    1. 在我的 DialogFlow Webhook 方法的第一行写日志以记录以下响应:

      <代码> {原始请求":{来源":谷歌","版本":"2",数据":{isInSandbox":真,表面":{能力":[{"name":"actions.capability.AUDIO_OUTPUT"},{"name":"actions.capability.WEB_BROWSER"},{"name":"actions.capability.MEDIA_RESPONSE_AUDIO"},{名称":actions.capability.SCREEN_OUTPUT"}]},输入":[{原始输入":[{"查询":"测试",输入类型":键盘"}],参数":[{"rawText":"测试","textValue":"测试",名称":文本"}],意图":actions.intent.TEXT"}],用户":{"lastSeen":"2018-05-03T11:40:57Z","accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx","locale":"en-US",用户 ID":15229245xxxxx"},对话":{"conversationId":"15253476xxxxx","type":"ACTIVE","conversationToken":"["身份验证","wh_patient-details"]"},可用表面":[{能力":[{"name":"actions.capability.AUDIO_OUTPUT"},{名称":actions.capability.SCREEN_OUTPUT"}]}]}},"id":"1d6ed865-0803-49ca-bbac-xxxx","时间戳":"2018-05-03T11:42:22.835Z","lang":"en-us",结果":{"来源":"代理","resolvedQuery":"test",演讲":"","action":"v00.xxxxx","actionIncomplete":false,参数":{"CallEnum":"测试"},上下文":[{"name":"身份验证",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":1},{"name":"actions_capability_screen_output",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":0},{"name":"actions_capability_audio_output",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":0},{"name":"wh_patient-details",参数":{"患者 ID":0,"CallEnum":"测试","fallbackLifespan":0,"providerId":0,"练习编号":0,"CallEnum.original":"","fullDob":"0001 年 1 月 1 日"},寿命":199},{"name":"google_assistant_input_type_keyboard",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":0},{"name":"actions_capability_web_browser",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":0},{"name":"actions_capability_media_response_audio",参数":{"CallEnum":"测试","CallEnum.original":""},寿命":0}],元数据":{"intentName":"v00xxxx","isResponseToSlotfilling":false,"intentId":"c7bd9113-d5b4-4312-8851-xxxxxxx","webhookUsed":"true","webhookForSlotFillingUsed":"false",nluResponseTime":556},履行":{"演讲":"测试",消息":[{类型":0,"演讲":"测试"}]},分数":0.8399999737739563},地位":{代码":200,错误类型":成功"},"sessionId":"152534xxxxxxx",isStackdriverLoggingEnabled":假

      }

    相关部分在哪里:

    用户":{"lastSeen":"2018-05-03T11:40:57Z","accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx","locale":"en-US",用户 ID":15229245xxxxx"}

    1. 来自这篇文章:

      1. 返回

        1. 转到 Google 操作控制台 > 帐户关联(参见第 4 点)并检查您是否设置了以下范围:

        1. 通过以下方式更改您的代码:
          一种.使用来自 https://[domain].auth0.com/userinfo 的授权令牌调用 originalRequest.data.user.accessToken(见第 7 和第 8 点).成功的响应应该给你 userId
          湾在
          https://[domain].auth0.com/oauth/token 上发帖包含您的 client_id、client_secret、受众、grant_type 的请求正文.成功的回复应该会给你一个新的access_token.
          C.将授权令牌更改为新获取的 access_token 从点 11.b 并调用 https://[domain].auth0.com/api/v2/users/[userId] 其中 userId 是您从第 11.a 点获得的那个.成功的响应应该会为您提供Google"access_token 和 userId(在身份下).
          d.使用点 11.c 中的授权令牌更改标头中的授权令牌.这是您用来调用 Google API 的令牌.例如,对于日历,调用 https://www.googleapis.com/calendar/v3/users/me/calendarList.您将获得所需的响应.

        这是代码(我重用了变量):

        string responseText = string.Empty;string clientId = "DCuPWHknmv_k2xxxxxxxxxxxxxxxxx";//Auth0 ClientIdstring clientSecret = "7G3xlreFIULPZ9OtwlOxCX99xxxxxxxxxxxxxxxxxxx";//Auth0 ClientSecretstring accessToken = jsonObject.SelectToken("originalRequest.data.user.accessToken").ToString();尝试{使用 (var httpClient = new HttpClient()){httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer" + accessToken);var url = "https://xxx.auth0.com/userinfo";responseText = httpClient.GetStringAsync(url).Result;JObject jsonUserInfo = JObject.Parse(responseText);string userId = jsonUserInfo.SelectToken("sub").ToString();url = "https://xxx.auth0.com/oauth/token";var content = new FormUrlEncodedContent(new[]{new KeyValuePair("client_id", clientId),new KeyValuePair("client_secret", clientSecret),new KeyValuePair("audience", "https://[domain].auth0.com/api/v2/"),new KeyValuePair("grant_type", "client_credentials")});var postResult = httpClient.PostAsync(url, content).Result;jsonUserInfo = JObject.Parse(postResult.Content.ReadAsStringAsync().Result);accessToken = jsonUserInfo.SelectToken("access_token").ToString();httpClient.DefaultRequestHeaders.Remove("授权");httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer" + accessToken);url = "https://xxx.auth0.com/api/v2/users/" + userId;jsonUserInfo = JObject.Parse(httpClient.GetStringAsync(url).Result);accessToken = jsonUserInfo.SelectToken("identities[0].access_token").ToString();userId = jsonUserInfo.SelectToken("identities[0].user_id").ToString();httpClient.DefaultRequestHeaders.Remove("授权");httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer" + accessToken);url = "https://www.googleapis.com/calendar/v3/users/me/calendarList";responseText = httpClient.GetStringAsync(url).Result;}}

        Target/Summary: I have an Actions App developed in Google DialogFlow and I want the user be able to create Google Calendar Event using the App (from Google Assistant). In other words, authenticate the user to Allow my app to use his Calendar to create Events.

        What is done:

        1. Since Google Actions doesn't allow use of Google Auth/Token endpoints, I opted to use http://www.auth0.com.

        1. Created an account (used my Google account) on auth0.com, created an Application and setup the following values using their management panel (Domain, CliendId and ClientSecret generated by auth0):

        1. Created OAuth Client Id on Google Cloud Console Credentials page:

        1. Configured Actions Account Linking like so:

        1. Went back to auth0.com > Connections > Social > Enabled Google:

        1. Checked the "Sign in required" in DialogFlow > Integrations > Google Assistant:

        1. Wrote log on the first line of my DialogFlow Webhook method to log the following Response:

            {
            "originalRequest":{
               "source":"google",
               "version":"2",
               "data":{
                  "isInSandbox":true,
                  "surface":{
                     "capabilities":[
                        {
                           "name":"actions.capability.AUDIO_OUTPUT"
                        },
                        {
                           "name":"actions.capability.WEB_BROWSER"
                        },
                        {
                           "name":"actions.capability.MEDIA_RESPONSE_AUDIO"
                        },
                        {
                           "name":"actions.capability.SCREEN_OUTPUT"
                        }
                     ]
                  },
                  "inputs":[
                     {
                        "rawInputs":[
                           {
                              "query":"test",
                              "inputType":"KEYBOARD"
                           }
                        ],
                        "arguments":[
                           {
                              "rawText":"test",
                              "textValue":"test",
                              "name":"text"
                           }
                        ],
                        "intent":"actions.intent.TEXT"
                     }
                  ],
                  "user":{
                     "lastSeen":"2018-05-03T11:40:57Z",
                     "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
                     "locale":"en-US",
                     "userId":"15229245xxxxx"
                  },
                  "conversation":{
                     "conversationId":"15253476xxxxx",
                     "type":"ACTIVE",
                     "conversationToken":"["authentication","wh_patient-details"]"
                  },
                  "availableSurfaces":[
                     {
                        "capabilities":[
                           {
                              "name":"actions.capability.AUDIO_OUTPUT"
                           },
                           {
                              "name":"actions.capability.SCREEN_OUTPUT"
                           }
                        ]
                     }
                  ]
               }
            },
            "id":"1d6ed865-0803-49ca-bbac-xxxx",
            "timestamp":"2018-05-03T11:42:22.835Z",
            "lang":"en-us",
            "result":{
               "source":"agent",
               "resolvedQuery":"test",
               "speech":"",
               "action":"v00.xxxxx",
               "actionIncomplete":false,
               "parameters":{
                  "CallEnum":"Test"
               },
               "contexts":[
                  {
                     "name":"authentication",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":1
                  },
                  {
                     "name":"actions_capability_screen_output",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":0
                  },
                  {
                     "name":"actions_capability_audio_output",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":0
                  },
                  {
                     "name":"wh_patient-details",
                     "parameters":{
                        "patientId":0,
                        "CallEnum":"Test",
                        "fallbackLifespan":0,
                        "providerId":0,
                        "practiceId":0,
                        "CallEnum.original":"",
                        "fullDob":"01 January, 0001"
                     },
                     "lifespan":199
                  },
                  {
                     "name":"google_assistant_input_type_keyboard",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":0
                  },
                  {
                     "name":"actions_capability_web_browser",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":0
                  },
                  {
                     "name":"actions_capability_media_response_audio",
                     "parameters":{
                        "CallEnum":"Test",
                        "CallEnum.original":""
                     },
                     "lifespan":0
                  }
               ],
               "metadata":{
                  "intentName":"v00xxxx",
                  "isResponseToSlotfilling":false,
                  "intentId":"c7bd9113-d5b4-4312-8851-xxxxxxx",
                  "webhookUsed":"true",
                  "webhookForSlotFillingUsed":"false",
                  "nluResponseTime":556
               },
               "fulfillment":{
                  "speech":"Test",
                  "messages":[
                     {
                        "type":0,
                        "speech":"Test"
                     }
                  ]
               },
               "score":0.8399999737739563
            },
            "status":{
               "code":200,
               "errorType":"success"
            },
            "sessionId":"152534xxxxxxx",
            "isStackdriverLoggingEnabled":false
          

          }

        where the relevant section is:

        "user":{
                   "lastSeen":"2018-05-03T11:40:57Z",
                   "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
                   "locale":"en-US",
                   "userId":"15229245xxxxx"
                }
        

        1. From this post: See answer by @Prisoner

        The auth token (which you have issued, because you're the OAuth server) will be sent in the JSON object at originalRequest.data.user.accessToken.

        So I used the authorization token from above in the code below:

        string clientId = "361385932727-ksg6jgjxxxxxSNIP";
        string clientSecret = "rc2K1UUyntxxxxxxSNIP";
        string accessToken = jsonObject.SelectToken("originalRequest.data.user.accessToken");
        string userId = jsonObject.SelectToken("originalRequest.data.user.userId");
        
        IAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = new ClientSecrets
                    {
                        ClientId = clientId,
                        ClientSecret = clientSecret
                    },
                    Scopes = new[] { CalendarService.Scope.Calendar }
                });
        
        TokenResponse token = flow.ExchangeCodeForTokenAsync(userId, accessToken, 
            "https://oauth-redirect.googleusercontent.com/r/xxxxxxxxx", CancellationToken.None).Result;
        
        UserCredential credential = new UserCredential(flow, userId, new TokenResponse { AccessToken = token.AccessToken });
        CalendarService service = new CalendarService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "Test Auth0",
        });
        
        var list = service.CalendarList.List().Execute().Items;
        

        And the exception:

        Error:"invalid_grant", Description:"Malformed auth code.", Uri:""
        Stacktrace:    at Google.Apis.Auth.OAuth2.Requests.TokenRequestExtenstions.<ExecuteAsync>d__0.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<ExchangeCodeForTokenAsync>d__30.MoveNext()
        

        And when I change the ClientId/ClientSecret in the code above to the one from auth0.com, the exception is:

        Error:"invalid_client", Description:"The OAuth client was not found.", Uri:""
        Stacktrace:    at Google.Apis.Auth.OAuth2.Requests.TokenRequestExtenstions.<ExecuteAsync>d__0.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<FetchTokenAsync>d__35.MoveNext()
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
           at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
           at Google.Apis.Auth.OAuth2.Flows.AuthorizationCodeFlow.<ExchangeCodeForTokenAsync>d__30.MoveNext()
        

        What am I missing here? Can somebody help.

        解决方案

        Here are the "missing" steps after which I got this thing working. I am sure there must be alternate ways of doing this but I will suffice with this until I get some feedback here.

        Note: Google Actions Console UI has changed so the screens in the original question maybe different (but are complete).

        1. Make sure the Calendar API is enabled in the Google Cloud Console (API Library) against the project you have selected.

        1. Go back to https://manage.auth0.com > APIs. Edit the listed System API > Machine to Machine Applications. Authorize your listed Application and select/tick the following two scopes:

          read:users
          read:user_idp_tokens

        1. Go to Google Actions Console > Account Linking (see point no. 4) and check if you have set the following scopes:

        1. Change your code by:
          a. Call https://[domain].auth0.com/userinfo with the Authorization token from originalRequest.data.user.accessToken (see point 7 and 8). Successful response should give you the userId
          b. Post on https://[domain].auth0.com/oauth/token with request body containing your client_id, client_secret, audience, grant_type. Successful response should give you a new access_token.
          c. Change the Authorization token to the newly acquired access_token from point 11.b and make a call to https://[domain].auth0.com/api/v2/users/[userId] where the userId is the one you have from point 11.a. Successful response should give you a "Google" access_token and userId (under identities).
          d. Change the authorization token in the header with the one from point 11.c. This is the token you use to call the Google APIs. For example, for Calendars, call https://www.googleapis.com/calendar/v3/users/me/calendarList. You will get the required response.

        Here is the code (I have reused the variables):

        string responseText = string.Empty;
        string clientId = "DCuPWHknmv_k2xxxxxxxxxxxxxxxxx";     //Auth0 ClientId
        string clientSecret = "7G3xlreFIULPZ9OtwlOxCX99xxxxxxxxxxxxxxxxxxx";    //Auth0 ClientSecret
        string accessToken = jsonObject.SelectToken("originalRequest.data.user.accessToken").ToString();
        
        try
        {
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
                var url = "https://xxx.auth0.com/userinfo";
                responseText = httpClient.GetStringAsync(url).Result;
        
                JObject jsonUserInfo = JObject.Parse(responseText);
                string userId = jsonUserInfo.SelectToken("sub").ToString();
        
                url = "https://xxx.auth0.com/oauth/token";
                var content = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("client_id", clientId),
                    new KeyValuePair<string, string>("client_secret", clientSecret),
                    new KeyValuePair<string, string>("audience", "https://[domain].auth0.com/api/v2/"),
                    new KeyValuePair<string, string>("grant_type", "client_credentials")
                });
        
                var postResult = httpClient.PostAsync(url, content).Result;
                jsonUserInfo = JObject.Parse(postResult.Content.ReadAsStringAsync().Result);
                accessToken = jsonUserInfo.SelectToken("access_token").ToString();
        
                httpClient.DefaultRequestHeaders.Remove("Authorization");
                httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
        
                url = "https://xxx.auth0.com/api/v2/users/" + userId;
                jsonUserInfo = JObject.Parse(httpClient.GetStringAsync(url).Result);
                accessToken = jsonUserInfo.SelectToken("identities[0].access_token").ToString();
                userId = jsonUserInfo.SelectToken("identities[0].user_id").ToString();
        
                httpClient.DefaultRequestHeaders.Remove("Authorization");
                httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
        
                url = "https://www.googleapis.com/calendar/v3/users/me/calendarList";
                responseText = httpClient.GetStringAsync(url).Result;
            }
        }
        

        这篇关于DialogFlow/Actions:允许 Google 助理用户从 Actions 应用程序在 Google 日历中创建事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆