Sdílet prostřednictvím


Webhooky Partnerského centra

Platí pro: Partnerské centrum | Partnerské centrum provozované společností 21Vianet | Partnerské centrum pro Microsoft Cloud pro státní správu USA

příslušné role: Správce fakturace | Agent pro správu | Obchodní agent | Agent helpdesku

Rozhraní API Webhooku Partnerského centra umožňují partnerům registrovat události změn prostředků. Tyto události se doručují ve formě HTTP POST na zaregistrovanou adresu URL partnera. Pokud chtějí partneři přijímat oznámení o změně prostředků z Partnerského centra, musí hostovat zpětné volání, kam může Partnerské centrum zaslat (POST) událost změny prostředků. Událost je digitálně podepsaná, aby partner mohl ověřit, že byla odeslána z Partnerského centra. Oznámení webhooku se aktivují jenom v prostředí, které má nejnovější konfiguraci pro spoluprodej.

Partnerské centrum podporuje následující události Webhooku.

  • Zjištěná událost podvodu Azure ("azure-fraud-event-detected")

    Tato událost se vyvolá při zjištění události podvodu Azure.

  • Událost schválená delegovaným správcem (dap-admin-relationship-approved)

    Tato událost se vyvolá, když jsou delegovaná oprávnění správce schválena tenantem zákazníka.

  • Přijetí vztahu s prodejcem zákazníkem (reseller-relationship-accepted-by-customer)

    Tato událost nastane, když nájemce zákazníka schválí vztah s prodejcem.

  • Vztah nepřímého prodejce přijatý zákazníkem ("indirect-reseller-relationship-accepted-by-customer")

    Tato událost se vyvolá, když zákaznický tenant schválí vztah nepřímého prodejce.

  • Událost ukončení vztahu delegovaného správce ("dap-admin-relationship-terminated")

    Tato událost se vyvolá, když zákazník ukončí oprávnění delegovaného správce.

  • Vztah správce dap ukončený událostí Microsoftu (dap-admin-relationship-terminated-by-microsoft)

    Tato událost se vyvolá, když Microsoft ukončí DAP mezi klientem partnera a zákazníka, když je DAP neaktivní po dobu delší než 90 dnů.

  • Událost aktivace podrobného přiřazení přístupu správce ("granular-admin-access-assignment-activated")

    Tato událost se vyvolá, když partner aktivuje přístup k podrobně delegovaných administrátorských oprávněním poté, co jsou role Microsoft Entra přiřazeny ke konkrétním skupinám zabezpečení.

  • Událost vytvoření přiřazení granulárního přístupu správce ("granular-admin-access-assignment-created")

    Tato událost se vyvolá, když partner vytvoří podrobné přiřazení přístupu k oprávněním delegovaného správce. Partneři můžou přiřadit role Microsoft Entra schválené zákazníkem ke konkrétním skupinám zabezpečení.

  • Událost odstranění přiřazení granulárního přístupu správce ("granular-admin-access-assignment-deleted")

    Tato událost se vyvolá, když partner odstraní přiřazení přístupu k podrobným delegovaným oprávněním správce.

  • Událost aktualizace podrobného přiřazení přístupu správce ("granular-admin-access-assignment-updated")

    Tato událost se vyvolá, když partner aktualizuje přiřazení přístupu ke granulárním delegovaným oprávněním správce.

  • Událost aktivace granulárního vztahu správce ("granular-admin-relationship-activated")

    Tato událost se vyvolá, když se vytvoří a aktivuje Granulární delegovaná oprávnění správce pro schválení zákazníkem.

  • Událost schválení vztahu granulárního správce (granular-admin-relationship-approved)

    Tato událost se vyvolá, když zákaznický tenant schválí podrobná delegovaná oprávnění správce.

  • Událost vypršení vztahu detailního administrátora ("granular-admin-relationship-expired")

    Tato událost se vyvolá, když vypršela platnost podrobných delegovaných oprávnění správce.

  • Událost vytvoření detailního vztahu správce ("granular-admin-relationship-created")

    Tato událost se vyvolá při vytvoření podrobných oprávnění delegovaného správce.

  • Událost aktualizace granulárního vztahu správce ("granular-admin-relationship-updated")

    Tato událost se vyvolá, když zákazník nebo partner změní nebo aktualizuje granulární delegovaná správcovská oprávnění.

  • Jemná událost automatického rozšíření vztahu správce (granular-admin-relationship-auto-extended)

    Tato událost nastane, když systém automaticky rozšíří granulární delegovaná oprávnění správce.

  • Granulární událost ukončení vztahu správce ("granular-admin-relationship-terminated")

    Tato událost se vyvolá, když partner nebo tenant zákazníka ukončí Granular Delegované Správcovské Oprávnění.

  • Migrace nového obchodu dokončena (new-commerce-migration-completed)

    Tato událost se vyvolá po dokončení migrace nového komerčního systému.

  • Vytvořená nová migrace commerce (new-commerce-migration-created)

    Tato událost se vyvolá při vytvoření nové komerční migrace.

  • Nová migrace commerce selhala (new-commerce-migration-failed)

    Tato událost se vyvolá, když dojde k selhání migrace nového obchodního modelu.

  • Vytvoření převodu ("create-transfer")

    Tato událost se spustí při vytvoření přenosu.

  • Update Transfer ("update-transfer")

    Tato událost se vyvolá při aktualizaci přenosu.

  • Dokončení převodu ("complete-transfer")

    Tato událost se vyvolá, když je přenos dokončen.

  • uplynutí platnosti převodu ("uplynutí převodu")

    Tato událost nastane při vypršení platnosti přenosu.

  • Neúspěšný přenos ("fail-transfer")

    Tato událost se vyvolá, když se přenos nezdaří.

  • Nový plán migrace obchodování selhal (new-commerce-migration-schedule-failed)

    Tato událost se vyvolá, když se nezdaří nový plán migrace komerčního obchodu.

  • Událost vytvoření referenčního seznamu (vytvoření referenčního seznamu)

    Tato událost se spustí při vytvoření doporučení.

  • Aktualizovaná událost referenčního seznamu ("referenční seznam aktualizován")

    Tato událost se vyvolá při aktualizaci referenčního seznamu.

  • Událost vytvoření souvisejícího doporučení ("related-referral-created")

    Tato událost se vyvolá při vytvoření souvisejícího referenčního seznamu.

  • Související událost aktualizace referenčního seznamu ("související-referenční seznam-aktualizován")

    Tato událost je vyvolána, když je aktualizována související reference.

  • Aktivní událost odběru ("subscription-active")

    Tato událost se vyvolá při aktivaci předplatného.

    Poznámka:

    Webhook aktivního odběru a odpovídající událost protokolu aktivit jsou v tuto chvíli k dispozici pouze pro tenanty sandboxu.

  • Událost čekající na odběr (čeká na předplatné)

    Tato událost se vyvolá, když je odpovídající objednávka úspěšně přijata a vytvoření odběru čeká na vyřízení.

    Poznámka:

    Webhook čekající na odběr a odpovídající událost protokolu aktivit jsou v tuto chvíli k dispozici pouze pro tenanty sandboxu.

  • Událost obnovení předplatného ("obnovené předplatné")

    Tato událost se vyvolá, když se předplatné dokončí prodlužování platnosti.

    Poznámka:

    Webhook pro prodloužení předplatného a odpovídající událost protokolu aktivit jsou v tuto chvíli k dispozici pouze pro tenanty sandboxu.

  • Událost aktualizace odběru (aktualizace předplatného)

    Tato událost se vyvolá při změně odběru. Tyto události se generují, když dojde k interní změně kromě toho, kdy se změny provádějí prostřednictvím rozhraní API Partnerského centra.

    Poznámka:

    Mezi časem, kdy se předplatné změní, a aktivací události Aktualizace odběru je zpoždění až 48 hodin.

  • Testovací událost (vytvoření testu)

    Tato událost vám umožní připojit se k registraci a otestovat registraci tak, že požádáte o testovací událost a pak budete sledovat její průběh. Při pokusu o doručení události se zobrazí zprávy o selhání, které přijímá Microsoft. Toto omezení platí jenom pro události vytvořené testem. Data starší než sedm dnů se vymažou.

  • Událost překročení prahové hodnoty ("usagerecords-thresholdExceeded")

    Tato událost se vyvolá, když množství využití Microsoft Azure pro každého zákazníka překročí jejich rozpočet na využití (prahovou hodnotu). Další informace najdete v tématu (Nastavení rozpočtu útraty Azure pro zákazníky). Pro podrobnosti navštivte partnerské centrum: /set-an-azure-spending-budget-for-your-customers.

Budoucí události webhooku budou přidány pro prostředky měnící se v systému, který partner nemá pod kontrolou, a budou provedeny další aktualizace, aby se tyto události co nejvíce blížily "skutečnému času". Zpětná vazba od partnerů, která udává, jaké události přinášejí hodnotu jejich podnikání, je užitečná při určování, jaké nové akce přidat.

Úplný seznam událostí webhooku podporovaných Partnerským centrem najdete v tématu Události webhooku Partnerského centra.

Požadavky

  • Přihlašovací údaje popsané v ověřování v Partnerském centru Tento scénář podporuje ověřování pomocí samostatných přihlašovacích údajů aplikace i aplikace a uživatele.

Příjem událostí z Partnerského centra

Pokud chcete přijímat události z Partnerského centra, musíte zveřejnit veřejně přístupný koncový bod. Vzhledem k tomu, že je tento koncový bod vystavený, musíte ověřit, že komunikace pochází z Partnerského centra. Všechny události Webhooku, které obdržíte, jsou digitálně podepsané certifikátem, který je řetězový k Microsoft Root. Je také k dispozici odkaz na certifikát použitý k podepsání události. To umožňuje obnovení certifikátu, aniž byste museli znovu nasadit nebo překonfigurovat službu. Partnerské centrum se pokusí doručit událost 10krát. Pokud se událost po 10 pokusech stále nedoručí, přesune se do offline fronty a neprovádí se žádné další pokusy o doručení.

Následující ukázka ukazuje událost publikované z Partnerského centra.

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"
}

Poznámka:

Autorizační hlavička má schéma "Podpis". Toto je podpis obsahu kódovaný v base64.

Jak ověřit zpětné volání

Pokud chcete ověřit událost zpětného volání přijatou z Partnerského centra, postupujte takto:

  1. Ověřte, že jsou k dispozici požadovaná záhlaví (autorizace, x-ms-certificate-url, x-ms-signature-algorithm).
  2. Stáhněte si certifikát použitý k podepsání obsahu (x-ms-certificate-url).
  3. Ověřte řetěz certifikátů.
  4. Ověřte "Organizaci" certifikátu.
  5. Načtěte obsah s kódováním UTF8 do vyrovnávací paměti.
  6. Vytvořte poskytovatele kryptografických služeb RSA.
  7. Ověřte, že data odpovídají tomu, co bylo podepsáno zadaným algoritmem hash (například SHA256).
  8. Pokud ověření proběhne úspěšně, zpracujte zprávu.

Poznámka:

Ve výchozím nastavení se token podpisu odešle v autorizační hlavičce. Pokud v registraci nastavíte SignatureTokenToMsSignatureHeader hodnotu true, token podpisu se místo toho odešle do hlavičky x-ms-signature.

Model událostí

Následující tabulka popisuje vlastnosti události Partnerského centra.

Vlastnosti

Název Popis
EventName Název události. Ve tvaru {resource}-{action}. Například "test-created".
ResourceUri Identifikátor URI prostředku, který se změnil.
ResourceName Název prostředku, který se změnil.
AuditUrl Nepovinné. URI záznamu auditu.
ResourceChangeUtcDate Datum a čas ve formátu UTC, kdy došlo ke změně prostředku.

Vzorek

Následující ukázka ukazuje strukturu události Partnerského centra.

{
    "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"
}

Rozhraní API webhooku

Ověřování

Všechna volání rozhraní API webhooku se ověřují pomocí bearer tokenu v hlavičce autorizace. Získejte přístupový token pro přístup https://api.partnercenter.microsoft.com. Tento token je stejný token, který se používá pro přístup ke zbytku rozhraní API Partnerského centra.

Získání seznamu událostí

Vrátí seznam událostí aktuálně podporovaných rozhraními API Webhooku.

Adresa URL prostředku

https://api.partnercenter.microsoft.com/webhooks/v1/registration/events

Ukázka žádosti

GET /webhooks/v1/registration/events
content-type: application/json
authorization: Bearer eyJ0e.......
accept: */*
host: api.partnercenter.microsoft.com

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 183
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: aaaa0000-bb11-2222-33cc-444444dddddd
MS-RequestId: 79419bbb-06ee-48da-8221-e09480537dfc
X-Locale: en-US

[ "subscription-updated", "test-created", "usagerecords-thresholdExceeded" ]

Registrace k příjmu událostí

Zaregistruje tenanta pro příjem zadaných událostí.

Adresa URL zdroje

https://api.partnercenter.microsoft.com/webhooks/v1/registration

Příklad požadavku

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"]
}

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 346
Content-Type: application/json; charset=utf-8
content-encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: bbbb1111-cc22-3333-44dd-555555eeeeee
MS-RequestId: f04b1b5e-87b4-4d95-b087-d65fffec0bd2

{
    "SubscriberId": "e82cac64-dc67-4cd3-849b-78b6127dd57d",
    "WebhookUrl": "{{YourCallbackUrl}}",
    "WebhookEvents": [ "subscription-updated", "test-created" ]
}

Zobrazení registrace

Vrátí registraci události Webhooky pro tenanta.

Adresa URL prostředku

https://api.partnercenter.microsoft.com/webhooks/v1/registration

Příklad požadavku

GET /webhooks/v1/registration
Content-Type: application/json
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 341
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: cccc2222-dd33-4444-55ee-666666ffffff
MS-RequestId: ca30367d-4b24-4516-af08-74bba6dc6657
X-Locale: en-US

{
    "WebhookUrl": "{{YourCallbackUrl}}",
    "WebhookEvents": ["subscription-updated", "test-created"]
}

Aktualizace registrace události

Aktualizuje existující registraci události.

Adresa URL prostředku

https://api.partnercenter.microsoft.com/webhooks/v1/registration

Příklad požadavku

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"]
}

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 346
Content-Type: application/json; charset=utf-8
content-encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: bbbb1111-cc22-3333-44dd-555555eeeeee
MS-RequestId: f04b1b5e-87b4-4d95-b087-d65fffec0bd2

{
    "SubscriberId": "e82cac64-dc67-4cd3-849b-78b6127dd57d",
    "WebhookUrl": "{{YourCallbackUrl}}",
    "WebhookEvents": [ "subscription-updated", "test-created" ]
}

Odeslání testovací události pro ověření registrace

Vygeneruje testovací událost pro ověření registrace webhooků. Cílem tohoto testu je ověřit, že můžete přijímat události z Partnerského centra. Data pro tyto události se odstraní sedm dní po vytvoření počáteční události. Před odesláním ověřovací události musíte být zaregistrovaní pro událost "vytvoření testu" pomocí rozhraní API pro registraci.

Poznámka:

Při publikování události ověření platí omezení 2 požadavků za minutu.

Adresa URL prostředku

https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents

Příklad požadavku

POST /webhooks/v1/registration/validationEvents
MS-CorrelationId: dddd3333-ee44-5555-66ff-777777aaaaaa
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate
Content-Length:

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 181
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: eeee4444-ff55-6666-77aa-888888bbbbbb
MS-RequestId: 2f498d5a-a6ab-468f-98d8-93c96da09051
X-Locale: en-US

{ "correlationId": "eeee4444-ff55-6666-77aa-888888bbbbbb" }

Ověřte, že událost byla doručena

Vrátí aktuální stav události ověření. Toto ověření může být užitečné při řešení potíží s doručováním událostí. Odpověď obsahuje výsledek pro každý pokus o doručení události.

Adresa URL prostředku

https://api.partnercenter.microsoft.com/webhooks/v1/registration/validationEvents/{correlationId}

Příklad požadavku

GET /webhooks/v1/registration/validationEvents/eeee4444-ff55-6666-77aa-888888bbbbbb
MS-CorrelationId: dddd3333-ee44-5555-66ff-777777aaaaaa
Authorization: Bearer ...
Accept: */*
Host: api.partnercenter.microsoft.com
Accept-Encoding: gzip, deflate

Příklad odpovědi

HTTP/1.1 200
Status: 200
Content-Length: 469
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
MS-CorrelationId: ffff5555-aa66-7777-88bb-999999cccccc
MS-RequestId: 0843bdb2-113a-4926-a51c-284aa01d722e
X-Locale: en-US

{
    "correlationId": "eeee4444-ff55-6666-77aa-888888bbbbbb",
    "partnerId": "00234d9d-8c2d-4ff5-8c18-39f8afc6f7f3",
    "status": "completed",
    "callbackUrl": "{{YourCallbackUrl}}",
    "results": [{
        "responseCode": "OK",
        "responseMessage": "",
        "systemError": false,
        "dateTimeUtc": "2017-12-08T21:39:48.2386997"
    }]
}

Příklad ověření podpisu

Ukázkový podpis kontroleru zpětného volání (ASP.NET)

[AuthorizeSignature]
[Route("webhooks/callback")]
public IHttpActionResult Post(PartnerResourceChangeCallBack callback)

Ověření podpisu

Následující příklad ukazuje, jak přidat autorizační atribut kontroleru, který přijímá zpětná volání z událostí Webhooku.

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 StreamReaders;
            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}."));
            }
        }
    }
}