DialogFlow / Actions: разрешить пользователю Google Assistant создавать событие в Календаре Google из приложения Actions - PullRequest
0 голосов
/ 27 апреля 2018

Target / Резюме : У меня есть приложение "Действия", разработанное в Google DialogFlow, и я хочу, чтобы пользователь мог создавать событие Календаря Google с помощью приложения (из Google Assistant). Другими словами, аутентифицируйте пользователя, чтобы разрешить приложению my использовать его Календарь для создания событий.

Что сделано:

  1. Так как в Google Actions не разрешено использование конечных точек Google Auth / Token, я решил использовать http://www.auth0.com.

image Account Linking">

  1. Создал учетную запись (использовал мою учетную запись Google) на auth0.com , создал приложение и настроил следующие значения с помощью их панели управления ( Domain, CliendId и ClientSecret генерируется auth0):

Settings-1

Settings-2

Advanced-Settings-1

Advanced-Settings-2

  1. Создан идентификатор клиента OAuth на странице учетных данных консоли Google Cloud:

Credentials-1

Credentials-2

Consent-Screen

  1. Настроенные действия привязки аккаунта примерно так:

Account-Linking-1

Account-Linking-Scope

  1. Вернулся на auth0.com> Подключения> Социальные сети> Включен Google:

Auth0 Google Connection

  1. Проверено «Требуется вход в систему» ​​в DialogFlow> Интеграции> Google Assistant:

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
    

    }

, где соответствующий раздел:

"user":{
           "lastSeen":"2018-05-03T11:40:57Z",
           "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
           "locale":"en-US",
           "userId":"15229245xxxxx"
        }
  1. Из этого поста: Смотрите ответ @ Prisoner

токен авторизации (который вы выдали, потому что вы 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 голосов
/ 09 мая 2018

Вот «пропущенные» шаги, после которых я начал работать. Я уверен, что должны быть альтернативные способы сделать это, но я буду достаточно с этим, пока я не получу некоторую обратную связь здесь.

Примечание : пользовательский интерфейс консоли действий Google изменился, поэтому экраны в исходном вопросе могут отличаться (но они завершены).

  1. Убедитесь, что API календаря включен в консоли Google Cloud Console (Библиотека API) для выбранного вами проекта.

Google-Calendar-API-Enable

  1. Вернитесь к https://manage.auth0.com> API. Отредактируйте перечисленный Системный API> Приложения для Машины. Авторизуйте указанное приложение и выберите / отметьте следующие две области:

    следующим образом: пользователи
    следующим образом: user_idp_tokens

Auth0-APIs

APIs-Auth

  1. Перейдите в Консоль действий Google> Связывание аккаунта (см. Пункт № 4) и проверьте, установлены ли следующие области действия:

Actions-Scopes

  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.
    с. Измените токен авторизации на вновь полученный access_token из пункта 11.b и сделайте вызов https://[domain].auth0.com/api/v2/users/[userId], где userId - это тот, который у вас есть из пункта 11.a. Успешный ответ должен дать вам "Google" access_token и userId (под идентификацией).
    д. Измените токен авторизации в шапке на токен из пункта 11.c. Это токен, который вы используете для вызова API Google. Например, для Календарей звоните https://www.googleapis.com/calendar/v3/users/me/calendarList.. Вы получите требуемый ответ.

Вот код (я использовал переменные):

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