Skip to content

Commit

Permalink
Merge pull request #162 from DuendeSoftware/joe/mtls-net8
Browse files Browse the repository at this point in the history
Update mTLS to .NET 8
  • Loading branch information
brockallen authored Jan 23, 2024
2 parents b0c9a67 + 24e841a commit 252363a
Show file tree
Hide file tree
Showing 131 changed files with 327 additions and 198 deletions.
2 changes: 2 additions & 0 deletions IdentityServer/v7/Basics/IdentityServer/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class Program
{
public static int Main(string[] args)
{
Console.Title = "IdentityServer";

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
Expand Down
53 changes: 53 additions & 0 deletions IdentityServer/v7/MTLS/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"version": "0.2.0",
"compounds": [
{
"name": "Run All",
"configurations": ["IdentityServerHost", "Api", "ClientCredentials"],
"presentation": {
"group": "10-compunds",
}
}
],
"configurations": [
{
"name": "IdentityServerHost",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-identityserverhost",
"program": "${workspaceFolder}/IdentityServerHost/bin/Debug/net8.0/IdentityServerHost.dll",
"args": [],
"cwd": "${workspaceFolder}/IdentityServerHost",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"console": "externalTerminal",
},
{
"name": "Api",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-api",
"program": "${workspaceFolder}/Api/bin/Debug/net8.0/Api.dll",
"args": [],
"cwd": "${workspaceFolder}/Api/",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"console": "externalTerminal",
},
{
"name": "ClientCredentials",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-clientcredentials",
"program": "${workspaceFolder}/ClientCredentials/bin/Debug/net8.0/ClientCredentials.dll",
"args": [],
"cwd": "${workspaceFolder}/ClientCredentials",
"console": "externalTerminal",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
}
]
}
53 changes: 53 additions & 0 deletions IdentityServer/v7/MTLS/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/MTLS.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-identityserverhost",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/IdentityServerHost",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-api",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/Api",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-clientcredentials",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/ClientCredentials",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
7 changes: 3 additions & 4 deletions IdentityServer/v7/MTLS/Api/Api.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />

<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
</ItemGroup>

</Project>
86 changes: 42 additions & 44 deletions IdentityServer/v7/MTLS/Api/ConfirmationValidationMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,66 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json.Linq;
using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace SampleApi
namespace Api;

public static class ConfirmationValidationExtensions
{
public static class ConfirmationValidationExtensions
public static IApplicationBuilder UseConfirmationValidation(this IApplicationBuilder app, ConfirmationValidationMiddlewareOptions options = default)
{
public static IApplicationBuilder UseConfirmationValidation(this IApplicationBuilder app, ConfirmationValidationMiddlewareOptions options = default)
{
return app.UseMiddleware<ConfirmationValidationMiddleware>(options ?? new ConfirmationValidationMiddlewareOptions());
}
return app.UseMiddleware<ConfirmationValidationMiddleware>(options ?? new ConfirmationValidationMiddlewareOptions());
}
}

public class ConfirmationValidationMiddlewareOptions
{
public string JwtBearerSchemeName { get; set; } = JwtBearerDefaults.AuthenticationScheme;
}

public class ConfirmationValidationMiddlewareOptions
// this middleware validate the cnf claim (if present) against the thumbprint of the X.509 client certificate for the current client
public class ConfirmationValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ConfirmationValidationMiddlewareOptions _options;

public ConfirmationValidationMiddleware(RequestDelegate next, ILogger<ConfirmationValidationMiddlewareOptions> logger, ConfirmationValidationMiddlewareOptions options = null)
{
public string JwtBearerSchemeName { get; set; } = JwtBearerDefaults.AuthenticationScheme;
_next = next;
_logger = logger;
_options ??= new ConfirmationValidationMiddlewareOptions();
}

// this middleware validate the cnf claim (if present) against the thumbprint of the X.509 client certificate for the current client
public class ConfirmationValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ConfirmationValidationMiddlewareOptions _options;

public ConfirmationValidationMiddleware(RequestDelegate next, ILogger<ConfirmationValidationMiddlewareOptions> logger, ConfirmationValidationMiddlewareOptions options = null)
{
_next = next;
_logger = logger;
_options ??= new ConfirmationValidationMiddlewareOptions();
}

public async Task Invoke(HttpContext ctx)
public async Task Invoke(HttpContext ctx)
{
if (ctx.User.Identity.IsAuthenticated)
{
if (ctx.User.Identity.IsAuthenticated)
var cnfJson = ctx.User.FindFirst("cnf")?.Value;
if (!String.IsNullOrWhiteSpace(cnfJson))
{
var cnfJson = ctx.User.FindFirst("cnf")?.Value;
if (!String.IsNullOrWhiteSpace(cnfJson))
{
var certificate = await ctx.Connection.GetClientCertificateAsync();
var thumbprint = Base64UrlTextEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256));

var cnf = JObject.Parse(cnfJson);
var sha256 = cnf.Value<string>("x5t#S256");
var certificate = await ctx.Connection.GetClientCertificateAsync();
var thumbprint = Base64UrlTextEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256));

if (String.IsNullOrWhiteSpace(sha256) ||
!thumbprint.Equals(sha256, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("certificate thumbprint does not match cnf claim.");
await ctx.ChallengeAsync(_options.JwtBearerSchemeName);
return;
}

_logger.LogDebug("certificate thumbprint matches cnf claim.");
var sha256 = JsonDocument.Parse(cnfJson).RootElement.GetString("x5t#S256");

if (String.IsNullOrWhiteSpace(sha256) ||
!thumbprint.Equals(sha256, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("certificate thumbprint does not match cnf claim.");
await ctx.ChallengeAsync(_options.JwtBearerSchemeName);
return;
}

_logger.LogDebug("certificate thumbprint matches cnf claim.");
}

await _next(ctx);
}

await _next(ctx);
}
}
33 changes: 16 additions & 17 deletions IdentityServer/v7/MTLS/Api/IdentityController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@
using Microsoft.Extensions.Logging;
using System.Linq;

namespace SampleApi.Controllers
namespace Api.Controllers;

[Route("identity")]
public class IdentityController : ControllerBase
{
[Route("identity")]
public class IdentityController : ControllerBase
{
private readonly ILogger<IdentityController> _logger;
private readonly ILogger<IdentityController> _logger;

public IdentityController(ILogger<IdentityController> logger)
{
_logger = logger;
}
public IdentityController(ILogger<IdentityController> logger)
{
_logger = logger;
}

// this action simply echoes the claims back to the client
[HttpGet]
public ActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value });
_logger.LogInformation("claims: {claims}", claims);
// this action simply echoes the claims back to the client
[HttpGet]
public ActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value });
_logger.LogInformation("claims: {claims}", claims);

return new JsonResult(claims);
}
return new JsonResult(claims);
}
}
44 changes: 22 additions & 22 deletions IdentityServer/v7/MTLS/Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;

namespace SampleApi
namespace Api;

public class Program
{
public class Program
public static void Main(string[] args)
{
public static void Main(string[] args)
{
Console.Title = "Sample API";
Console.Title = "API";

BuildWebHost(args).Run();
}
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();
public static IHost BuildWebHost(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();

return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog()
.Build();
}
}
Loading

0 comments on commit 252363a

Please sign in to comment.