Sdílet prostřednictvím


Webhook 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 události z Partnerského centra, musí vytvořit místo, kam může Partnerské centrum zaslat (POST) událost změny zdroje. 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 nájemcem zákazníka.

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

    Tato událost nastane, když klientský nájemce schválí prodejní vztah.

  • 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 se zprostředkovatelem.

  • 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 spustí, když Microsoft ukončí DAP mezi tenantem partnera a zákazníkem po více než 90 dnech neaktivity DAP.

  • Událost aktivace detailní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ří granulární 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ích oprávnění 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 detailní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 aktivují Granulární delegovaná oprávnění správce, která zákazník schválí.

  • 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í granulární delegovaná oprávnění správce.

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

    Tato událost se vyvolá, když vyprší platnost Granulovaných Delegovaných Správcovských Oprávnění.

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

    Tato událost se vyvolá při vytvoření granulární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í.

  • Granulární 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.

  • Ukončení granulárního vztahu administrátora ("granular-admin-relationship-terminated")

    Tato událost se vyvolá, když partner nebo tenant zákazníka ukončí Granulární 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é obchodní migrace.

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

    Tato událost se vyvolá, když selže migrace nového obchodního systému.

  • Vytvořit převod ("create-transfer")

    Tato událost je vyvolána při vytvoření přenosu.

  • Update Transfer ("update-transfer")

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

  • Dokončení převodu ("úplný převod")

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

  • Prodloužení platnosti převodu ("vypršení 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ří harmonogram nové obchodní migrace.

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

    Tato událost je vyvolána při vytvoření doporučení.

  • Událost aktualizace doporučení ("aktualizace doporučení")

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

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

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

  • 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áno související doporučení.

  • 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. Můžete vidět zprávy o selhání, které přicházejí od Microsoftu, když se pokoušíte doručit událost. 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, jak je popsáno v ověřování Partnerského centra. Tento scénář podporuje ověřování jak pomocí samostatných přihlašovacích údajů aplikace, tak přihlašovacích údajů 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í událost doručit celkem 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 zdroje, který se změnil.
ResourceName Název prostředku, který se změnil.
AuditUrl Nepovinné. URI záznamu auditu.
Datum změny zdroje UTC 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

Příklad žá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 prostředku

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

Ukázka žádosti

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 Webhooks pro tenanta.

URL zdroje

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

Příklad žádosti

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

Ukázka žádosti

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

Vytvoří testovací událost k ověření registrace webových háčků. 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í rychlosti 2 požadavků za minutu.

Adresa URL zdroje

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

Ukázka žádosti

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}

Ukázka žádosti

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