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: Faturalama Yöneticisi | 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 bir etkinlik almak için iş ortakları, kaynak değişikliği etkinliğini post etmek üzere İş Ortağı Merkezi'ne bir callback kurar. İş ortağının İş Ortağı Merkezi'nden gönderildiğini doğrulayabilmesi için olay dijital olarak imzalanır. Webhook bildirimleri yalnızca Ortak Satış için en son yapılandırmaya sahip olan ortama tetiklenir.

İş Ortağı Merkezi aşağıdaki Web Kancası (Webhook) olaylarını destekler.

  • Azure Dolandırıcılık Etkinliği Tespit Edildi ("azure-fraud-event-detected")

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

  • Yetkilendirilmiş Yönetici İlişkisinin Onaylandığı Etkinlik ("dap-admin-relationship-approved")

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

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

    Bu olay, müşteri kiracısı Bayi İlişkisini onayladığında tetiklenir.

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

    Bu olay, müşteri kiracısı Dolaylı Bayi İlişkisi'ni onayladığında tetiklenir.

  • Delege Edilmiş Yönetici İlişkisinin Sonlandırılması Etkinliği ("dap-admin-relationship-terminated")

    Bu etkinlik, müşteri Temsilci Yönetici Ayrıcalıklarını sonlandırdığında oluşturulur.

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

    DAP'in 90 günden uzun süre etkisiz kalması durumunda, Microsoft'un İş Ortağı ve Müşteri kiracısı arasında DAP'ı sonlandırdığı anda bu olay tetiklenir.

  • Granüler Yönetici Erişim Ataması Etkinleştirildi Olayı ("granular-admin-access-assignment-activated")

    Belirli güvenlik gruplarına Microsoft Entra rolleri atandığında, iş ortağı Granüler Yetkilendirilmiş Yönetici Ayrıcalıkları erişim atamasını etkinleştirdiğinde bu olay meydana gelir.

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

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

  • Granüler Yönetici Erişim Ataması Silindi Olayı ("granular-admin-access-assignment-deleted")

    Bu olay, iş ortağı Ayrıntılı Delegasyonlu Yönetici Yetkileri erişim atamasını sildiğinde oluşturulur.

  • Granüler Yönetici Erişim Ataması Güncelleme Olayı ("granular-admin-access-assignment-updated")

    Bu olay, iş ortağı Ayrıntılı Yetkilendirilmiş Yöneticilik Ayrıcalıkları erişim atamasını güncelleştirdiğinde tetiklenir.

  • Granüler Yönetici İlişkisi Aktifleştirme Olayı ("granular-admin-relationship-activated")

    Müşterinin onaylaması için Granüler Temsilci Yönetici Ayrıcalıkları oluşturulup etkin hale geldiğinde bu olay tetiklenir.

  • Granüler Yönetici İlişkisi Onay Etkinliği ("granular-admin-relationship-approved")

    Müşteri kiracı, Ayrıntılı Yetkilendirilmiş Yönetici Ayrıcalıklarını onayladığında bu olay tetiklenir.

  • Detaylı Yönetici İlişkisi Sona Erdi Etkinliği ("granular-admin-relationship-expired")

    Bu olay, Granüler Temsilci Yönetici Ayrıcalıklarının süresi dolduğunda oluşur.

  • Granüler Yönetici İlişkisi Oluşturuldu Etkinliği ("granular-admin-relationship-created")

    Bu olay, Granüler Temsilci Yönetici Ayrıcalıkları oluşturulduğunda tetiklenir.

  • Granüler Yönetici İlişki Güncellenme Etkinliği ("granular-admin-relationship-updated")

    Bu olay, müşteri veya iş ortağı Ayrıntılı Yetkili Yönetici Ayrıcalıkları'nı güncelleştirdiğinde tetiklenir.

  • Granüler Yönetici İlişkisi Otomatik Uzatma Etkinliği ("granular-admin-relationship-auto-extended")

    Sistem Ayrıntılı Yetkilendirilmiş Yönetici Ayrıcalıklarını otomatik olarak genişlettiğinde bu olay tetikleniyor.

  • Granüler Yönetim İlişkisi Sonlandırma Olayı ("granular-admin-relationship-terminated")

    Bu olay, iş ortağı veya müşteri kiracısı Granüler Delegeli Yönetici Ayrıcalıkları'nı sonlandırdığında oluşur.

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

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

  • Yeni Ticaret Taşınması Oluşturuldu ("new-commerce-migration-created")

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

  • 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.

  • Transfer Oluştur ("create-transfer")

    Transfer oluşturulduğunda bu olay tetiklenir.

  • Güncelleme Aktarımı

    Bu olay, aktarım güncellendiğinde tetiklenir.

  • Tam Aktarım ("tam aktarım")

    Aktarım tamamlandığında bu olay tetiklenir.

  • Transfer Süresi Bitimi ("expire-transfer")

    Bu olay, aktarım süresi dolduğunda tetikleniyor.

  • Hata Aktarımı ("hata aktarımı")

    Aktarım başarısız olduğunda bu olay oluşur.

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

    Bu olay, yeni ticaret geçiş zamanlamasının başarısız olması durumunda tetikleniyor.

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

    Bu etkinlik, bir referans oluşturulduğunda tetiklenir.

  • Referans Güncellendi Etkinliği ("referans-güncellendi")

    Başvuru güncellendiğinde bu etkinlik tetiklenir.

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

    Bu olay, ilgili yönlendirme oluşturulduğunda tetiklenir.

  • İlgili Referans Güncelleme Etkinliği ("related-referral-updated")

    İlgili başvuru güncellendiğinde bu olay tetiklenir.

  • Abonelik Aktif Etkinliği ("abonelik-aktif")

    Bu olay, abonelik etkinleştirildiğinde tetiklenir.

    Not

    Abonelik Aktif webhook'u ve ilgili Etkinlik Günlüğü Kaydı şu anda yalnızca Korumalı Alan kiracıları için kullanılabilir.

  • Abonelik Bekleme Durumu ("abonelik beklemede")

    Bu olay, karşılık gelen sipariş başarıyla alındığında ve abonelik oluşturma işlemi beklemede olduğunda tetiklenir.

    Not

    Şu anda Abonelik Onay Bekliyor webhook'u ve ilgili Etkinlik Günlüğü Kaydı yalnızca Test Ortamı kiracıları için kullanılabilir.

  • Abonelik Yenilendi Etkinliği ("abonelik yenilendi")

    Abonelik yenileme tamamlandığında bu olay tetikleniyor.

    Not

    Abonelik Yenilendi webhook'u ve buna karşılık gelen Etkinlik Günlüğü, şu anda yalnızca Sandbox ortamı kiracıları için kullanılabilir.

  • Abonelik Güncelleme Olayı ("abonelik-güncelleme")

    Abonelik değiştiğinde bu olay tetikleniyor. Bu olaylar, İş Ortağı Merkezi API'sini kullanarak 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 etkinlik, kendi kendinize katılım sürecini yönetmenizi ve test etmenizi mümkün kılar. Bir test etkinliği istemeniz ve daha sonra ilerlemesini takip etmeniz yoluyla kaydınızı yapabilirsiniz. Microsoft'tan etkinliği teslim ederken alınan hata mesajlarını 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şiğin Aşıldığı Etkinlik ("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/müşterileriniz için bir Azure harcama bütçesi ayarlama).

Ortakların kontrolünde olmayan sistemlerde değişiklik gösteren kaynaklar için gelecekte Webhook olayları eklenecek ve bu olayları mümkün olduğunca "gerçek zamanlıya" yaklaştırmak için daha fazla güncelleme yapılacak. İş ortaklarından gelen ve etkinliklerin işlerine değer katacakları geri bildirimler, eklenecek yeni olayları belirlemede yararlıdır.

İş Ortağı Merkezi tarafından desteklenen webhook olaylarının tam listesi için bkz İş Ortağı Merkezi webhook 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 etkinlikleri alma

İş Ortağı Merkezi'nden etkinlikleri almak için, genel erişime açık 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 Webhook olayları, Microsoft Kök Sertifikası Zinciri'ne bağlı bir sertifikayla dijital olarak imzalanmıştır. Etkinliği 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 yenilenmesini sağlar. İş Ortağı Merkezi, etkinliği ulaştırmak için 10 girişimde bulunur. Olay 10 denemeden sonra hala teslim edilmemişse çevrimdışı bir kuyruğa taşınır ve teslimde başka bir deneme 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 başlığında "Signature" adlı bir şema 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ı doğrulamak için aşağıdaki 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 "Kurumunu" 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.

Özellikler

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

Webhook API'leri

Kimlik Doğrulaması

Webhook API'lerine yapılan tüm çağrıların kimliği, Yetkilendirme Başlığındaki Bearer belirteci kullanılarak doğrulanır. https://api.partnercenter.microsoft.com'ye erişmek iç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: aaaa0000-bb11-2222-33cc-444444dddddd
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: 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" ]
}

Kaydı görüntüle

Kiracı için Webhook olay kaydını geri döndürür.

Kaynak URL

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

Talep ö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: cccc2222-dd33-4444-55ee-666666ffffff
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 olay kaydını güncelleştirir.

Kaynak URL

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

Talep ö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: 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" ]
}

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

Webhook 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 isteğe izin veren bir hız 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: dddd3333-ee44-5555-66ff-777777aaaaaa
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: eeee4444-ff55-6666-77aa-888888bbbbbb
MS-RequestId: 2f498d5a-a6ab-468f-98d8-93c96da09051
X-Locale: en-US

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

Etkinliğin teslim edildiğini 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/eeee4444-ff55-6666-77aa-888888bbbbbb
MS-CorrelationId: dddd3333-ee44-5555-66ff-777777aaaaaa
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: 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"
    }]
}

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