DialogFlow / Actions:允许Google智能助理用户通过Actions App在Google日历中创建活动

时间:2018-04-27 12:41:24

标签: auth0 actions-on-google dialogflow google-calendar-api

:定位/摘要: 我有一个使用Google DialogFlow开发的动作应用程序,我希望用户能够使用该应用程序(来自Google智能助理)创建Google日历活动。换句话说,验证用户是否允许我的应用程序使用他的日历创建活动。

做了什么:

  1. 由于Google Actions不允许使用Google Auth / Token端点,因此我选择使用http://www.auth0.com
  2. Actions > Account Linking

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

      Settings-2

      Advanced-Settings-1

      Advanced-Settings-2

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

        Credentials-2

        Consent-Screen

        1. 配置操作帐户链接如下:
        2. Account-Linking-1

          Account-Linking-Scope

          1. 回到auth0.com>连接>社交>启用Google:
          2. Auth0 Google Connection

            1. 检查DialogFlow中的“需要登录”>整合> Google智能助理:
            2. GA

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

                  {
                  "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
                

                }

              2. 相关部分是:

                "user":{
                           "lastSeen":"2018-05-03T11:40:57Z",
                           "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
                           "locale":"en-US",
                           "userId":"15229245xxxxx"
                        }
                
                1. 来自这篇文章:See answer by @Prisoner
                2.   

                  身份验证令牌(您已发出,因为您是OAuth   服务器)将在JSON对象中发送   originalRequest.data.user.accessToken。

                  所以我在下面的代码中使用了上面的授权令牌:

                  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;
                  

                  例外:

                  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()
                  

                  当我将上面代码中的ClientId / ClientSecret更改为auth0.com中的那个时,例外是:

                  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()
                  

                  我在这里缺少什么?有人可以帮忙。

1 个答案:

答案 0 :(得分:3)

以下是&#34;缺失&#34;之后,我让这件事工作了。我确信必须有其他方法可以做到这一点,但我会满足于此,直到我得到一些反馈。

注意:Google Actions控制台用户界面已更改,因此原始问题中的屏幕可能不同(但已完成)。

  1. 确保针对您选择的项目在Google Cloud Console(API库)中启用了Calendar API。
  2. Google-Calendar-API-Enable

    1. 返回https://manage.auth0.com&gt;蜜蜂。编辑列出的System API&gt;机器到机器应用。授权您列出的应用程序并选择/勾选以下两个范围:

      读:用户
      读:user_idp_tokens

    2. Auth0-APIs

      APIs-Auth

      1. 转到Google操作系统控制台&gt;帐户链接(参见第4点)并检查您是否设置了以下范围:
      2. Actions-Scopes

        1. 通过以下方式更改您的代码:
          一个。使用originalRequest.data.user.accessToken中的授权令牌调用https://[domain].auth0.com/userinfo(请参阅第7和第8点)。成功的回复应该给你userId
          湾在https://[domain].auth0.com/oauth/token上发布请求正文,其中包含您的client_id,client_secret,audience,grant_type。成功的回复应该会给你一个新的access_token C。将授权令牌从点11.b更改为新获取的access_token,并调用https://[domain].auth0.com/api/v2/users/[userId],其中userId是您从点11.a开始的那个。成功的回复应该会给你一个&#34; Google&#34; access_token和userId(在身份下) d。使用点11.c中的一个更改标题中的授权令牌。这是您用来调用Google API的令牌。例如,对于日历,请致电https://www.googleapis.com/calendar/v3/users/me/calendarList。您将获得所需的响应。
        2. 这是代码(我已经重用了变量):

          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;
              }
          }