Bagikan melalui


Webhook Pusat Mitra

Berlaku untuk: Pusat Mitra | Pusat Mitra yang dioperasikan oleh 21Vianet | Pusat Mitra untuk Microsoft Cloud untuk Pemerintah AS

Peran yang sesuai: Admin Penagihan | Agen Administrasi | Agen Penjualan | Agen Helpdesk

API Webhook Pusat Mitra memungkinkan mitra untuk mendaftar untuk peristiwa perubahan sumber daya. Peristiwa ini dikirimkan dalam bentuk HTTP POST ke URL terdaftar mitra. Untuk menerima peristiwa dari Pusat Mitra, mitra mengatur panggilan balik di mana Pusat Mitra dapat mengirimkan peristiwa perubahan sumber daya. Peristiwa ini ditandatangani secara digital agar mitra dapat memverifikasi bahwa peristiwa tersebut memang dikirimkan dari Partner Center. Pemberitahuan webhook hanya dikirimkan ke lingkungan yang memiliki konfigurasi terbaru untuk Penjualan bersama.

Pusat Mitra mendukung peristiwa Webhook berikut.

  • Peristiwa Penipuan Azure Terdeteksi ("terdeteksi peristiwa penipuan azure")

    Kejadian ini dimunculkan saat peristiwa penipuan Azure terdeteksi.

  • Peristiwa Persetujuan Hubungan Admin yang Didelegasikan ("dap-admin-relationship-approved")

    Peristiwa ini terjadi ketika Hak Istimewa Admin yang Didelegasikan disetujui oleh penyewa pelanggan.

  • Hubungan Penjual Diterima oleh Acara Penerimaan Pelanggan ("reseller-relationship-accepted-by-customer")

    Peristiwa ini terjadi ketika penyewa pelanggan menyetujui Hubungan Peritel.

  • Hubungan Reseller Tidak Langsung Diterima oleh Pelanggan ("indirect-reseller-relationship-accepted-by-customer")

    Peristiwa ini dipicu ketika tenant pelanggan menyetujui Hubungan Tidak Langsung dengan Reseller.

  • Peristiwa Penghentian Hubungan Admin yang Didelegasikan ("dap-admin-relationship-terminated")

    Kejadian ini dimunculkan ketika pelanggan mengakhiri Hak Istimewa Admin yang Didelegasikan.

  • Hubungan Dap Admin Dihentikan oleh Microsoft ("dap-admin-relationship-terminated-by-microsoft")

    Peristiwa ini terjadi ketika Microsoft mengakhiri DAP antara tenant Mitra dan Pelanggan saat DAP tidak aktif selama lebih dari 90 hari.

  • Peristiwa Penetapan Akses Administrator Granular yang Diaktifkan ("granular-admin-access-assignment-activated")

    Peristiwa ini terjadi ketika mitra mengaktifkan akses Granular Delegated Admin Privileges setelah peran Microsoft Entra ditetapkan ke grup keamanan tertentu.

  • Peristiwa Penetapan Akses Admin Granular Dibuat ("granular-admin-access-assignment-created")

    Kejadian ini terjadi ketika mitra membuat penugasan akses Granular Delegated Admin Privileges. Mitra dapat menetapkan peran Microsoft Entra yang disetujui pelanggan ke grup keamanan tertentu.

  • Peristiwa Penghapusan Pemberian Akses Admin Granular ("granular-admin-access-assignment-deleted")

    Kejadian ini dimunculkan ketika mitra menghapus penetapan akses Granular Delegated Admin Privileges.

  • Peristiwa Terbaru Penetapan Akses Admin Granular ("granular-admin-access-assignment-updated")

    Kejadian ini terjadi ketika mitra memperbarui penugasan akses Granular Delegated Admin Privileges.

  • Peristiwa Aktivasi Hubungan Administratif Granular ("aktivasi-hubungan-administratif-granular")

    Peristiwa ini dipicu ketika Hak Admin Granular yang Didelegasikan dibuat dan aktif untuk persetujuan pelanggan.

  • Peristiwa Persetujuan Hubungan Admin Granular ("granular-admin-relationship-approved")

    Peristiwa ini terjadi ketika tenant pelanggan menyetujui Hak Administratif Terdelegasi Granular.

  • Peristiwa Berakhirnya Hubungan Admin Granular ("granular-admin-relationship-expired")

    Peristiwa ini dipicu ketika Hak Delegasi Admin Granular kedaluwarsa.

  • Peristiwa Terbentuknya Hubungan Admin Granular ("granular-admin-relationship-created")

    Peristiwa ini diaktifkan ketika Hak Istimewa Admin yang Didelegasikan Secara Granular dibuat.

  • Peristiwa Pembaruan Hubungan Admin Granular ("granular-admin-relationship-updated")

    Kejadian ini dimunculkan ketika pelanggan atau mitra memperbarui Hak Istimewa Admin Yang Didelegasikan Granular.

  • Peristiwa Perluasan Otomatis Hubungan Admin Granular ("granular-admin-relationship-auto-extended")

    Peristiwa ini terjadi ketika sistem secara otomatis memperluas Hak Admin Didelegasikan Secara Granular.

  • Peristiwa Pengakhiran Hubungan Admin Granular ("granular-admin-relationship-terminated")

    Kejadian ini dimunculkan ketika mitra atau penyewa pelanggan mengakhiri Hak Istimewa Admin Yang Didelegasikan Granular.

  • Migrasi Komersial Baru Selesai ("new-commerce-migration-completed")

    Kejadian ini dimunculkan ketika migrasi perdagangan baru selesai.

  • Migrasi Komersial Baru Telah Dibuat ("new-commerce-migration-created")

    Kejadian ini dimunculkan saat migrasi perdagangan baru dibuat.

  • Migrasi Perdagangan Baru Gagal ("new-commerce-migration-failed")

    Peristiwa ini dimunculkan ketika migrasi perdagangan baru gagal.

  • Buat Transfer ("create-transfer")

    Peristiwa ini dipicu ketika transfer dibuat.

  • Transfer Pembaruan ("update-transfer")

    Peristiwa ini diaktifkan ketika transfer diperbarui.

  • Transfer Selesai ("transfer selesai")

    Peristiwa ini diaktifkan ketika transfer selesai.

  • Pengalihan Kedaluwarsa ("expire-transfer")

    Peristiwa ini dipicu ketika transfer kedaluwarsa.

  • Transfer Gagal ("transfer gagal")

    Peristiwa ini dipicu ketika transfer gagal.

  • Jadwal Migrasi Komersial Baru Gagal ("new-commerce-migration-schedule-failed")

    Kejadian ini terjadi ketika jadwal migrasi perdagangan baru mengalami kegagalan.

  • Peristiwa Pembuatan Rujukan ("referral-created")

    Peristiwa ini terjadi ketika rujukan dibuat.

  • Peristiwa Pembaruan Rujukan ("rujukan-diperbarui")

    Peristiwa ini terjadi ketika rujukan diperbarui.

  • Peristiwa Penyiapan Rujukan Terkait ("penyiapan-rujukan-terkait")

    Peristiwa ini terpicu ketika rujukan terkait dibuat.

  • Peristiwa Pembaruan Rujukan Terkait ("rujukan-terkait-diperbarui")

    Peristiwa ini terjadi ketika rujukan terkait diperbarui.

  • Peristiwa Aktif Langganan ("langganan aktif")

    Kejadian ini dipicu ketika langganan diaktifkan.

    Catatan

    Webhook Langganan Aktif dan Event Catatan Aktivitas terkait hanya tersedia untuk penyewa Sandbox saat ini.

  • Peristiwa Langganan Tertunda ("langganan tertunda")

    Peristiwa ini terjadi ketika pesanan yang sesuai telah berhasil diterima dan pembuatan langganan sedang dalam proses.

    Catatan

    Webhook Langganan Tertunda dan Peristiwa Catatan Aktivitas yang terkait hanya tersedia untuk tenant Sandbox saat ini.

  • Peristiwa Perpanjangan Langganan ("perpanjangan langganan")

    Peristiwa ini terjadi ketika langganan selesai diperpanjang.

    Catatan

    Webhook Pembaruan Langganan dan Peristiwa Log Aktivitas yang terkait saat ini hanya tersedia untuk pengguna Sandbox.

  • Peristiwa Pembaruan Langganan ("langganan diperbarui")

    Peristiwa ini dipicu ketika langganan berubah. Peristiwa ini dihasilkan ketika ada perubahan internal selain ketika perubahan dilakukan melalui API Pusat Mitra.

    Catatan

    Ada penundaan hingga 48 jam antara waktu langganan berubah dan ketika event Pembaruan Langganan diaktifkan.

  • Peristiwa Uji Coba ("pembuatan uji coba")

    Acara ini memungkinkan Anda untuk melakukan onboarding secara mandiri dan menguji pendaftaran Anda dengan meminta acara uji coba lalu melacak perkembangannya. Anda dapat melihat pesan kegagalan yang diterima dari Microsoft saat mencoba menyampaikan acara. Pembatasan ini hanya berlaku untuk peristiwa "hasil uji coba". Data yang lebih lama dari tujuh hari dibersihkan.

  • Peristiwa Terlampaui Ambang Batas ("usagerecords-thresholdExceeded")

    Kejadian ini dinaikkan ketika jumlah penggunaan Microsoft Azure untuk setiap pelanggan melebihi anggaran pengeluaran penggunaan mereka (ambang batasnya). Untuk informasi selengkapnya, lihat (Mengatur anggaran pengeluaran Azure untuk pelanggan/pusat mitra/set-an-azure-spending-budget-for-your-customers).

Peristiwa Webhook di masa mendatang akan ditambahkan untuk sumber daya yang mengalami perubahan dalam sistem yang tidak berada di bawah kendali mitra, dan pembaruan lebih lanjut akan dilakukan agar peristiwa tersebut terjadi sedekat mungkin dengan waktu nyata. Umpan balik dari Mitra tentang peristiwa mana yang menambah nilai ke bisnis mereka berguna dalam menentukan peristiwa baru apa yang akan ditambahkan.

Untuk daftar lengkap peristiwa Webhook yang didukung oleh Pusat Mitra, lihat Peristiwa webhook Pusat Mitra.

Prasyarat

  • Kredensial seperti yang dijelaskan dalam Autentikasi Pusat Mitra. Skenario ini mendukung autentikasi dengan kredensial Aplikasi mandiri dan Aplikasi+Pengguna.

Menerima acara dari Pusat Mitra

Untuk menerima event dari Pusat Mitra, Anda harus mengekspos titik akhir yang dapat diakses publik. Karena titik akhir ini terekspos, Anda harus memvalidasi bahwa komunikasi berasal dari Pusat Mitra. Semua event Webhook yang Anda terima ditandatangani secara digital dengan sertifikat yang ditautkan ke Akar Microsoft. Tautan ke sertifikat yang digunakan untuk menandatangani acara juga disediakan. Ini memungkinkan sertifikat diperbarui tanpa Anda harus menyebarkan ulang atau mengonfigurasi ulang layanan Anda. Partner Center mencoba mengirimkan event sebanyak 10 kali. Jika peristiwa masih belum dikirimkan setelah 10 upaya, peristiwa tersebut dipindahkan ke antrean offline dan tidak ada upaya lebih lanjut yang dilakukan saat pengiriman.

Contoh berikut menunjukkan acara yang diunggah dari Pusat Mitra.

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

Catatan

Header Otorisasi memiliki skema "Tanda Tangan". Ini adalah tanda tangan konten yang dikodekan base64.

Cara mengautentikasi panggilan balik

Untuk mengautentikasi peristiwa panggilan balik yang diterima dari Pusat Mitra, ikuti langkah-langkah berikut:

  1. Pastikan header yang diperlukan ada (Authorization, x-ms-certificate-url, x-ms-signature-algorithm).
  2. Unduh sertifikat yang digunakan untuk menandatangani konten (x-ms-certificate-url).
  3. Verifikasi Rantai Sertifikat.
  4. Verifikasi "Organisasi" sertifikat.
  5. Baca konten dengan pengodean UTF8 ke dalam buffer.
  6. Buat Penyedia Kripto RSA.
  7. Verifikasi data cocok dengan apa yang ditandatangani dengan algoritma hash yang ditentukan (misalnya SHA256).
  8. Jika verifikasi berhasil, proses pesan.

Catatan

Secara default, token tanda tangan dikirim di header Otorisasi. Jika Anda mengatur SignatureTokenToMsSignatureHeader ke true dalam pendaftaran Anda, token tanda tangan dikirim di header x-ms-signature sebagai gantinya.

Model peristiwa

Tabel berikut menggambarkan atribut acara di Pusat Mitra.

Sifat

Nama Deskripsi
EventName Nama peristiwa. Dalam format {resource}-{action}. Misalnya, "test-create".
ResourceUri URI sumber daya yang berubah.
ResourceName Nama sumber daya yang berubah.
AuditUrl Opsional. URI dari catatan audit.
ResourceChangeUtcDate Tanggal dan waktu, dalam format UTC, ketika perubahan sumber daya terjadi.

Contoh

Contoh berikut menunjukkan struktur acara Pusat Mitra.

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

API Webhook

Autentikasi

Semua panggilan ke API Webhook diautentikasi menggunakan token Pembawa di Header Otorisasi. Dapatkan token akses untuk mengakses https://api.partnercenter.microsoft.com. Token ini adalah token yang sama yang digunakan untuk mengakses API Pusat Mitra lainnya.

Mendapatkan daftar peristiwa

Mengembalikan daftar peristiwa yang saat ini didukung oleh API Webhook.

URL Sumber Daya

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

Contoh permintaan

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

Contoh tanggapan

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

Daftar untuk menerima peristiwa

Mendaftarkan penyewa untuk menerima peristiwa yang ditentukan.

URL Sumber Daya

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

Contoh permintaan

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

Contoh tanggapan

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

Melihat pendaftaran

Mengembalikan data pendaftaran event Webhooks untuk tenant.

URL Sumber Daya

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

Contoh permintaan

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

Contoh tanggapan

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

Memperbarui pendaftaran acara

Memperbarui pendaftaran acara yang ada.

URL Sumber Daya

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

Contoh permintaan

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

Contoh tanggapan

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

Mengirim peristiwa pengujian untuk memvalidasi pendaftaran Anda

Membuat acara pengujian untuk memvalidasi pendaftaran Webhooks. Pengujian ini dimaksudkan untuk memvalidasi bahwa Anda dapat menerima event dari Partner Center. Data untuk peristiwa ini dihapus tujuh hari setelah peristiwa awal dibuat. Anda harus terdaftar ke dalam acara "test-created" menggunakan API pendaftaran sebelum mengirim acara validasi.

Catatan

Ada batas 2 permintaan per menit saat mengirimkan acara validasi.

URL Sumber Daya

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

Contoh permintaan

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:

Contoh tanggapan

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

Pastikan bahwa acara telah terkirim

Mengembalikan status kejadian validasi saat ini. Verifikasi ini dapat membantu memecahkan masalah penyampaian acara. Respons berisi hasil untuk setiap upaya yang dilakukan untuk mengirimkan peristiwa.

URL Sumber Daya

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

Contoh permintaan

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

Contoh tanggapan

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

Contoh untuk Validasi Tanda Tangan

Contoh tanda tangan Pengontrol Panggilan Balik (ASP.NET)

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

Validasi Tanda Tangan

Contoh berikut menunjukkan cara menambahkan Atribut Otorisasi ke pengontrol yang menerima panggilan balik dari peristiwa Webhook.

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