Microsoft graph is sending a very strange value in the query string of a validation request when creating a Change Notification subscription. My subscription is created with the following code:
public async Task<Subscription> SubscribeToUserChanges()
{
var subscription = new Subscription
{
ChangeType = "created,updated",
NotificationUrl = UserChangesSubscriberUrl,
Resource = SubscriptionResources.AllUsers,
ExpirationDateTime = DateTimeOffset.UtcNow.AddDays(SubscriptionTimeConstants.UsersTimeInDays),
ClientState = UserChangesSubscriberVerificationSecret,
LatestSupportedTlsVersion = TlsVersions.V1_2
};
return await _graphClient.Subscriptions
.Request()
.AddAsync(subscription);
}
The following log is from my Azure Function handling the validation step of a Change Notification subscription. The code producing it is further below.
2022-09-06T16:33:40.049 [Information] Executing '[redacted]' (Reason='This function was programmatically called via the host APIs.', Id=[redacted])
2022-09-06T16:33:40.049 [Information] C# HTTP trigger function processed a request.
2022-09-06T16:33:40.049 [Information] ?validationToken=Validation%3a+Testing+client+application+reachability+for+subscription+Request-Id%3a+[redacted]
2022-09-06T16:33:40.050 [Information] Validation: Testing client application reachability for subscription Request-Id: [redacted]
2022-09-06T16:33:40.092 [Error] Invalid validation token. Expected 'fwldU*******', but got 'Valid*******'
2022-09-06T16:33:40.147 [Information] Executed 'UserChangedSubscriber' (Succeeded, Id=[redacted], Duration=97ms)
As you can see in 3rd line of the log, the validationToken sent in the query string is some description microsoft graph seems to have included in the request instead of my validationToken/changeState. I cannot figure why.
// Handles the change notifications
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var secret = Environment.GetEnvironmentVariable(EnvVarNames.UserChangesSubscriberVerificationSecret, EnvironmentVariableTarget.Process)
?? throw new Exception($"Missing environment variable '{EnvVarNames.UserChangesSubscriberVerificationSecret}'");
log.LogInformation(req.QueryString.Value);
if (req.Query.TryGetValue("validationToken", out var validationToken))
{
log.LogInformation(validationToken);
var token = validationToken.ToString();
if (token.Length < 5)
{
log.LogError($"Validation token {token} too short");
return new UnauthorizedResult();
}
// validation only
if (token != secret)
{
log.LogError($"Invalid validation token. Expected '{secret.Substring(0,5)}*******', but got '{token.Substring(0,5)}*******'");
return new UnauthorizedResult();
}
else
{
log.LogInformation("Subscription validation succeeded.");
return new ContentResult() { Content = validationToken, ContentType = "text/plain", StatusCode = 200 };
}
}
// ... Rest of function is ommitted as it does not apply to this issue
}