DialogFlow / Actions: Permitir al usuario del Asistente de Google crear eventos en Google Calendar desde la aplicación Actions

Objetivo / Resumen: Tengo una aplicación Actions desarrollada en Google DialogFlow y quiero que el usuario pueda crear Google Calendar Event usando la aplicación (desde el Asistente de Google). En otras palabras, autentique al usuario para Permitirm aplicación para usars Calendario para crear eventos.

Qué se hace:

Dado que Google Actions no permite el uso de puntos finales de Google Auth / Token, opté por usarhttp: //www.auth0.co.

Creé una cuenta (usé mi cuenta de Google) en auth0.com, creó una aplicación y configuró los siguientes valores utilizando su panel de administración Domain, CliendId y ClientSecret generado por auth0):

Id. De cliente OAuth creado en la página de credenciales de Google Cloud Console:

Vinculación de cuenta de acciones configuradas de esta manera:

Volvió a auth0.com> Conexiones> Social> Google habilitado:

Comprueba el "inicio de sesión requerido" en DialogFlow> Integraciones> Asistente de Google:

Escribí en la primera línea de mi método DialogFlow Webhook para registrar la siguiente respuesta:

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

}

donde la sección relevante es:

"user":{
           "lastSeen":"2018-05-03T11:40:57Z",
           "accessToken":"4CfRs-Lt5lWVQuyOYODvf1xxxxxxx",
           "locale":"en-US",
           "userId":"15229245xxxxx"
        }
De esta publicación:Ver respuesta de @ Prisoner

El token de autenticación (que ha emitido, porque usted es el servidor OAuth) se enviará en el objeto JSON en originalRequest.data.user.accessToken.

Así que usé el token de autorización de arriba en el código a continuación:

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;

Y la excepción:

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

Y cuando cambio ClientId / ClientSecret en el código anterior por el de auth0.com, la excepción es:

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

¿Que me estoy perdiendo aqui? Alguien puede ayudar.

Respuestas a la pregunta(1)

Su respuesta a la pregunta