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: Globální správce | 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 chcete přijímat událost z Partnerského centra, partneři hostují zpětné volání, kde Partnerské centrum může událost změny prostředku PUBLIKOVAT. 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.

  • Vztah prodejce akceptovaný událostí zákazníka (reseller-relationship-accepted-by-customer)

    Tato událost se vyvolá, když tenant zákazníka schválí vztah prodejce.

  • Vztah nepřímého prodejce akceptovaný událostí zákazníka ("indirect-reseller-relationship-accepted-by-customer")

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

  • Delegovaná událost ukončení vztahu 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ů.

  • Podrobná událost přiřazení přístupu správce aktivovaná (granular-admin-access-assignment-activated)

    Tato událost se vyvolá, když partner aktivuje přiřazení přístupu k oprávněním podrobného delegovaného správce po přiřazení rolí Microsoft Entra ke konkrétním skupinám zabezpečení.

  • Podrobná událost vytvoření přiřazení 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í.

  • Podrobná událost přiřazení přístupu správce – Odstraněná událost (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.

  • Podrobná událost přiřazení přístupu správce – aktualizovaná událost (granular-admin-access-assignment-updated)

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

  • Podrobná událost aktivovaná vztahem správce (granular-admin-relationship-activated)

    Tato událost se vyvolá, když se vytvoří podrobná delegovaná oprávnění správce a aktivuje se, aby zákazník schvaloval.

  • Podrobná událost schválená vztahem správce (granular-admin-relationship-approved)

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

  • Podrobná událost relace správce s vypršenou platností (granular-admin-relationship-expired)

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

  • Podrobná událost vytvoření 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.

  • Podrobná událost aktualizace vztahu správce ("granular-admin-relationship-updated")

    Tato událost se vyvolá, když zákazník nebo partner aktualizuje podrobná delegovaná oprávnění správce.

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

    Tato událost se vyvolá, když systém automaticky rozšíří oprávnění podrobného delegovaného správce.

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

    Tato událost se vyvolá, když partner nebo tenant zákazníka ukončí podrobná delegovaná oprávnění správce.

  • Událost připravená k faktuře (připraveno k faktuře)

    Tato událost se vyvolá, když je nová faktura připravená.

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

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

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

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

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

    Tato událost se vyvolá, když dojde k selhání migrace nového komerčního obchodu.

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

    Tato událost se vyvolá 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á po dokončení 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 vyvolá při vytvoření referenčního seznamu.

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

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

  • Související událost vytvoření referenčního seznamu (vytvoření souvisejícího referenčního seznamu)

    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 se vyvolá při aktualizaci souvisejícího referenčního seznamu.

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

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

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

    Tato událost se vyvolá, když se odběr čeká na vyřízení.

  • Událost prodloužení předplatného (prodloužení předplatného)

    Tato událost se vyvolá při prodloužení předplatného.

  • 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é microsoft přijímá. Toto omezení platí jenom pro události vytvořené testem. Data starší než sedm dnů se vymažou.

  • Prahová hodnota překročila událost ("usagerecords-thresholdExceeded")

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

Budoucí události webhooku budou přidány pro prostředky, které se mění v systému, který partner nemá pod kontrolou, a další aktualizace budou provedeny tak, aby se tyto události co nejvíce blížily "reálnému čase". Zpětná vazba odpartnerůch

Ú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 zřetěděný s kořenem Microsoftu. 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. V Partnerském centru se 10 pokusů o doručení události. Pokud se událost po 10 pokusech stále nedoručí, přesune se do offline fronty a při doručení se neprovedou žádné další pokusy.

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.

Ověření zpětného 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. Přečtěte si 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 formuláři {resource}-{action}. Například "test-created".
Identifikátor ResourceUri Identifikátor URI prostředku, který se změnil.
ResourceName Název prostředku, který se změnil.
AuditUrl Nepovinné. Identifikátor URI záznamu auditování.
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í nosné tokeny v autorizační hlavičce. 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 požadavku

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

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í limit 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ěření doručení události

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