2
votes

I am currently subscribing to calendar events with:

var request = graphClient.Subscriptions.Request();

var result = await request.AddAsync(
    new Subscription {
        ChangeType = "created,updated,deleted",
            NotificationUrl = notificationUrl,
            Resource = "/users/" + userId + "/" + resource,
            ExpirationDateTime = DateTimeOffset.UtcNow.AddMinutes(4230),
            ClientState = "my-subscription-identifier"
    }
);

It is possible to declare a ClientState, so when a notification is received I can handle it by comparing the ClientState with the state sent in the create subscription request?

After I send my subscription request, the server sents me a validation message. What I am missing is the ClientState in the validation request from Microsoft Graph.

In the documentation under change notifications the request is described like this:

POST https://{notificationUrl}?validationToken={TokenDefinedByMicrosoftGraph}
ClientState: {Data sent in ClientState value in subscription request (if any)}

Unfortunately, I cannot find the client state in the body of the validation request (or any other part).

  • Is the client state declaration correct?

  • If yes, why is the ClientState not in the validation request as described in the documentation (since it seems important for security reasons)?

Edit:

I use ngrok for testing my webhooks. The raw data of the validation token post message that i receive looks like this:

POST /?validationToken=NmMwNjE5YjAtNzc3Zi00NmMwLWI1ZmYtYjJiNWI5NzU0MGY5 
HTTP/1.1
Host: localhost:12345
User-Agent: Go-http-client/1.1
Content-Length: 0
Content-Type: text/plain; charset=utf-8
X-Forwarded-For: 
X-Forwarded-Proto: https
X-Original-Host: 5a665085.eu.ngrok.io

If i receive an update notification for example the request body looks like this:

{
"value": [
    {
        "subscriptionId": "de6c71b...",
        "subscriptionExpirationDateTime": "2018-06-02T05:11:28.6421943+00:00",
        "changeType": "updated",
        "resource": "Users/9c4661.../Events/AQMkAGY4Y2E0YjZiLTA4ZDctNGJ...",
        "resourceData": {
            "@odata.type": "#Microsoft.Graph.Event",
            "@odata.id": "Users/9c4661.../Events/AQMkAGY4Y2E0YjZiLTA4ZDctNGJ...",
            "@odata.etag": "W/\"DwAAABYAAABzyrM9Qi...\"",
            "id": "AQMkAGY4Y2E0YjZiLTA4ZDctNGJ..."
        },
        "clientState": "my-subscription-identifier"
    }
]
}
1
Can you include the payload you are getting back? You may want to use something like Fiddler to sniff it right off the write instead of trying to parse logs for it. - Marc LaFleur
@MarcLaFleur I am using ngrok for testing and posted the raw data of the validation token post request shown in their web interface. Hope this helps. - Michael Hufnagel
I'm pretty sure this is an error in the docs and that clientState is not sent in validation requests. I see the same behavior. I'm double-checking with the Graph devs to confirm. - Jason Johnston
If it is an error in the docs may i ask why it isn't included. I think it is important to only answer my own validation token requests (identify them with my client state). - Michael Hufnagel
My gut reaction is the same as yours. It certainly seems like you'd want clientState returned as part of the validation; particularly if you're encoding additional information into clientState to connect the dots. That said, my gut has been mistaken may times in the past so there may very well be a solid reason why this isn't being sent. - Marc LaFleur

1 Answers

2
votes

The goal for initial validation is for Microsoft to validate the client's notification URL: that it is up and responsive, and that it can accept notifications.

Validations are meant to be side-effect free and not disclose any information.

So far, we have not encountered a scenario where including clientState was useful, and that is why we are not including it in the validation request.

If you have a case in which including more information in this request would be useful (e.g. as part of a bigger scenario/workflow) can you briefly describe it?

Note: we will soon update the documentation to be consistent with the API behavior.