Search code examples
google-apps-scriptpostmanwebhooksstrava

Problem creating a Strava webhook subscription using Google Apps Script



EDIT 2 - I have now provided my own answer to the issue - any further thoughts or inputs would still be appreciated

EDIT 1 - POTENTIAL RELEVANT INFORMATION:

I've found this footnote at the bottom of the Content Service documentation page that says:

For security reasons, content returned by the Content service isn't served from script.google.com, but instead redirected to a one-time URL at script.googleusercontent.com. This means that if you use the Content service to return data to another application, you must ensure that the HTTP client is configured to follow redirects. For example, in the cURL command line utility, add the flag -L. Check the documentation for your HTTP client for more information on how to enable this behavior.

This seems relevant as I am using ContentService to serve data to Strava's GET request - would this not mean that the response it receives comes from a different URL and hence not from the specified callback URL?


I've been trying to use Google Apps Script to create a webhook subscription to Strava and I feel like I'm extremely close to figuring it out, but I've encounter one last hurdle that I can't seem to pass.

The documentation for creating a webhook subscription to Strava is listed here and I've reached the stage at which I'm making a POST to the Strava API requesting a subscription. Strava then sends an HTTP GET request to my callback_url that I've specified which contains some parameters - most important of which is the hub.challenge parameter. This param must then be sent back within 2 seconds by my callback address in order for the link to be validated. This is where I'm having some grief.

function doGet(e) {

  var hubChal = e.parameter["hub.challenge"];

  var result = {
    "hub.challenge": hubChal
  };

  return ContentService.createTextOutput(JSON.stringify(result))
    .setMimeType(ContentService.MimeType.JSON);

}

Above is my current function that is dealing with incoming GET requests.

In the documentation it states:

Your callback address must respond within two seconds to the GET request from Strava’s subscription service. The response should indicate status code 200 and should echo the hub.challenge field in the response body as application/json content type: { “hub.challenge”:”15f7d1a91c1f40f8a748fd134752feb3” }

However, when I send the POST to the webhook subscription API I receive this response on Postman:

{
    "message": "Bad Request",
    "errors": [
        {
            "resource": "PushSubscription",
            "field": "callback url",
            "code": "GET to callback URL does not return 200"
        }
    ]
}

I checked through the troubleshooting advice that is offered on the doc page, an element of which gives a sample GET request for you to send yourself to see what your callback address offers in return:

Check that the response to the above request shows a 200 status and correctly echos the hub.challenge in the JSON body. The response body to the above sample curl request should look like { “hub.challenge”:”15f7d1a91c1f40f8a748fd134752feb3” }

With the following dummy GET request:

{your-callback-url}?hub.verify_token=test&hub.challenge=15f7d1a91c1f40f8a748fd134752feb3&hub.mode=subscribe

Plugging in my callback url and sending a GET request via Postman returns the following to me:

{
    "hub.challenge": "15f7d1a91c1f40f8a748fd134752feb3"
}

Along with showing a 200 OK Status code and in well under 2 seconds.

I really can't see what I've done incorrectly here, as it seems like I'm fulfilling the criteria laid out to set up a subscription. It's worth mentioning that I'm not very familiar with Google Apps Script, so it's entirely possible and even likely that I'm missing something basic, but I can't see it for the life of me.

I've gone through all of the troubleshooting advice and haven't been able to find an answer online despite an entire afternoon and evening of searching yesterday. Any help would be enormously appreciated - thanks.


Solution

  • I believe I've finally figured out my issue and unfortunately it seems it is not possible to set up a webhook subscription to Strava using Google Apps Script - at least not whilst using ContentService to serve data to Strava GET requests.

    At the end of the ContentService documentation it says:

    For security reasons, content returned by the Content service isn't served from script.google.com, but instead redirected to a one-time URL at script.googleusercontent.com. This means that if you use the Content service to return data to another application, you must ensure that the HTTP client is configured to follow redirects. For example, in the cURL command line utility, add the flag -L. Check the documentation for your HTTP client for more information on how to enable this behavior.

    The problem with this is that Strava expects and will only accept a 200 status code attached to the response to its GET request, however ContentService sends a 302 redirect to this one-time URL. I was able to verify this in Postman by turning off "Automatically follow redirects" in the request settings of my dummy GET request:

    https://i.sstatic.net/vAvIm.png

    Since I have auto redirect enabled by default on Postman, I wasn't seeing the 302 at any point when sending my own requests, leading to everything appearing to be in order.

    If there is a way around this ContentService issue then please do inform me, as the outcome of this is that I'm now going to have to poll Strava for new data (booo) which will make my finished product significantly more clunky.

    Hopefully this post will now stand to help anyone else who finds themselves with my issue in the future and will save them a few days of confusion!