File: CertificateForwardingTest.cs
Web Access
Project: src\src\Middleware\HttpOverrides\test\Microsoft.AspNetCore.HttpOverrides.Tests.csproj (Microsoft.AspNetCore.HttpOverrides.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
 
namespace Microsoft.AspNetCore.HttpOverrides;
 
public class CertificateForwardingTests
{
    [Fact]
    public void VerifySettingNullHeaderOptionThrows()
    {
        var services = new ServiceCollection()
            .AddOptions()
            .AddCertificateForwarding(o => o.CertificateHeader = null);
        var options = services.BuildServiceProvider().GetRequiredService<IOptions<CertificateForwardingOptions>>();
        Assert.Throws<OptionsValidationException>(() => options.Value);
    }
 
    [Fact]
    public void VerifySettingEmptyHeaderOptionThrows()
    {
        var services = new ServiceCollection()
            .AddOptions()
            .AddCertificateForwarding(o => o.CertificateHeader = "");
        var options = services.BuildServiceProvider().GetRequiredService<IOptions<CertificateForwardingOptions>>();
        Assert.Throws<OptionsValidationException>(() => options.Value);
    }
 
    [Fact]
    public async Task VerifyHeaderIsUsedIfNoCertificateAlreadySet()
    {
        using var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddCertificateForwarding(options => { });
                })
                .Configure(app =>
                {
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                    app.UseCertificateForwarding();
                    app.Use(async (context, next) =>
                    {
                        Assert.Equal(context.Connection.ClientCertificate, Certificates.SelfSignedValidWithNoEku);
                        await next(context);
                    });
                });
            }).Build();
 
        await host.StartAsync();
 
        var server = host.GetTestServer();
 
        var context = await server.SendAsync(c =>
        {
            c.Request.Headers["X-Client-Cert"] = Convert.ToBase64String(Certificates.SelfSignedValidWithNoEku.RawData);
        });
    }
 
    [Fact]
    public async Task VerifyHeaderOverridesCertificateEvenAlreadySet()
    {
        using var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddCertificateForwarding(options => { });
                })
                .Configure(app =>
                {
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        context.Connection.ClientCertificate = Certificates.SelfSignedNotYetValid;
                        await next(context);
                    });
                    app.UseCertificateForwarding();
                    app.Use(async (context, next) =>
                    {
                        Assert.Equal(context.Connection.ClientCertificate, Certificates.SelfSignedValidWithNoEku);
                        await next(context);
                    });
                });
            }).Build();
 
        await host.StartAsync();
 
        var server = host.GetTestServer();
 
        var context = await server.SendAsync(c =>
        {
            c.Request.Headers["X-Client-Cert"] = Convert.ToBase64String(Certificates.SelfSignedValidWithNoEku.RawData);
        });
    }
 
    [Fact]
    public async Task VerifySettingTheAzureHeaderOnTheForwarderOptionsWorks()
    {
        using var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddCertificateForwarding(options => options.CertificateHeader = "X-ARR-ClientCert");
                })
                .Configure(app =>
                {
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                    app.UseCertificateForwarding();
                    app.Use(async (context, next) =>
                    {
                        Assert.Equal(context.Connection.ClientCertificate, Certificates.SelfSignedValidWithNoEku);
                        await next(context);
                    });
                });
            }).Build();
 
        await host.StartAsync();
 
        var server = host.GetTestServer();
 
        var context = await server.SendAsync(c =>
        {
            c.Request.Headers["X-ARR-ClientCert"] = Convert.ToBase64String(Certificates.SelfSignedValidWithNoEku.RawData);
        });
    }
 
    [Fact]
    public async Task VerifyACustomHeaderFailsIfTheHeaderIsNotPresent()
    {
        using var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddCertificateForwarding(options => options.CertificateHeader = "some-random-header");
                })
                .Configure(app =>
                {
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                    app.UseCertificateForwarding();
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                });
            }).Build();
 
        await host.StartAsync();
 
        var server = host.GetTestServer();
 
        var context = await server.SendAsync(c =>
        {
            c.Request.Headers["not-the-right-header"] = Convert.ToBase64String(Certificates.SelfSignedValidWithNoEku.RawData);
        });
    }
 
    [Fact]
    public async Task VerifyArrHeaderEncodedCertFailsOnBadEncoding()
    {
        using var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddCertificateForwarding(options => { });
                })
                .Configure(app =>
                {
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                    app.UseCertificateForwarding();
                    app.Use(async (context, next) =>
                    {
                        Assert.Null(context.Connection.ClientCertificate);
                        await next(context);
                    });
                });
            }).Build();
 
        await host.StartAsync();
 
        var server = host.GetTestServer();
 
        var context = await server.SendAsync(c =>
        {
            c.Request.Headers["X-Client-Cert"] = "OOPS" + Convert.ToBase64String(Certificates.SelfSignedValidWithNoEku.RawData);
        });
    }
}