Webhook creation for workspace on project changed fails

I am attempting to create a webhook that executes whenever a project’s dates in a workspace change. I’m using the following body to do that:

{
  "data": {
    "filters": [
      {
        "action": "changed",
        "fields": [
          "start_on",
          "due_on",
          "due_date"
        ],
        "resource_type": "project"
      }
    ],
    "resource": "123456910",
    "target": "https://myhookurl.com"
  }
}

The request returns a “403: invalid_filters_for_larger_scoped_webhooks”. Looking at this post, hooking on a workspace for project changes should be perfectly acceptable. Interestingly the webhook creation loop actually continued still and I got a subsequent request with an x-hook-secret header. However, the hook doesn’t seem to be working and does not appear to exist when I query Asana’s API for hooks on the workspace.

Is this a bug in the webhook creation logic?

Hi @Matt_Bowser,

A webhook on a workspace to return project changes within that workspace definitely works; I use it extensively in Flowsana.

Two thoughts here:

(1)
due_date is a deprecated field; try removing that and see if it helps.

(2)
I’m not at all sure that webhooks on higher-level objects than tasks support the fields parameter. That could be the issue.

Let us know here if it still doesn’t work after removing due_date.

It didn’t seem to be a problem with due_date specifically. I removed all field filters and I have a webhook working, though its pretty noisy. Thank you for your help!

I also want to note for folks who might reference this post in the future that opt_fields in the query parameters of the webhook creation POST do not seem to be respected. The resulting event stream contains who changed a field, what field changed, and how it changed (modified, removed, etc.). There is no information about what the fields changed to or from.

{
“events”: [
{
“user”: {
“gid”: “1234”,
“resource_type”: “user”
},
“created_at”: “2023-04-24T15:46:02.999Z”,
“action”: “changed”,
“resource”: {
“gid”: “5678”,
“resource_type”: “project”
},
“parent”: null,
“change”: {
“field”: “due_date”,
“action”: “changed”
}
},
{
“user”: {
“gid”: “1234”,
“resource_type”: “user”
},
“created_at”: “2023-04-24T15:46:02.999Z”,
“action”: “changed”,
“resource”: {
“gid”: “5678”,
“resource_type”: “project”
},
“parent”: null,
“change”: {
“field”: “due_on”,
“action”: “changed”
}
}
]
}

Hi @Matt_Bowser ,

There might be some confusion with how the opt_fields query param works for our webhooks.

For context, the opt_fields option for our Establish a webhook is meant for the response from creating that webhook not for the webhook events that your target URL receives.

So let’s say you made a call to our Establish a webhook endpoint. With the following request body:

{
    "data": {
        "resource": "<PROJECT_GID>",
        "target": "<YOUR_TARGET_URL>"
    }
}

You will get a response like the following:

{
    "data": {
        "gid": "<WEBHOOK_GID>",
        "resource_type": "webhook",
        "resource": {
            "gid": "<PROJECT_GID>",
            "resource_type": "project",
            "name": "Test Project"
        },
        "target": "<YOUR_TARGET_URL>",
        "active": true,
        "is_workspace_webhook": false,
        "created_at": "2023-05-19T21:36:29.054Z",
        "last_failure_at": null,
        "last_failure_content": "",
        "last_success_at": "2023-05-19T21:36:29.386Z",
        "filters": []
    }
}

Now let’s say you do this again but this time specify target in the opt_fields query params. The result you will get back from this creation request will look like:

{
    "data": {
        "gid": "<WEBHOOK_GID>",
        "target": "<YOUR_TARGET_URL>"
    }
}

Now you probably want to ask how would you receive information about what the fields changed to and from → We recommend that you extract the gid of the resource the webhook event sent back and make another API call to get that information. Our webhooks are specifically designed not let users specify which properties they want back from our events for security reasons because we don’t want attackers to intercept webhook event responses and see sensitive data. Instead we rely on our developers to use the information they received from the event to look up the information they need. (See my comment here on this forum thread)

I hope this makes sense. Let me know if I mis-understood your question.

Hey John,

This is interesting; I had always heard previously from Asana dev folks that the reason webhooks didn’t send actual data was for efficiency’s sake, not for security. I feel like I’m just missing something here on the security angle: what’s the difference between the webhook and a response back on subsequent call to the API to get specific data? Why is the webhook easier for an attacker to intercept than the response from any other call to the API?

Hi @Phil_Seeman,

Apologies for any confusion that may have been caused from my wording. Yes. That is another reason why we don’t send actual data. The security reasoning from my understanding is because events are being sent unpredictably which poses a higher risk if data is sent along with it vs a request being made by a known requester and sending data back to that requester immediately. Another risk is if someone manages to establish a webhook they would be able to collect tons of information under the radar because we send data to the target URL while with API calls we can determine requester on each request. This is just my understanding based off what I’ve learned so far about our webhooks. I can double check with our webhook team on the design decision.

Hi @John_Vu,

Thanks for the additional info! Yes, it might be interesting to see what additional input the webhook team might have.

Hi @Phil_Seeman ,

I talked to the team and it does look like security was part of the design decision with our webhooks. To answer your question

Q:

  • What’s the difference between the webhook and a response back on subsequent call to the API to get specific data? Why is the webhook easier for an attacker to intercept than the response from any other call to the API?

A:

  • To make a follow up request to load data, you need a way to authenticate with the API. This means getting a PAT / OAuth token, then making a request.
  • To get information from a compromised webhook, you just need a way to access the data we send to a particular route. Maybe you get access to its logs, or are able to takeover that route in the domain.
  • Getting access credentials is much harder than e.g. getting access to logs. So as a best practice for securing webhooks we decided to send no customer data other than identifiers. The other option was to encrypt the webhooks, so that if someone can see the responses they’re useless without a secret (this is something that we might implement in the future).

Essentially we believe it is harder for someone to get access to access credentials than it is for them to overtake a webhook route. Additionally, If someone gains access to your target URL then they’ll be able to get a direct stream of all events that the webhook is subscribed to which is a lot more volume of sensitive data compared to them gaining access to an individual GET request.

1 Like

Thanks, @John_Vu , very informative!

1 Like