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:
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"
}
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).
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
originalRequest.data.user.accessToken
(see point 7 and 8). Successful response should give you the userId
access_token
.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;
}
}
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments