diff --git a/.gitignore b/.gitignore index 18539b1..2409aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /examplesUsingCertificateAuthentication/AspNetCoreChained/_chainedServerLogs.txt /examplesUsingCertificateAuthentication/AspNetCoreChained/_clientLogs.txt /examplesUsingCertificateAuthentication/AzureCertAuth/certauth.txt +**/_logs-* diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/AzureCertAuth.csproj b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/AzureCertAuth.csproj index 5b5ea5d..92b36df 100644 --- a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/AzureCertAuth.csproj +++ b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/AzureCertAuth.csproj @@ -7,20 +7,15 @@ - - - - - - - - - + + + - - - PreserveNewest - + + + + + diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Program.cs b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Program.cs index 40c71ac..c0e920c 100644 --- a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Program.cs +++ b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Program.cs @@ -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(); - }); -} +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(); +} \ No newline at end of file diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Properties/launchSettings.json b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Properties/launchSettings.json index 1483d57..79833e0 100644 --- a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Properties/launchSettings.json +++ b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Properties/launchSettings.json @@ -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" } diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Startup.cs b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/StartupExtensions.cs similarity index 60% rename from examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Startup.cs rename to examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/StartupExtensions.cs index 21f19b0..ba666fd 100644 --- a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/Startup.cs +++ b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/StartupExtensions.cs @@ -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(); - - 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(); - - 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(); + + 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(); + + 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(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; + } +} \ No newline at end of file diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/server.pfx b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/server.pfx deleted file mode 100644 index 0a16db7..0000000 Binary files a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuth/server.pfx and /dev/null differ diff --git a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuthClientConsole/Program.cs b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuthClientConsole/Program.cs index c6ff016..be75d95 100644 --- a/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuthClientConsole/Program.cs +++ b/examplesUsingCertificateAuthentication/AzureCertAuth/AzureCertAuthClientConsole/Program.cs @@ -22,7 +22,7 @@ private static async Task 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() {