Bypass DTO binding - parse JSON manually in endpoints
eb50ce18e0c3e28099eab6a8e9c7d160d313443c
ASP.NET Core minimal API model binding was causing issues with
source generators. Now parsing JSON with JsonNode directly.
diff --git a/src/Forge.Web/Program.cs b/src/Forge.Web/Program.csindex affc52f..d5dbbdc 100644--- a/src/Forge.Web/Program.cs+++ b/src/Forge.Web/Program.cs@@ -183,8 +183,7 @@ app.MapPost("/auth/passkey/register/start", async ( app.MapPost("/auth/passkey/register/complete", async ( HttpContext context, [FromServices] IFido2Service fido2Service,- [FromServices] IAuthService authService,- [FromBody] PasskeyRegistrationRequest request) =>+ [FromServices] IAuthService authService) => { var username = authService.GetConfiguredUsername(); if (string.IsNullOrEmpty(username))@@ -194,21 +193,27 @@ app.MapPost("/auth/passkey/register/complete", async ( try {+ // Parse JSON manually to avoid base64url conversion issues+ using var reader = new StreamReader(context.Request.Body);+ var json = await reader.ReadToEndAsync();+ var node = System.Text.Json.Nodes.JsonNode.Parse(json);+ var response = new Fido2NetLib.AuthenticatorAttestationRawResponse {- Id = request.Id,- RawId = Base64Url.Decode(request.rawId),+ Id = node!["id"]!.GetValue<string>(),+ RawId = Base64Url.Decode(node["rawId"]!.GetValue<string>()), Type = Fido2NetLib.Objects.PublicKeyCredentialType.PublicKey, Response = new Fido2NetLib.AuthenticatorAttestationRawResponse.AttestationResponse {- ClientDataJson = Base64Url.Decode(request.Response.clientDataJSON),- AttestationObject = Base64Url.Decode(request.Response.attestationObject),+ ClientDataJson = Base64Url.Decode(node["response"]!["clientDataJSON"]!.GetValue<string>()),+ AttestationObject = Base64Url.Decode(node["response"]!["attestationObject"]!.GetValue<string>()), Transports = [] },- ClientExtensionResults = request.ClientExtensionResults ?? new Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs()+ ClientExtensionResults = new Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs() }; - var credential = await fido2Service.CompleteRegistrationAsync(username, response, request.DeviceName);+ var deviceName = node["deviceName"]?.GetValue<string>();+ var credential = await fido2Service.CompleteRegistrationAsync(username, response, deviceName); return Results.Json(new { success = true, credentialId = credential.Id }); } catch (Exception ex)@@ -227,24 +232,28 @@ app.MapGet("/auth/passkey/authenticate/start", async ( app.MapPost("/auth/passkey/authenticate/complete", async ( HttpContext context, [FromServices] IFido2Service fido2Service,- [FromServices] IAuthService authService,- [FromBody] PasskeyAuthenticationRequest request) =>+ [FromServices] IAuthService authService) => { try {+ // Parse JSON manually to avoid base64url conversion issues+ using var reader = new StreamReader(context.Request.Body);+ var json = await reader.ReadToEndAsync();+ var node = System.Text.Json.Nodes.JsonNode.Parse(json);+ var response = new Fido2NetLib.AuthenticatorAssertionRawResponse {- Id = request.Id,- RawId = Base64Url.Decode(request.rawId),+ Id = node!["id"]!.GetValue<string>(),+ RawId = Base64Url.Decode(node["rawId"]!.GetValue<string>()), Type = Fido2NetLib.Objects.PublicKeyCredentialType.PublicKey, Response = new Fido2NetLib.AuthenticatorAssertionRawResponse.AssertionResponse {- ClientDataJson = Base64Url.Decode(request.Response.clientDataJSON),- AuthenticatorData = Base64Url.Decode(request.Response.authenticatorData),- Signature = Base64Url.Decode(request.Response.signature),- UserHandle = request.Response.userHandle != null ? Base64Url.Decode(request.Response.userHandle) : null+ ClientDataJson = Base64Url.Decode(node["response"]!["clientDataJSON"]!.GetValue<string>()),+ AuthenticatorData = Base64Url.Decode(node["response"]!["authenticatorData"]!.GetValue<string>()),+ Signature = Base64Url.Decode(node["response"]!["signature"]!.GetValue<string>()),+ UserHandle = node["response"]?["userHandle"]?.GetValue<string>() is string uh ? Base64Url.Decode(uh) : null },- ClientExtensionResults = request.ClientExtensionResults ?? new Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs()+ ClientExtensionResults = new Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs() }; var username = await fido2Service.CompleteAuthenticationAsync(response);@@ -317,32 +326,6 @@ app.MapMethods("/{owner}/{repo}.git/{**rest}", new[] { "GET", "POST" }, async (H app.Run(); -// DTOs for passkey requests-public record PasskeyRegistrationRequest(- string Id,- string rawId,- string Type,- PasskeyRegistrationResponse Response,- string? DeviceName,- Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs? ClientExtensionResults);--public record PasskeyRegistrationResponse(- string clientDataJSON,- string attestationObject);--public record PasskeyAuthenticationRequest(- string Id,- string rawId,- string Type,- PasskeyAuthenticationResponse Response,- Fido2NetLib.Objects.AuthenticationExtensionsClientOutputs? ClientExtensionResults);--public record PasskeyAuthenticationResponse(- string clientDataJSON,- string authenticatorData,- string signature,- string? userHandle);- file static class Base64Url { public static byte[] Decode(string s) =>