【发布时间】:2021-09-10 22:55:13
【问题描述】:
我们正在使用 Microsoft Graph(测试版)Webhook 来获取有关 Microsoft Teams 中的状态更改的通知,并且目前我们的客户遇到了问题。
当我们从 Graph API 接收到存在更改通知时,它不包含 validationTokens 属性,因此验证和后续处理失败。
我们的代码类似于微软提供的sample。
客户收到的请求的(简化/缩短的)内容如下所示:
{
"value": [
{
"subscriptionId": "...",
"clientState": "...",
"changeType": "updated",
"resource": "communications/presences?$filter=id+in+(...)",
"subscriptionExpirationDateTime": "2021-04-22T02:06:56.2872368-07:00",
"resourceData": {
"@odata.id": "communications/presences?$filter=id+in+(...)",
"@odata.type": "#Microsoft.Graph.presence",
"id": "..."
},
"tenantId": "...",
"encryptedContent": {
"data": "...",
"dataSignature": "...",
"dataKey": "...",
"encryptionCertificateId": "3",
"encryptionCertificateThumbprint": "..."
}
}
]
}
与我们的实验室相比,请求正文缺少validationTokens 属性:
{
"value": [
{
"subscriptionId": "...",
"clientState": "...",
"changeType": "updated",
"resource": "communications/presences?$filter=id+in+(...)",
"subscriptionExpirationDateTime": "2021-04-26T00:07:08.9251516-07:00",
"resourceData": {
"@odata.id": "communications/presences?$filter=id+in+(...)",
"@odata.type": "#Microsoft.Graph.presence",
"id": "..."
},
"tenantId": "...",
"encryptedContent": {
"data": "...",
"dataSignature": "...",
"dataKey": "...",
"encryptionCertificateId": "3",
"encryptionCertificateThumbprint": "..."
}
}
],
"validationTokens": [
"..."
]
}
根据doc,validationTokens 仅提供给带有资源数据的更改通知——这里就是这种情况,所以我们猜测应该存在validationTokens?
欢迎任何提示。
编辑 这是一个用于反序列化请求正文/处理通知请求的简短代码:
<HttpPost("/Notification/{connectorId}/{apiLinkId}")>
Public Async Function Listen(connectorId As Guid, apiLinkId As Guid, <FromQuery> Optional validationToken As String = Nothing) As Task(Of IActionResult)
If Not String.IsNullOrEmpty(validationToken) Then
' Validate the new subscription by sending the token back to Microsoft Graph.
' This response is required for each subscription.
Return Content(WebUtility.HtmlEncode(validationToken))
End If
Try
' Parse the received notifications.
Dim options As New JsonSerializerOptions With {.PropertyNameCaseInsensitive = True}
options.Converters.Add(New JsonStringEnumConverter(JsonNamingPolicy.CamelCase))
Dim plainNotifications As New Dictionary(Of String, ChangeNotification)()
Dim notificationCollection = Await JsonSerializer.DeserializeAsync(Of ChangeNotificationCollection)(Request.Body, options)
notificationCollection.Value _
.Where(Function(x) x.EncryptedContent Is Nothing) _
.ForEach(Sub(notification)
Dim subscription = Stores.TeamsPresenceSubscriptionStore.Instance.GetValueOrDefault(notification.SubscriptionId.Value)
' Verify the current client state matches the one that was sent.
If subscription Is Nothing OrElse notification.ClientState <> subscription.SecretClientState Then
Log.msg(Category.TEAMS, "Error: Failed to verify notification")
Return
End If
' Just keep the latest notification for each resource. No point pulling data more than once.
plainNotifications(notification.Resource) = notification
End Sub)
If plainNotifications.Count > 0 Then
' Query for the changed messages
GetChangedMessages(plainNotifications.Values)
End If
If notificationCollection.ValidationTokens IsNot Nothing AndAlso notificationCollection.ValidationTokens.Any() Then
' -> notificationCollection.ValidationTokens is not set at the customer
End If
Catch ex As Exception
' Still return a 202 so the service doesn't resend the notification.
End Try
Return Accepted()
End Function
创建订阅的代码是
Subscription = graphApi.Client.Subscriptions.Request().AddAsync(New Subscription() With
{
.Resource = $"/communications/presences?$filter=id in ({String.Join(",", userIds.Select(Function(id) $"'{id}'"))})",
.ChangeType = "updated",
.NotificationUrl = $"{publicNotificationEndpoint}/Notification/{connectorid}/{Me.GraphApi.Link.Id}",
.LifecycleNotificationUrl = $"{publicNotificationEndpoint}/LifecycleNotification/{connectorid}/{Me.GraphApi.Link.Id}",
.ClientState = SecretClientState,
.ExpirationDateTime = DateTime.UtcNow.Add(MAX_SUBSCRIPTION_LIFETIME),
.EncryptionCertificate = Convert.ToBase64String(encryptionCertificate.Export(X509ContentType.Cert)),
.EncryptionCertificateId = encryptionCertificate.Version.ToString(),
.IncludeResourceData = True
}).Result
【问题讨论】:
-
只是为了隔离问题,您可以尝试使用 Graph Explorer 或 POSTMAN 重现问题,看看您是否看到不同的结果。
-
嗨@hypetsch,你能分享一下你用来获取validationTokens的代码吗?
-
@Jagadeesh-MSFT - 根据要求,我添加了上面的(简化)代码,但如前所述,validationTokens 不是我们在通知中收到的 JSON 内容的一部分。我在问题中添加的 JSON 实际上是通过 Wireshark 捕获的格式化/缩短的内容。无论如何,我标记了未设置 .validationTokens 属性的位置。
-
嗨@hypetsch,你还面临这个问题吗
-
我们同时实施了一种解决方法(并忽略验证令牌)。我会要求我们的服务部门联系客户,询问他们是否可以尝试。
标签: microsoft-graph-api microsoft-teams microsoft-graph-teams