Webhooks do Centro de Parceiros
Aplica-se a: Partner Center | Partner Center operado pela 21Vianet | Partner Center for Microsoft Cloud for US Government
Funções apropriadas: Administrador global | Administrador de Faturação | Agente administrativo | Agente de vendas | Agente do serviço de assistência
As APIs Webhook do Partner Center permitem que os parceiros se registrem para eventos de alteração de recursos. Esses eventos são entregues na forma de HTTP POSTs para a URL registrada do parceiro. Para receber um evento do Partner Center, os parceiros hospedam um retorno de chamada onde o Partner Center pode POSTAR o evento de alteração de recurso. O evento é assinado digitalmente para que o parceiro possa verificar se foi enviado do Partner Center. As notificações Webhook são acionadas apenas para o ambiente que tem a configuração mais recente para Co-sell.
O Partner Center suporta os seguintes eventos Webhook.
Azure Fraud Event Detected ("azure-fraud-event-detected")
Esse evento é gerado quando o evento de fraude do Azure é detetado.
Evento Aprovado de Relacionamento de Administrador Delegado ("dap-admin-relationship-approved")
Esse evento é gerado quando os Privilégios de Administrador Delegado são aprovados pelo locatário do cliente.
Evento de Relacionamento de Revendedor Aceito pelo Cliente ("Relacionamento de Revendedor-Aceito pelo Cliente")
Esse evento é gerado quando o locatário do cliente aprova o Relacionamento de Revendedor.
Relação de Revendedor Indireto Aceite pelo Cliente Evento ("indirect-reseller-relationship-accepted-by-customer")
Esse evento é gerado quando o locatário do cliente aprova o Relacionamento de Revendedor Indireto.
Evento Encerrado de Relacionamento de Administrador Delegado ("dap-admin-relationship-terminated")
Esse evento é gerado quando o cliente encerra os Privilégios de Administrador Delegado.
Relação de administrador do Dap encerrada por evento da Microsoft ("dap-admin-relationship-terminated-by-microsoft")
Esse evento é gerado quando a Microsoft encerra o DAP entre o locatário do Parceiro e do Cliente quando o DAP está inativo por mais de 90 dias.
Evento Granular Admin Access Assignment Activated ("granular-admin-access-assignment-activated")
Esse evento é gerado quando o parceiro ativa a atribuição de acesso de Privilégios de Administrador Delegado Granular depois que as funções do Microsoft Entra são atribuídas a grupos de segurança específicos.
Evento criado da atribuição de acesso de administrador granular ("granular-admin-access-assignment-created")
Esse evento é gerado quando o parceiro cria a atribuição de acesso Granular Delegated Admin Privileges. Os parceiros podem atribuir funções do Microsoft Entra aprovadas pelo cliente a grupos de segurança específicos.
Evento Granular Admin Access Assignment Deleted ("granular-admin-access-assignment-deleted")
Esse evento é gerado quando o parceiro exclui a atribuição de acesso de Privilégios de Administrador Delegado Granular.
Evento Granular Admin Access Assignment Updated ("granular-admin-access-assignment-updated")
Esse evento é gerado quando o parceiro atualiza a atribuição de acesso de privilégios de administrador delegado granular.
Evento Granular Admin Relationship Activated ("granular-admin-relationship-activated")
Esse evento é gerado quando os Privilégios de Administrador Delegado Granular são criados e ativos para o cliente aprovar.
Evento Granular Admin Relationship Approved ("granular-admin-relationship-approved")
Esse evento é gerado quando o locatário do cliente aprova os Privilégios de Administrador Delegado Granular.
Evento Granular Admin Relationship Expired ("granular-admin-relationship-expired")
Esse evento é gerado quando os Privilégios de Administrador Delegado Granular expiram.
Evento criado de relação de administrador granular ("granular-admin-relationship-created")
Esse evento é gerado quando os Privilégios de Administrador Delegado Granular são criados.
Evento Granular Admin Relationship Updated ("granular-admin-relationship-updated")
Esse evento é gerado quando o cliente ou parceiro atualiza os Privilégios de Administrador Delegado Granular.
Evento Estendido Automático de Relacionamento de Administrador Granular ("granular-admin-relationship-auto-extended")
Esse evento é gerado quando o sistema estende automaticamente os privilégios de administrador delegado granular.
Evento Granular Admin Relationship Terminated ("granular-admin-relationship-terminated")
Esse evento é gerado quando o parceiro ou locatário do cliente encerra os Privilégios de Administrador Delegado Granular.
Evento Invoice Ready ("pronto para fatura")
Esse evento é gerado quando a nova fatura está pronta.
Nova migração de comércio concluída ("new-commerce-migration-completed")
Esse evento é gerado quando a nova migração de comércio é concluída.
Nova migração comercial criada ("new-commerce-migration-created")
Esse evento é gerado quando a nova migração de comércio é criada.
Falha na migração de novo comércio ("new-commerce-migration-failed")
Esse evento é gerado quando a nova migração de comércio é falhada.
Criar transferência ("create-transfer")
Esse evento é gerado quando a transferência é criada.
Transferência de atualização ("update-transfer")
Este evento é gerado quando a transferência é atualizada.
Transferência Completa ("transferência completa")
Este evento é gerado quando a transferência é concluída.
Falha na transferência ("fail-transfer")
Esse evento é gerado quando a transferência falha.
Falha no novo cronograma de migração do Commerce ("new-commerce-migration-schedule-failed")
Esse evento é gerado quando o novo cronograma de migração de comércio é falhado.
Evento criado por referência ("referral-created")
Esse evento é gerado quando a referência é criada.
Evento de referência atualizada ("referral-updated")
Este evento é gerado quando a referência é atualizada.
Evento criado por referência relacionada ("related-referral-created")
Esse evento é gerado quando a referência relacionada é criada.
Evento de Referência Relacionada Atualizado ("related-referral-updated")
Este evento é gerado quando a referência relacionada é atualizada.
Evento de Subscrição Atualizada ("subscrição atualizada")
Esse evento é gerado quando a assinatura é alterada. Esses eventos são gerados quando há uma alteração interna, além de quando as alterações são feitas por meio da API do Partner Center.
Nota
Há um atraso de até 48 horas entre o momento em que uma assinatura é alterada e quando o evento Assinatura atualizada é acionado.
Evento de teste ("test-created")
Este evento permite-lhe auto-integrar e testar o seu registo, solicitando um evento de teste e, em seguida, acompanhando o seu progresso. Você pode ver as mensagens de falha que estão sendo recebidas da Microsoft ao tentar entregar o evento. Esta restrição aplica-se apenas a eventos "criados por teste". Os dados com mais de sete dias são limpos.
Evento Threshold Exceeded ("usagerecords-thresholdExceeded")
Esse evento é gerado quando a quantidade de uso do Microsoft Azure para qualquer cliente excede seu orçamento de gastos de uso (seu limite). Para obter mais informações, consulte (Definir um orçamento de gastos do Azure para seus clientes/partner-center/set-an-azure-spending-budget-for-your-customers).
Eventos futuros do Webhook serão adicionados para recursos que mudam no sistema que o parceiro não está no controle, e novas atualizações serão feitas para que esses eventos sejam o mais próximos possível do "tempo real". O feedback dos Parceiros sobre quais eventos agregam valor aos seus negócios é útil para determinar quais novos eventos devem ser adicionados.
Para obter uma lista completa dos eventos Webhook suportados pelo Partner Center, consulte Eventos Webhook do Partner Center.
Pré-requisitos
- Credenciais conforme descrito na autenticação do Partner Center. Este cenário oferece suporte à autenticação com credenciais autônomas de Aplicativo e Aplicativo+Usuário.
Receber eventos do Partner Center
Para receber eventos do Partner Center, você deve expor um ponto de extremidade acessível publicamente. Como esse ponto de extremidade está exposto, você deve validar se a comunicação é do Partner Center. Todos os eventos Webhook que você recebe são assinados digitalmente com um certificado que é encadeado para a raiz da Microsoft. Um link para o certificado usado para assinar o evento também é fornecido. Isso permite que o certificado seja renovado sem que você precise reimplantar ou reconfigurar seu serviço. O Partner Center faz 10 tentativas para realizar o evento. Se o evento ainda não for entregue após 10 tentativas, ele será movido para uma fila offline e nenhuma outra tentativa será feita na entrega.
O exemplo a seguir mostra um evento publicado do Partner Center.
POST /webhooks/callback
Content-Type: application/json
Authorization: Signature VOhcjRqA4f7u/4R29ohEzwRZibZdzfgG5/w4fHUnu8FHauBEVch8m2+5OgjLZRL33CIQpmqr2t0FsGF0UdmCR2OdY7rrAh/6QUW+u+jRUCV1s62M76jbVpTTGShmrANxnl8gz4LsbY260LAsDHufd6ab4oejerx1Ey9sFC+xwVTa+J4qGgeyIepeu4YCM0oB2RFS9rRB2F1s1OeAAPEhG7olp8B00Jss3PQrpLGOoAr5+fnQp8GOK8IdKF1/abUIyyvHxEjL76l7DVQN58pIJg4YC+pLs8pi6sTKvOdSVyCnjf+uYQWwmmWujSHfyU37j2Fzz16PJyWH41K8ZXJJkw==
X-MS-Certificate-Url: https://3psostorageacct.blob.core.windows.net/cert/pcnotifications-dispatch.microsoft.com.cer
X-MS-Signature-Algorithm: rsa-sha256
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Content-Length: 195
{
"EventName": "test-created",
"ResourceUri": "http://localhost:16722/v1/webhooks/registration/test",
"ResourceName": "test",
"AuditUri": null,
"ResourceChangeUtcDate": "2017-11-16T16:19:06.3520276+00:00"
}
Nota
O cabeçalho Autorização tem um esquema de "Assinatura". Esta é uma assinatura codificada base64 do conteúdo.
Como autenticar o retorno de chamada
Para autenticar o evento de retorno de chamada recebido do Partner Center, siga estas etapas:
Verifique se os cabeçalhos necessários estão presentes (Authorization, x-ms-certificate-url, x-ms-signature-algorithm).
Baixe o certificado usado para assinar o conteúdo (x-ms-certificate-url).
Verifique a cadeia de certificados.
Verifique a "Organização" do certificado.
Leia o conteúdo com codificação UTF8 em um buffer.
Crie um provedor de criptografia RSA.
Verifique se os dados correspondem ao que foi assinado com o algoritmo de hash especificado (por exemplo, SHA256).
Se a verificação for bem-sucedida, processe a mensagem.
Nota
Por padrão, o token de assinatura é enviado em um cabeçalho de Autorização. Se você definir SignatureTokenToMsSignatureHeader como true em seu registro, o token de assinatura será enviado no cabeçalho x-ms-signature.
Modelo de evento
A tabela a seguir descreve as propriedades de um evento do Partner Center.
Propriedades
Nome | Descrição |
---|---|
Nome do Evento | O nome do evento. No formato {resource}-{action}. Por exemplo, "test-created". |
ResourceUri | O URI do recurso que foi alterado. |
ResourceName | O nome do recurso que foi alterado. |
AuditUrl | Opcional. O URI do registro de auditoria. |
ResourceChangeUtcDate | A data e a hora, no formato UTC, em que ocorreu a alteração do recurso. |
Exemplo
O exemplo a seguir mostra a estrutura de um evento do Partner Center.
{
"EventName": "test-created",
"ResourceUri": "http://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents/c0bfd694-3075-4ec5-9a3c-733d3a890a1f",
"ResourceName": "test",
"AuditUri": null,
"ResourceChangeUtcDate": "2017-11-16T16:19:06.3520276+00:00"
}
Webhook APIs
Autenticação
Todas as chamadas para as APIs Webhook são autenticadas usando o token Bearer no cabeçalho de autorização. Adquira um token de acesso para acessar https://api.partnercenter.microsoft.com
o . Esse token é o mesmo usado para acessar o restante das APIs do Partner Center.
Obter uma lista de eventos
Retorna uma lista dos eventos atualmente suportados pelas APIs Webhook.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration/events
Exemplo de solicitação
GET /webhooks/v1/registration/events
content-type: application/json
authorization: Bearer eyJ0e.......
accept: */*
host: api.partnercenter.microsoft.com
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 183
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: c0bcf3a3-46e9-48fd-8e05-f674b8fd5d66
MS-RequestId: 79419bbb-06ee-48da-8221-e09480537dfc
X-Locale: en-US
[ "subscription-updated", "test-created", "usagerecords-thresholdExceeded" ]
Registe-se para receber eventos
Registra um locatário para receber os eventos especificados.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration
Exemplo de solicitação
POST /webhooks/v1/registration
Content-Type: application/json
Authorization: Bearer eyJ0e.....
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Content-Length: 219
{
"WebhookUrl": "{{YourCallbackUrl}}",
"WebhookEvents": ["subscription-updated", "test-created"]
}
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 346
Content-Type: application/json; charset=utf-8
content-encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: 718f2336-8b56-4f42-93ac-54896047c59a
MS-RequestId: f04b1b5e-87b4-4d95-b087-d65fffec0bd2
{
"SubscriberId": "e82cac64-dc67-4cd3-849b-78b6127dd57d",
"WebhookUrl": "{{YourCallbackUrl}}",
"WebhookEvents": [ "subscription-updated", "test-created" ]
}
Ver um registo
Retorna o registro de evento Webhooks para um locatário.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration
Exemplo de solicitação
GET /webhooks/v1/registration
Content-Type: application/json
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 341
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: c3b88ab0-b7bc-48d6-8c55-4ae6200f490a
MS-RequestId: ca30367d-4b24-4516-af08-74bba6dc6657
X-Locale: en-US
{
"WebhookUrl": "{{YourCallbackUrl}}",
"WebhookEvents": ["subscription-updated", "test-created"]
}
Atualizar um registro de evento
Atualiza um registro de evento existente.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration
Exemplo de solicitação
PUT /webhooks/v1/registration
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOR...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Content-Length: 258
{
"WebhookUrl": "{{YourCallbackUrl}}",
"WebhookEvents": ["subscription-updated", "test-created"]
}
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 346
Content-Type: application/json; charset=utf-8
content-encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: 718f2336-8b56-4f42-93ac-54896047c59a
MS-RequestId: f04b1b5e-87b4-4d95-b087-d65fffec0bd2
{
"SubscriberId": "e82cac64-dc67-4cd3-849b-78b6127dd57d",
"WebhookUrl": "{{YourCallbackUrl}}",
"WebhookEvents": [ "subscription-updated", "test-created" ]
}
Envie um evento de teste para validar o seu registo
Gera um evento de teste para validar o registro do Webhooks. Este teste destina-se a validar que você pode receber eventos do Partner Center. Os dados desses eventos são excluídos sete dias após a criação do evento inicial. Você deve estar registrado para o evento "test-created", usando a API de registro, antes de enviar um evento de validação.
Nota
Há um limite de aceleração de 2 solicitações por minuto ao postar um evento de validação.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents
Exemplo de solicitação
POST /webhooks/v1/registration/validationEvents
MS-CorrelationId: 3ef0202b-9d00-4f75-9cff-15420f7612b3
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Content-Length:
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 181
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: 04af2aea-d413-42db-824e-f328001484d1
MS-RequestId: 2f498d5a-a6ab-468f-98d8-93c96da09051
X-Locale: en-US
{ "correlationId": "04af2aea-d413-42db-824e-f328001484d1" }
Verificar se o evento foi entregue
Retorna o estado atual do evento de validação. Essa verificação pode ser útil para solucionar problemas de entrega de eventos. A Resposta contém um resultado para cada tentativa feita para entregar o evento.
URL do Recurso
https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents/{correlationId}
Exemplo de solicitação
GET /webhooks/v1/registration/validationEvents/04af2aea-d413-42db-824e-f328001484d1
MS-CorrelationId: 3ef0202b-9d00-4f75-9cff-15420f7612b3
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Exemplo de resposta
HTTP/1.1 200
Status: 200
Content-Length: 469
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: 497e0a23-9498-4d6c-bd6a-bc4d6d0054e7
MS-RequestId: 0843bdb2-113a-4926-a51c-284aa01d722e
X-Locale: en-US
{
"correlationId": "04af2aea-d413-42db-824e-f328001484d1",
"partnerId": "00234d9d-8c2d-4ff5-8c18-39f8afc6f7f3",
"status": "completed",
"callbackUrl": "{{YourCallbackUrl}}",
"results": [{
"responseCode": "OK",
"responseMessage": "",
"systemError": false,
"dateTimeUtc": "2017-12-08T21:39:48.2386997"
}]
}
Exemplo de validação de assinatura
Exemplo de assinatura do controlador de retorno de chamada (ASP.NET)
[AuthorizeSignature]
[Route("webhooks/callback")]
public IHttpActionResult Post(PartnerResourceChangeCallBack callback)
Validação de assinatura
O exemplo a seguir mostra como adicionar um atributo de autorização ao controlador que está recebendo retornos de chamada de eventos Webhook.
namespace Webhooks.Security
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using Microsoft.Partner.Logging;
/// <summary>
/// Signature based Authorization
/// </summary>
public class AuthorizeSignatureAttribute : AuthorizeAttribute
{
private const string MsSignatureHeader = "x-ms-signature";
private const string CertificateUrlHeader = "x-ms-certificate-url";
private const string SignatureAlgorithmHeader = "x-ms-signature-algorithm";
private const string MicrosoftCorporationIssuer = "O=Microsoft Corporation";
private const string SignatureScheme = "Signature";
/// <inheritdoc/>
public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
ValidateAuthorizationHeaders(actionContext.Request);
await VerifySignature(actionContext.Request);
}
private static async Task<string> GetContentAsync(HttpRequestMessage request)
{
// By default the stream can only be read once and we need to read it here so that we can hash the body to validate the signature from microsoft.
// Load into a buffer, so that the stream can be accessed here and in the api when it binds the content to the expected model type.
await request.Content.LoadIntoBufferAsync();
var s = await request.Content.ReadAsStreamAsync();
var reader = new StreamReader(s);
var body = await reader.ReadToEndAsync();
// set the stream position back to the beginning
if (s.CanSeek)
{
s.Seek(0, SeekOrigin.Begin);
}
return body;
}
private static void ValidateAuthorizationHeaders(HttpRequestMessage request)
{
var authHeader = request.Headers.Authorization;
if (string.IsNullOrWhiteSpace(authHeader?.Parameter) && string.IsNullOrWhiteSpace(GetHeaderValue(request.Headers, MsSignatureHeader)))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Authorization header missing."));
}
var signatureHeaderValue = GetHeaderValue(request.Headers, MsSignatureHeader);
if (authHeader != null
&& !string.Equals(authHeader.Scheme, SignatureScheme, StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(signatureHeaderValue)
&& !signatureHeaderValue.StartsWith(SignatureScheme, StringComparison.OrdinalIgnoreCase))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.Unauthorized, $"Authorization scheme needs to be '{SignatureScheme}'."));
}
if (string.IsNullOrWhiteSpace(GetHeaderValue(request.Headers, CertificateUrlHeader)))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest, $"Request header {CertificateUrlHeader} missing."));
}
if (string.IsNullOrWhiteSpace(GetHeaderValue(request.Headers, SignatureAlgorithmHeader)))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest, $"Request header {SignatureAlgorithmHeader} missing."));
}
}
private static string GetHeaderValue(HttpHeaders headers, string key)
{
headers.TryGetValues(key, out var headerValues);
return headerValues?.FirstOrDefault();
}
private static async Task VerifySignature(HttpRequestMessage request)
{
// Get signature value from either authorization header or x-ms-signature header.
var base64Signature = request.Headers.Authorization?.Parameter ?? GetHeaderValue(request.Headers, MsSignatureHeader).Split(' ')[1];
var signatureAlgorithm = GetHeaderValue(request.Headers, SignatureAlgorithmHeader);
var certificateUrl = GetHeaderValue(request.Headers, CertificateUrlHeader);
var certificate = await GetCertificate(certificateUrl);
var content = await GetContentAsync(request);
var alg = signatureAlgorithm.Split('-'); // for example RSA-SHA1
var isValid = false;
var logger = GetLoggerIfAvailable(request);
// Validate the certificate
VerifyCertificate(certificate, request, logger);
if (alg.Length == 2 && alg[0].Equals("RSA", StringComparison.OrdinalIgnoreCase))
{
var signature = Convert.FromBase64String(base64Signature);
var csp = (RSACryptoServiceProvider)certificate.PublicKey.Key;
var encoding = new UTF8Encoding();
var data = encoding.GetBytes(content);
var hashAlgorithm = alg[1].ToUpper();
isValid = csp.VerifyData(data, CryptoConfig.MapNameToOID(hashAlgorithm), signature);
}
if (!isValid)
{
// log that we were not able to validate the signature
logger?.TrackTrace(
"Failed to validate signature for webhook callback",
new Dictionary<string, string> { { "base64Signature", base64Signature }, { "certificateUrl", certificateUrl }, { "signatureAlgorithm", signatureAlgorithm }, { "content", content } });
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Signature verification failed"));
}
}
private static ILogger GetLoggerIfAvailable(HttpRequestMessage request)
{
return request.GetDependencyScope().GetService(typeof(ILogger)) as ILogger;
}
private static async Task<X509Certificate2> GetCertificate(string certificateUrl)
{
byte[] certBytes;
using (var webClient = new WebClient())
{
certBytes = await webClient.DownloadDataTaskAsync(certificateUrl);
}
return new X509Certificate2(certBytes);
}
private static void VerifyCertificate(X509Certificate2 certificate, HttpRequestMessage request, ILogger logger)
{
if (!certificate.Verify())
{
logger?.TrackTrace("Failed to verify certificate for webhook callback.", new Dictionary<string, string> { { "Subject", certificate.Subject }, { "Issuer", certificate.Issuer } });
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Certificate verification failed."));
}
if (!certificate.Issuer.Contains(MicrosoftCorporationIssuer))
{
logger?.TrackTrace($"Certificate not issued by {MicrosoftCorporationIssuer}.", new Dictionary<string, string> { { "Issuer", certificate.Issuer } });
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.Unauthorized, $"Certificate not issued by {MicrosoftCorporationIssuer}."));
}
}
}
}