Aracılığıyla paylaş


İş Ortağı Merkezi web kancaları

Şunlar için geçerlidir: İş Ortağı Merkezi | 21Vianet tarafından işletilen İş Ortağı Merkezi | ABD Kamu için Microsoft Bulut İş Ortağı Merkezi

Uygun roller: Genel yönetici | Faturalama Yönetici | Yönetici aracısı | Satış temsilcisi | Yardım masası aracısı

İş Ortağı Merkezi Web Kancası API'leri, iş ortaklarının kaynak değişikliği olaylarına kaydolmasına olanak sağlar. Bu olaylar, iş ortağının kayıtlı URL'sine HTTP POST'leri biçiminde teslim edilir. İş Ortağı Merkezi'nden olay almak için iş ortakları, İş Ortağı Merkezi'nin kaynak değişikliği olayını GÖNDERebileceği bir geri çağırma barındıracaktır. İş ortağının İş Ortağı Merkezi'nden gönderildiğini doğrulayabilmesi için olay dijital olarak imzalanır. Web kancası bildirimleri yalnızca Ortak satış için en son yapılandırmaya sahip olan ortama tetiklenir.

İş ortakları, aşağıdaki örnekler gibi İş Ortağı Merkezi tarafından desteklenen Web kancası olaylarından seçim yapabilir.

  • Azure Sahtekarlık Olayı Algılandı ("azure-fraud-event-detected")

    Azure sahtekarlık olayı algılandığında bu olay tetikleniyor.

  • temsilci Yönetici İlişki Onaylandı Olayı ("dap-admin-relationship-approved")

    Temsilci Yönetici Ayrıcalıkları müşteri kiracısı tarafından onaylandığında bu olay tetiklenir.

  • Müşteri Etkinliği Tarafından Kabul Edilen Bayi İlişkisi ("bayi-ilişki-müşteri tarafından kabul edilen")

    Bu olay, Bayi İlişkisi müşteri kiracısı tarafından onaylandığında tetiklenir.

  • temsilci Yönetici İlişki Sonlandırılan Olay ("dap-admin-relationship-terminated")

    Bu olay, Temsilci Yönetici Ayrıcalıkları müşteri tarafından sonlandırıldığında oluşturulur.

  • Microsoft Olayı tarafından Sonlandırılan Dap Yönetici İlişkisi ("dap-admin-relationship-terminated-by-microsoft")

    Bu olay, DAP 90 günden uzun süre etkin olmadığında Microsoft İş Ortağı ve Müşteri kiracısı arasında DAP'ı sonlandırdığında tetiklenir.

  • Ayrıntılı Yönetici Erişim Ataması Etkin Olayı ("granular-admin-access-assignment-activated")

    Bu olay, Microsoft Entra rolleri belirli güvenlik gruplarına atandıktan sonra Ayrıntılı Temsilci Yönetici Ayrıcalıklar erişim ataması iş ortağı tarafından etkinleştirildiğinde tetiklenir.

  • Ayrıntılı Yönetici Erişim Ataması Oluşturma Olayı ("granular-admin-access-assignment-created")

    Bu olay, iş ortağı tarafından Ayrıntılı Temsilci Yönetici Ayrıcalıklar erişim ataması oluşturulduğunda oluşturulur. İş ortakları, müşteri onaylı Microsoft Entra rollerini belirli güvenlik gruplarına atayabilir.

  • Ayrıntılı Yönetici Erişim Ataması Silindi Olayı ("granular-admin-access-assignment-deleted")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıklar erişim ataması iş ortağı tarafından silindiğinde oluşturulur.

  • Ayrıntılı Yönetici Erişim Ataması Güncelleştirilmiş Olayı ("granular-admin-access-assignment-updated")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıklar erişim ataması iş ortağı tarafından güncelleştirildiğinde oluşturulur.

  • Ayrıntılı Yönetici İlişki Etkinleştirilmiş Olayı ("granular-admin-relationship-activated")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıkları oluşturulduğunda ve müşterinin onaylaması için etkin olduğunda oluşturulur.

  • Ayrıntılı Yönetici İlişki Onaylandı Olayı ("granular-admin-relationship-approved")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıkları müşteri kiracısı tarafından onaylandığında tetiklenir.

  • Ayrıntılı Yönetici İlişki süresi Doldu Olayı ("granular-admin-relationship-expired")

    Ayrıntılı Temsilci Yönetici Ayrıcalıklarının süresi dolduğunda bu olay tetikleniyor.

  • Ayrıntılı Yönetici İlişki güncelleştirildi Olayı ("granular-admin-relationship-updated")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıkları iş ortağı/müşteri kiracısı tarafından güncelleştirildiğinde tetiklenir.

  • Ayrıntılı Yönetici İlişkisi Otomatik Genişletilmiş Olayı ("granular-admin-relationship-auto-extended")

    Bu olay, Ayrıntılı Temsilci Yönetici Ayrıcalıkları sistem tarafından otomatik olarak genişletildiğinde oluşturulur.

  • Ayrıntılı Yönetici İlişki Sonlandırılan Olay ("granular-admin-relationship-terminated")

    Ayrıntılı Temsilci Yönetici Ayrıcalıkları iş ortağı/müşteri kiracısı tarafından sonlandırıldığında bu olay tetiklenir.

  • Yeni Ticaret Geçişi Tamamlandı ("new-commerce-migration-completed")

    Bu olay, yeni ticaret geçişi tamamlandığında tetikleniyor.

  • Yeni Ticaret Geçişi Oluşturuldu ("new-commerce-migration-created")

    Bu olay, yeni ticaret geçişi oluşturulduğunda oluşturulur.

  • Yeni Ticaret Geçişi Başarısız Oldu ("new-commerce-migration-failed")

    Yeni ticaret geçişi başarısız olduğunda bu olay tetikleniyor.

  • Yeni Ticaret Geçiş Zamanlaması Başarısız Oldu ("new-commerce-migration-schedule-failed")

    Bu olay, yeni ticaret geçiş zamanlaması başarısız olduğunda tetikleniyor.

  • Referans Oluşturma Olayı ("referans oluşturuldu")

    Referans oluşturulduğunda bu olay oluşturulur.

  • Referans Güncelleştirildi Olayı ("referans güncelleştirildi")

    Başvuru güncelleştirildiğinde bu olay tetikleniyor.

  • İlgili Referans Oluşturma Olayı ("related-referral-created")

    bu olay, ilgili başvuru oluşturulduğunda oluşturulur.

  • İlgili Referans Güncelleştirildi Olayı ("related-referral-updated")

    İlgili başvuru güncelleştirildiğinde bu olay oluşturulur.

  • Abonelik Güncelleştirildi Olayı ("abonelik güncelleştirildi")

    Abonelik değiştiğinde bu olay tetikleniyor. Bu olaylar, İş Ortağı Merkezi API'si aracılığıyla yapılan değişikliklere ek olarak bir iç değişiklik olduğunda oluşturulur.

    Not

    Aboneliğin değişmesi ile Abonelik Güncelleştirildi olayının tetiklenmesi arasında 48 saate kadar gecikme olur.

  • Test Olayı ("test oluşturma")

    Bu olay, bir test olayı isteyerek ve ardından ilerleme durumunu izleyerek kaydınızı kendi kendine eklemenizi ve test etmenizi sağlar. Olayı teslim etmeye çalışırken Microsoft'tan alınan hata iletilerini görebilirsiniz. Bu kısıtlama yalnızca "test tarafından oluşturulan" olaylar için geçerlidir. Yedi günden eski veriler temizlenir.

  • Eşik Aşıldı Olayı ("usagerecords-thresholdExceeded")

    Bu olay, herhangi bir müşteri için Microsoft Azure kullanımı miktarı kullanım harcaması bütçesini (eşikleri) aştığında ortaya çıkar. Daha fazla bilgi için bkz. (Müşterileriniz için azure harcama bütçesi ayarlama/iş ortağı merkezi/set-an-azure-spending-budget-for-your-customers).

İş ortağının denetiminde olmadığı sistemde değişen kaynaklar için gelecekteki Web Kancası olayları eklenir ve bu olayları mümkün olduğunca "gerçek zamanlıya" yakın hale getirmek için daha fazla güncelleştirme yapılır. İş ortaklarının etkinliklerinin işlerine değer katacağı geri bildirim, eklenecek yeni olayları belirlemede yararlı olacaktır.

İş Ortağı Merkezi tarafından desteklenen Web kancası olaylarının tam listesi için bkz . İş Ortağı Merkezi web kancası olayları.

Önkoşullar

  • İş Ortağı Merkezi kimlik doğrulamasında açıklandığı gibi kimlik bilgileri. Bu senaryo hem tek başına Uygulama hem de Uygulama+Kullanıcı kimlik bilgileriyle kimlik doğrulamayı destekler.

İş Ortağı Merkezi'nden olayları alma

İş Ortağı Merkezi'nden olay almak için genel olarak erişilebilen bir uç noktayı kullanıma sunmanız gerekir. Bu uç nokta kullanıma sunulduğundan, iletişimin İş Ortağı Merkezi'nden olduğunu doğrulamanız gerekir. Aldığınız tüm Web Kancası olayları, Microsoft Kökü'ne zincirlenmiş bir sertifikayla dijital olarak imzalar. Olayı imzalamak için kullanılan sertifikanın bağlantısı da sağlanacaktır. Bu, hizmetinizi yeniden dağıtmanıza veya yeniden yapılandırmanıza gerek kalmadan sertifikanın yenilenmesine olanak sağlar. İş Ortağı Merkezi olayı teslim etmek için 10 girişimde bulunur. Olay 10 denemeden sonra hala teslim edilmezse, çevrimdışı bir kuyruğa taşınır ve teslimde başka bir girişim yapılmaz.

Aşağıdaki örnekte İş Ortağı Merkezi'nden gönderilen bir olay gösterilmektedir.

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

Not

Yetkilendirme üst bilgisinin bir "İmza" düzeni vardır. Bu, içeriğin base64 kodlanmış imzasıdır.

Geri arama kimliğini doğrulama

İş Ortağı Merkezi'nden alınan geri çağırma olayının kimliğini doğrulamak için şu adımları izleyin:

  1. Gerekli üst bilgilerin mevcut olduğunu doğrulayın (Yetkilendirme, x-ms-certificate-url, x-ms-signature-algorithm).

  2. İçeriği imzalamak için kullanılan sertifikayı indirin (x-ms-certificate-url).

  3. Sertifika Zincirini doğrulayın.

  4. Sertifikanın "Kuruluşunu" doğrulayın.

  5. UTF8 kodlamalı içeriği bir arabelleğe okuyun.

  6. Bir RSA Şifreleme Sağlayıcısı oluşturun.

  7. Verilerin belirtilen karma algoritmasıyla (örneğin SHA256) imzalanan değerle eşleşir olduğunu doğrulayın.

  8. Doğrulama başarılı olursa iletiyi işleyin.

Not

Varsayılan olarak, imza belirteci bir Yetkilendirme üst bilgisinde gönderilir. Kaydınızda SignatureTokenToMsSignatureHeader değerini true olarak ayarlarsanız, imza belirteci bunun yerine x-ms-signature üst bilgisinde gönderilir.

Olay modeli

Aşağıdaki tabloda bir İş Ortağı Merkezi olayının özellikleri açıklanmaktadır.

Properties

Veri Akışı Adı Açıklama
EventName Olayın adı. {resource}-{action} biçiminde. Örneğin, "test-oluşturuldu".
ResourceUri Değişen kaynağın URI'sini.
Resourcename Değişen kaynağın adı.
AuditUrl isteğe bağlı. Denetim kaydının URI'sini.
ResourceChangeUtcDate Kaynak değişikliğinin gerçekleştiği UTC biçimindeki tarih ve saat.

Örnek

Aşağıdaki örnekte bir İş Ortağı Merkezi olayının yapısı gösterilmektedir.

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

Web Kancası API'leri

Kimlik Doğrulaması

Web Kancası API'lerine yapılan tüm çağrıların kimliği Yetkilendirme Üst Bilgisindeki Taşıyıcı belirteci kullanılarak doğrulanır. erişmek https://api.partnercenter.microsoft.comiçin bir erişim belirteci alın. Bu belirteç, İş Ortağı Merkezi API'lerinin geri kalanına erişmek için kullanılan belirteçle aynıdır.

Olayların listesini alma

Şu anda Web Kancası API'leri tarafından desteklenen olayların listesini döndürür.

Kaynak URL

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

İstek örneği

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

Yanıt örneği

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

Olayları almak için kaydolma

Belirtilen olayları almak için bir kiracı kaydeder.

Kaynak URL

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

İstek örneği

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

Yanıt örneği

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

Kaydı görüntüleme

Bir kiracı için Web kancaları olay kaydını döndürür.

Kaynak URL

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

İstek örneği

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

Yanıt örneği

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

Olay kaydını güncelleştirme

Mevcut bir olay kaydını Güncelleştirmeler.

Kaynak URL

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

İstek örneği

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

Yanıt örneği

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

Kaydınızı doğrulamak için bir test olayı gönderme

Web kancaları kaydını doğrulamak için bir test olayı oluşturur. Bu test, İş Ortağı Merkezi'nden olayları alabildiğinizi doğrulamaya yöneliktir. Bu olaylara ilişkin veriler, ilk olay oluşturulduktan yedi gün sonra silinir. Doğrulama olayı göndermeden önce kayıt API'sini kullanarak "test tarafından oluşturulan" olay için kayıtlı olmanız gerekir.

Not

Doğrulama olayı gönderildiğinde dakikada 2 istek azaltma sınırı vardır.

Kaynak URL

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

İstek örneği

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:

Yanıt örneği

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

Olayın teslim edilmiş olduğunu doğrulayın

Doğrulama olayının geçerli durumunu döndürür. Bu doğrulama, olay teslim sorunlarını gidermek için yararlı olabilir. Yanıt, olayı teslim etmek için yapılan her deneme için bir sonuç içerir.

Kaynak URL

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

İstek örneği

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

Yanıt örneği

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

İmza Doğrulama örneği

Örnek Geri Çağırma Denetleyicisi imzası (ASP.NET)

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

İmza Doğrulama

Aşağıdaki örnekte, Web kancası olaylarından geri çağırmalar alan denetleyiciye yetkilendirme özniteliğinin nasıl ekleneceği gösterilmektedir.

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