DialogFlow / Actions: permite que o usuário do Google Assistant crie um evento no Google Calendar a partir do aplicativo Actions

Objetivo / Resumo: Tenho um aplicativo de ações desenvolvido no Google DialogFlow e desejo que o usuário possa criar um evento do Google Agenda usando o aplicativo (do Assistente do Google). Em outras palavras, autentique o usuário para permitirmeu aplicativo para usardele Calendário para criar eventos.

O que é feito:

Como o Google Actions não permite o uso de pontos de extremidade de autenticação / token do Google, optei por usarhttp://www.auth0.com.

Criou uma conta (usou minha conta do Google) emauth0.com, criou um aplicativo e configurou os seguintes valores usando seu painel de gerenciamento (Domínio, CliendId eClientSecret gerado por auth0):

ID do cliente OAuth criado na página Credenciais do Google Cloud Console:

Ações configuradas Vinculação de conta da seguinte forma:

Voltei para auth0.com> Conexões> Social> Google ativado:

Verificado o "Login necessário" em DialogFlow> Integrações> Assistente do Google:

Escreveu o log na primeira linha do meu método DialogFlow Webhook para registrar a seguinte resposta:

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

}

onde a seção relevante é:

"user":{
           "lastSeen":"2018-05-03T11:40:57Z",
           "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
           "locale":"en-US",
           "userId":"15229245xxxxx"
        }
A partir deste post:Ver resposta de @Prisoner

O token de autenticação (que você emitiu porque é o servidor OAuth) será enviado no objeto JSON em originalRequest.data.user.accessToken.

Então, usei o token de autorização de cima no código abaixo:

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;

E a exceção:

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

E quando altero o ClientId / ClientSecret no código acima para o de auth0.com, a exceção é:

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

O que estou perdendo aqui? Alguém pode ajudar.

questionAnswers(1)

yourAnswerToTheQuestion