Google Functions authentication issue

Dear colleagues,
Several days ago we encoutered the issue described here:

Since in the discussion some tests are mentioned, we would like to know, whether we have a final solution about this topic. Could we use Trigger URL of the Functions in secure way (with authentication) and how should we realize this?

Best regards,
Borislav

Hi Borislav,

We resolved the issue in that thread and the updated documentation should get you working on the trigger route instead of the test route:

One big limitation is that you can only connect to one cloud function per integration. You could have several actions connect to the same Google Cloud Function, but each individual cloud function will need a separate integration or else you are going to get all kinds of problems.

--Jason

Hi Jason,
Thank you very much for your reply! I read information you provided even before to write here in the Forum. The problem is, that I cannot see, how we could send Authentication Token with our request for invoking the Google Function. And we must do it, when we use Trigger Url. Another option is to use unauthenticated invokation, but as it is written in the documentation and as we all know it, this is unsecure. In my understanding we should send Authentication Token, which should be renewed on every 60 minutes. So, if I am right, I don`t see in the information you provided the answer of my question. Would you go a little bit in details, please?

Best regards,
Borislav

Hi Borislav,

The authentication should be taken care of you automatically.

We generate tokens based on the credential configuration you provided in your Google Data Action integration.

We generate a new token If we don't have a cached authentication token or if we get an authentication error invoking a cloud function. That token will then be added to the request that we send to the Google cloud function endpoint.

So you should be able to configure an integration and action to hit your cloud function without having to make that cloud function publicly accessible. Are you unable to get your data action / cloud function to work that way?

--Jason

Hi Jason,
I still cannot get my Functions working. But initialy about Example Function from the Documentation. I would like to mention, that in zipped json you could download from the page, you still have Test URL:
{
"name": "Test-Cloud-Function-Math-Example - Exported 2020-07-28 @ 11:06",
"integrationType": "google-cloud-data-actions",
"actionType": "custom",
"config": {
"request": {
"requestUrlTemplate": "https://cloudfunctions.googleapis.com/v1/projects/businesscallingapi/locations/us-central1/functions/function-test-action-math-example:call",..............

This is a little bit confusing, but it is not the main isuue. The main issue is, that when I use Test URL, I get the result of mathematical operation (the sum of the two numbers), but when I use Trigger URL, I get OK, but not the sum of the numbers.
Speaking about our productive functions, when we repalce the Test URL with Trigger URL, we got the following error:

{
"message": "The request could not be understood by the server due to malformed syntax.",
"code": "bad.request",
"status": 400,
"messageParams": {},
"contextId": "681f013c-b506-4fd9-8fc5-eb2d9f4a740b",
"details": [
{
"errorCode": "ACTION.REMOTE_ENDPOINT"
}
],
"errors": [
{
"message": "REST call for action execute failed. Message: We were unable to process the response from the remote endpoint because it had a 'content-type' header of 'text/html; charset=utf-8' instead of the required value of 'application/json'. [681f013c-b506-4fd9-8fc5-eb2d9f4a740b]",
"code": "BAD_REQUEST",
"status": 400,
"messageParams": {},
"details": [],
"errors": []
}
]
}

It is confusing for me, since I change only URL and nothing about the format.

I tried to add some screenshots, but there is some restrictions here. Could I send them personaly in any way?

Best regards,
Borislav

HI Borislav_Taskov,

Could you provide me a link to the documentation that still shows that out of date requestUrlTemplate example. The one at Example Google Cloud Function with a data action - Genesys Cloud Resource Center shows the example URL to be "https://us-central1-businesscallingapi.cloudfunctions.net/function-test-action-math-example"

If you are using an old example, you might also have incorrect request template that processed the request body differently than the trigger uses. The CALL api uses a string encoded JSON body that the TRIGGER api does not need.

The correct "config" should look like this:

"config": {
"request": {
"requestUrlTemplate": "https://us-central1-businesscallingapi.cloudfunctions.net/function-test-action-math-example",
"requestType": "POST",
"headers": {}
},
"response": {
"translationMap": {},
"translationMapDefaults": {},
"successTemplate": "${rawResult}"
}
}

I copied this from the example at the link provided. Please let me know if you still see the ":call" version there or anywhere else in our docs.

Hi Greg,
I used the link from Jason. From this page you could download zipped Test Function. And in this zipped JSON I see again the old URL. Also in zipped JSON you have this template in configuration:

"requestTemplate": "{ "data": "$esc.jsonString(${input.rawRequest})" }"

And in the text in the page you don`t have it.
I have to make more tests for detailed feedback, but I am really confused. All our Google Functions worked properly with TEST URL. When we switched to TRIGGER URL we started getting error messages, when we tested the functions. Initially I thought this was due to authorization, but now I see we actually fire the functions via the TRIGGER URL, but we recieve error messages in Genesys, that the response is not in proper format. And we changed nothing in the Google Function. How it happens, that with TEST URL the format is proper and with TRIGGER URL not?

Best regards,
Borislav

Best regards,
Borislav

Thanks for the details Barislav. I will get a story in for us updating the zip file. You are correct that is is out of date. The example text is current though, and the text Jason pasted
When using the trigger, the config I gave you is the correct example.
For calling triggers there are two changes needed different than using the CALL endpoint.

  1. No need for the requestTemplate manipulation we needed to use for the CALL format.
  2. The URL needs to be the trigger URL defined for your function in the Google config.

If you have both of those change and are still getting the error, please run as a /test and post the full output for review along with the correlation id returned and the region you are using.

I would also like the exact URL you are using in your config, just to be sure the endpoint you are hitting is correct.

Hi Greg,
Thank you very much for reply!
My original configuration with Test Url is:
request

{
"requestUrlTemplate": "https://cloudfunctions.googleapis.com/v1/projects/cf-genesys-dev-t7/locations/europe-west3/functions/gc_query_dedicated_skill:call",
"requestType": "POST",
"headers": {},
"requestTemplate": "{ "data": "$esc.jsonString(${input.rawRequest})" }"
}

response
{
"translationMap": {},
"translationMapDefaults": {},
"successTemplate": "${rawResult}"
}

When I test the function with this configuration everything is OK and I get result like this:
{
"skill": "FSD"
}

Then I made the corrections for Trigger Url:
request

{
"requestUrlTemplate": "https://europe-west3-cf-genesys-dev-t7.cloudfunctions.net/gc_query_dedicated_skill",
"requestType": "POST",
"headers": {},
"requestTemplate": "${input.rawRequest}"
}
(I cannot fully remove requestTemplate. I tried to remove, but aftrer saving I get it automatically back. That is why I left "${input.rawRequest}")

response is the same.

When test the function in this way, I get following error message:
{
"message": "The request could not be understood by the server due to malformed syntax.",
"code": "bad.request",
"status": 400,
"messageParams": {},
"contextId": "495868eb-bf0e-4340-91ef-ce1d006377d4",
"details": [
{
"errorCode": "ACTION.REMOTE_ENDPOINT"
}
],
"errors": [
{
"message": "REST call for action execute failed. Message: We were unable to process the response from the remote endpoint because it had a 'content-type' header of 'text/html; charset=utf-8' instead of the required value of 'application/json'. [495868eb-bf0e-4340-91ef-ce1d006377d4]",
"code": "BAD_REQUEST",
"status": 400,
"messageParams": {},
"details": [],
"errors": []
}
]
}

In the same time I see in Google Cloud, that the Function is executed correctly:
Default

2022-07-11T17:50:13.405372Z

gc_query_dedicated_skilldtf8twopmnde FSD

FSD

Debug

2022-07-11T17:50:13.407843979Z

gc_query_dedicated_skilldtf8twopmnde Function execution took 42 ms. Finished with status code: 200

Function execution took 42 ms. Finished with status code: 200

Still some issue in format, I believe.
Best regards,
Borislav

Hi Borislav,

I checked our logs and the content we are getting back is text/html. The call to authenticate, and to the europe-west3-cf-genesys-dev-t7.cloudfunctions.net endpoint all looked good.
The only suggestion I have is to change the permissions temporarily on your cloud function to allow unauthenticated access, and see what happens when you access the trigger from PostMan or curl to see what the text response we are getting looks like.

Hi Greg,
I had the same idea, but it is a little bit tricky to do this. I don`t have sufficient rights in Google Cloud to set unauthenticated access. I have to create a ticket about the topic and explain a lot the responsible colleagues. And even afterwards I am not sure I will get this temporary unauthenticated access. Of course, I could try, but it will take time also. Do you have any other idea how we could test and troubleshoot? Could we change the format of the response in any way?

Best regards,
Borislav

You might be able to use ngrok to capture the request response. Ngrok lets you setup a proxy that you would configure to point to the Google endpoint, and would change your Action to call the ngrok assigned proxy. You run the proxy locally. We often use this when doing internal testing of new endpoints to see the request/response flow.

Hi Greg,
I will try it tomorrow and write you about the result. At the moment here it is almost 23:00 and cannot go on working.

Best regards,
Borislav

Hi again Greg,
The colleagues granted only for very short time Public Access to the Function and I was able to test from Postman.
My cURL was:

curl --location --request POST 'https://europe-west3-cf-genesys-dev-t7.cloudfunctions.net/gc_query_dedicated_skill'
--header 'Content-Type: application/json'
--data-raw ' {
"integrationId": "be7e7424-e9dc-41b9-9471-abf71ddb184b",
"rawRequest": "{"contact":"359899884321","area_code":"DE_DUS_CCC"}",
"orgId": "720bb830-ca9b-47d1-bc1d-879e04bddc27",
"contact": "359899884321",
"area_code": "DE_DUS_CCC"
}'

and as a Response I recieved:

{"skill": "FSD"}

Best regards,
Borislav

Hi Borislav,

Can you look at the headers of the response, specifically the "Content-Type" header. We expect that header to include "application/json". If it is returning something like text/plain then that function needs to be updated to set the content-type to application/json.

--Jason

What was the content type header returned?

Hi Jason,
Great! We will try to meet your expectations.
Just a small question. Where in the documentation are they described? This about the response header, I mean. Obviously we have missed it.

Best regards,
Borislav

It would have been details from the curl. -v for verbose.

Just a small question. Where in the documentation are they described? This about the response header, I mean. Obviously we have missed it.

I don't see the content-type header requirement spelled out. On this page:

on the "Web services" tab it shows a requirement of "JSON-based web service calls." which I would expect to have a content-type that includes application/json for any response that includes a body. We can see about adding that as a documented requirement.

--Jason

Yes, if this requrement is valid for Google also, it will be nice, when it is written, I think. In Web Service I havent check at all, since I dont use this Integration. And one hint more. In your Example Function you have:

function createResponse(req) {

var response = {};
response.sumOfNumber1AndNumber2 = req.body.inputNumber1 + req.body.inputNumber2;
return response;

}

There is nothing about formating the response (its header).

Maybe, if you provide this in example, it will be easier for everyone.

Best regards,
Borislav