Skip to content

Commit

Permalink
Update to .NET 8
Browse files Browse the repository at this point in the history
  • Loading branch information
damienbod committed Oct 31, 2024
1 parent 92a67f7 commit de0d4e7
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 188 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/examplesUsingCertificateAuthentication/AspNetCoreChained/_chainedServerLogs.txt
/examplesUsingCertificateAuthentication/AspNetCoreChained/_clientLogs.txt
/examplesUsingCertificateAuthentication/AzureCertAuth/certauth.txt
**/_logs-*
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.AzureAppServices.HostingStartup" Version="6.0.8" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="8.0.10" />
<PackageReference Include="Microsoft.AspNetCore.AzureAppServices.HostingStartup" Version="8.0.10" />

<ItemGroup>
<None Update="server.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" NoWarn="NU1605" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.10" NoWarn="NU1605" />

<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,54 +1,34 @@
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;

namespace AzureCertAuth;

public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
.Enrich.FromLogContext()
.CreateLogger();

try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.MinimumLevel.Override("Microsoft", LogEventLevel.Verbose)
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.WriteTo.File(
//$@"../certauth.txt",
$@"D:\home\LogFiles\Application\{Environment.UserDomainName}.txt",
fileSizeLimitBytes: 1_000_000,
rollOnFileSizeLimit: true,
shared: true,
flushToDiskInterval: TimeSpan.FromSeconds(1))
.WriteTo.Console(theme: AnsiConsoleTheme.Code)
)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
using AzureCertAuth;
using Serilog;

Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();

Log.Information("Starting up OpeniddictServer");

try
{
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, loggerConfiguration) => loggerConfiguration
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}")
.WriteTo.File("../_logs-DownstreamApiCertAuth.txt")
.Enrich.FromLogContext()
.ReadFrom.Configuration(context.Configuration));

var app = builder
.ConfigureServices()
.ConfigurePipeline();

app.Run();
}
catch (Exception ex) when (ex.GetType().Name is not "StopTheHostException" && ex.GetType().Name is not "HostAbortedException")
{
Log.Fatal(ex, "Unhandled exception");
}
finally
{
Log.Information("Shut down complete");
Log.CloseAndFlush();
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44361",
"sslPort": 44361
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"AzureCertAuth": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"applicationUrl": "https://localhost:5009",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,103 +1,130 @@
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Authentication.Certificate;

namespace AzureCertAuth;

public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<MyCertificateValidationService>();

services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-ARR-ClientCert";
options.HeaderConverter = (headerValue) =>
{

X509Certificate2? clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
byte[] bytes = Convert.FromBase64String(headerValue);
clientCertificate = new X509Certificate2(bytes);
}

return clientCertificate;
};
});

services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options => // code from ASP.NET Core sample
{
// https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth
options.AllowedCertificateTypes = CertificateTypes.SelfSigned;

// Default values
//options.AllowedCertificateTypes = CertificateTypes.Chained;
//options.RevocationFlag = X509RevocationFlag.ExcludeRoot;
//options.RevocationMode = X509RevocationMode.Online;
//options.ValidateCertificateUse = true;
//options.ValidateValidityPeriod = true;

options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService =
context.HttpContext.RequestServices.GetService<MyCertificateValidationService>();

if (validationService!.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};

context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
else
{
context.Fail("invalid cert");
}

return Task.CompletedTask;
}
};
});

services.AddAuthorization();
services.AddControllers();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();

app.UseRouting();

//app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Serilog;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;

namespace AzureCertAuth;

internal static class StartupExtensions
{
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
var services = builder.Services;
var configuration = builder.Configuration;

if(builder.Environment.IsDevelopment())
{
builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
serverOptions.ConfigureHttpsDefaults(listenOptions =>
{
listenOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
listenOptions.AllowAnyClientCertificate();
});
});
}

services.AddSingleton<MyCertificateValidationService>();

services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-ARR-ClientCert";
options.HeaderConverter = (headerValue) =>
{
Console.WriteLine("headerValue: " + headerValue);

X509Certificate2? clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
byte[] bytes = Convert.FromBase64String(headerValue);
clientCertificate = new X509Certificate2(bytes);
}

return clientCertificate!;
};
});

services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options => // code from ASP.NET Core sample
{
// https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth
options.AllowedCertificateTypes = CertificateTypes.All;

// Default values
//options.AllowedCertificateTypes = CertificateTypes.Chained;
//options.RevocationFlag = X509RevocationFlag.ExcludeRoot;
options.RevocationMode = X509RevocationMode.NoCheck;
options.ValidateCertificateUse = false;
options.ValidateValidityPeriod = false;

options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService =
context.HttpContext.RequestServices.GetService<MyCertificateValidationService>();

if (validationService!.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};

context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
else
{
context.Fail("invalid cert");
}

return Task.CompletedTask;
},
OnAuthenticationFailed = new Func<CertificateAuthenticationFailedContext, Task>(context =>
{
Console.WriteLine("OnAuthenticationFailed: " + context.Exception.Message);
return Task.CompletedTask;
})
};
});

services.AddAuthorization();
services.AddControllers();

return builder.Build();
}

public static WebApplication ConfigurePipeline(this WebApplication app)
{
IdentityModelEventSource.ShowPII = true;
JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();

app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();

app.UseRouting();

if (app.Environment.IsDevelopment())
{
app.UseCertificateForwarding();
}

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

return app;
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private static async Task<JsonDocument> GetApiDataUsingHttpClientHandler()
handler.ClientCertificates.Add(cert);
var client = new HttpClient(handler);

var url = "https://localhost:44361/WeatherForecast";
var url = "https://localhost:5009/WeatherForecast";
//var url = "https://azurecertauth20201108214641.azurewebsites.net/WeatherForecast";
var request = new HttpRequestMessage()
{
Expand Down

0 comments on commit de0d4e7

Please sign in to comment.