passwordless-lib / fido2-net-lib

FIDO2 .NET library for FIDO2 / WebAuthn Attestation and Assertion using .NET
https://fido2-net-lib.passwordless.dev/
MIT License
1.14k stars 165 forks source link

Blazor WASM .net 7.0.11 build with Fido2 release 3.0.1 #446

Open s21-himesh opened 10 months ago

s21-himesh commented 10 months ago

Following code, client side, throwing exception.

Code

@page "/" @using System.Text.Json; @using Fido2NetLib;

Webauthn Recreate Issue

<button class="btn btn-primary" @onclick="test">Click me to recreate

@exception

@code { string jsonData = "{\"rp\":{\"id\":\"test.azurewebsites.net\",\"name\":\"WebAuthn\",\"icon\":\"https://test.azurewebsites.net/\"},\"user\":{\"name\":\"driver@driver.com\",\"id\":\"ZHJpdmVyQGRyaXZlci5jb20\",\"displayName\":\"driver@driver.com\"},\"challenge\":\"QHcEA7xeJRx3_SZV4uoFQQ\",\"pubKeyCredParams\":[{\"type\":\"public-key\",\"alg\":-7},{\"type\":\"public-key\",\"alg\":-257},{\"type\":\"public-key\",\"alg\":-37},{\"type\":\"public-key\",\"alg\":-35},{\"type\":\"public-key\",\"alg\":-258},{\"type\":\"public-key\",\"alg\":-38},{\"type\":\"public-key\",\"alg\":-36},{\"type\":\"public-key\",\"alg\":-259},{\"type\":\"public-key\",\"alg\":-39},{\"type\":\"public-key\",\"alg\":-8}],\"timeout\":60000,\"attestation\":\"direct\",\"authenticatorSelection\":{\"authenticatorAttachment\":\"platform\",\"requireResidentKey\":true,\"userVerification\":\"required\"},\"excludeCredentials\":[],\"extensions\":{\"uvm\":true},\"status\":\"ok\",\"errorMessage\":\"\"}"; string exception = string.Empty;

private void test()
{
    try
    {
        var deviceResponse = CredentialCreateOptions.FromJson(jsonData);            
        exception = $"worked as expected {JsonSerializer.Serialize(deviceResponse)}";
    }
    catch (Exception ex)
    {
        exception = $"failure {ex.ToString()}";
    }       
}

}

Exception being thrown:-

System.NotSupportedException: ConstructorContainsNullParameterNames, Fido2NetLib.PublicKeyCredentialRpEntity Path: $.Rp. ---> System.NotSupportedException: ConstructorContainsNullParameterNames, Fido2NetLib.PublicKeyCredentialRpEntity at System.Text.Json.ThrowHelper.ThrowNotSupportedException_ConstructorContainsNullParameterNames(Type ) at System.Text.Json.Serialization.Metadata.ReflectionJsonTypeInfo1[[Fido2NetLib.PublicKeyCredentialRpEntity, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].GetParameterInfoValues() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.g__ConfigureLocked|143_0() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.EnsureConfigured() at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.get_JsonTypeInfo() at System.Text.Json.WriteStack.PeekNestedJsonTypeInfo() at System.Text.Json.Serialization.JsonConverter1[[Fido2NetLib.PublicKeyCredentialRpEntity, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].TryWrite(Utf8JsonWriter , PublicKeyCredentialRpEntity& , JsonSerializerOptions , WriteStack& ) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1[[Fido2NetLib.PublicKeyCredentialRpEntity, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].GetMemberAndWriteJson(Object , WriteStack& , Utf8JsonWriter ) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1[[Fido2NetLib.CredentialCreateOptions, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].OnTryWrite(Utf8JsonWriter , CredentialCreateOptions , JsonSerializerOptions , WriteStack& ) at System.Text.Json.Serialization.JsonConverter1[[Fido2NetLib.CredentialCreateOptions, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].TryWrite(Utf8JsonWriter , CredentialCreateOptions& , JsonSerializerOptions , WriteStack& ) at System.Text.Json.Serialization.JsonConverter1[[Fido2NetLib.CredentialCreateOptions, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].WriteCore(Utf8JsonWriter , CredentialCreateOptions& , JsonSerializerOptions , WriteStack& ) Exception_EndOfInnerExceptionStack at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& , NotSupportedException ) at System.Text.Json.Serialization.JsonConverter1[[Fido2NetLib.CredentialCreateOptions, Fido2.Models, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]].WriteCore(Utf8JsonWriter , CredentialCreateOptions& , JsonSerializerOptions , WriteStack& ) at System.Text.Json.JsonSerializer.WriteCore[CredentialCreateOptions](Utf8JsonWriter , CredentialCreateOptions& , JsonTypeInfo1 ) at System.Text.Json.JsonSerializer.WriteString[CredentialCreateOptions](CredentialCreateOptions& , JsonTypeInfo1 ) at System.Text.Json.JsonSerializer.Serialize[CredentialCreateOptions](CredentialCreateOptions , JsonSerializerOptions ) at WebAuthnIssue.Client.Pages.Index.test() `

Does not occur locally and only on azure app service. App service is configured for .net 7.

Any ideas how this can be resolved?

s21-himesh commented 10 months ago

I believe this is directly related to #380

s21-himesh commented 10 months ago

Reading the various posts I believe the following should work. Again works locally but not on app service:

@page "/" @using System.Text.Json; @using Fido2NetLib;

Webauthn Recreate Issue

<button class="btn btn-primary" @onclick="test">Click me to recreate

@exception

@code { private readonly JsonSerializerOptions _jsonOptions = new FidoBlazorSerializerContext().Options; string jsonData = "{\"rp\":{\"id\":\"test.azurewebsites.net\",\"name\":\"WebAuthn\",\"icon\":\"https://test.azurewebsites.net/\"},\"user\":{\"name\":\"driver@driver.com\",\"id\":\"ZHJpdmVyQGRyaXZlci5jb20\",\"displayName\":\"driver@driver.com\"},\"challenge\":\"QHcEA7xeJRx3_SZV4uoFQQ\",\"pubKeyCredParams\":[{\"type\":\"public-key\",\"alg\":-7},{\"type\":\"public-key\",\"alg\":-257},{\"type\":\"public-key\",\"alg\":-37},{\"type\":\"public-key\",\"alg\":-35},{\"type\":\"public-key\",\"alg\":-258},{\"type\":\"public-key\",\"alg\":-38},{\"type\":\"public-key\",\"alg\":-36},{\"type\":\"public-key\",\"alg\":-259},{\"type\":\"public-key\",\"alg\":-39},{\"type\":\"public-key\",\"alg\":-8}],\"timeout\":60000,\"attestation\":\"direct\",\"authenticatorSelection\":{\"authenticatorAttachment\":\"platform\",\"requireResidentKey\":true,\"userVerification\":\"required\"},\"excludeCredentials\":[],\"extensions\":{\"uvm\":true},\"status\":\"ok\",\"errorMessage\":\"\"}"; string exception = string.Empty;

private void test()
{
    try
    {
        var deviceResponse = JsonSerializer.Deserialize<CredentialCreateOptions>(jsonData, _jsonOptions);            
        exception = $"worked as expected {JsonSerializer.Serialize(deviceResponse)}";
    }
    catch (Exception ex)
    {
        exception = $"failure {ex.ToString()}";
    }       
}

}

joegoldman2 commented 10 months ago

I am unable to reproduce the issue with .NET 7 and the package Fido2 3.0.1 or 4.0.0-beta4

using Fido2NetLib;
using System.Text.Json;
using System.Text.Json.Serialization;

string json = "{\"rp\":{\"id\":\"test.azurewebsites.net\",\"name\":\"WebAuthn\",\"icon\":\"https://test.azurewebsites.net/\"},\"user\":{\"name\":\"driver@driver.com\",\"id\":\"ZHJpdmVyQGRyaXZlci5jb20\",\"displayName\":\"driver@driver.com\"},\"challenge\":\"QHcEA7xeJRx3_SZV4uoFQQ\",\"pubKeyCredParams\":[{\"type\":\"public-key\",\"alg\":-7},{\"type\":\"public-key\",\"alg\":-257},{\"type\":\"public-key\",\"alg\":-37},{\"type\":\"public-key\",\"alg\":-35},{\"type\":\"public-key\",\"alg\":-258},{\"type\":\"public-key\",\"alg\":-38},{\"type\":\"public-key\",\"alg\":-36},{\"type\":\"public-key\",\"alg\":-259},{\"type\":\"public-key\",\"alg\":-39},{\"type\":\"public-key\",\"alg\":-8}],\"timeout\":60000,\"attestation\":\"direct\",\"authenticatorSelection\":{\"authenticatorAttachment\":\"platform\",\"requireResidentKey\":true,\"userVerification\":\"required\"},\"excludeCredentials\":[],\"extensions\":{\"uvm\":true},\"status\":\"ok\",\"errorMessage\":\"\"}";
var options = CredentialCreateOptions.FromJson(json);
var options2 = JsonSerializer.Deserialize<CredentialCreateOptions>(json, new FidoBlazorSerializerContext().Options);

[JsonSerializable(typeof(AssertionOptions))]
[JsonSerializable(typeof(AuthenticatorAssertionRawResponse))]
[JsonSerializable(typeof(AuthenticatorAttestationRawResponse))]
[JsonSerializable(typeof(CredentialCreateOptions))]
public partial class FidoBlazorSerializerContext : JsonSerializerContext
{
}

Are you able to commit a minimal repro and give the output of dotnet --info?

Yomist commented 10 months ago

@joegoldman2 did you try deploying on the app service? where do I specify the runtime I am getting unhandled exception from loading type 'System.TypeLoadException' occurred in Unknown Module. Could not load type 'System.Object' from assembly 'System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' because the parent does not exist.

image

s21-himesh commented 10 months ago

Hi @joegoldman2 @Yomist

I've prepared a repo https://github.com/s21-himesh/WebAuthnIssue

I've pushed to an app service/ web app https://webauthnfido.azurewebsites.net/ so you can see the exception.

Let me know, if you need anything else.

s21-himesh commented 10 months ago

Deployed to web app and the same result

`
private readonly static FidoBlazorSerializerContext context = new FidoBlazorSerializerContext(); string jsonData = "{\"rp\":{\"id\":\"test.azurewebsites.net\",\"name\":\"WebAuthn\",\"icon\":\"https://test.azurewebsites.net/\"},\"user\":{\"name\":\"driver@driver.com\",\"id\":\"ZHJpdmVyQGRyaXZlci5jb20\",\"displayName\":\"driver@driver.com\"},\"challenge\":\"QHcEA7xeJRx3_SZV4uoFQQ\",\"pubKeyCredParams\":[{\"type\":\"public-key\",\"alg\":-7},{\"type\":\"public-key\",\"alg\":-257},{\"type\":\"public-key\",\"alg\":-37},{\"type\":\"public-key\",\"alg\":-35},{\"type\":\"public-key\",\"alg\":-258},{\"type\":\"public-key\",\"alg\":-38},{\"type\":\"public-key\",\"alg\":-36},{\"type\":\"public-key\",\"alg\":-259},{\"type\":\"public-key\",\"alg\":-39},{\"type\":\"public-key\",\"alg\":-8}],\"timeout\":60000,\"attestation\":\"direct\",\"authenticatorSelection\":{\"authenticatorAttachment\":\"platform\",\"requireResidentKey\":true,\"userVerification\":\"required\"},\"excludeCredentials\":[],\"extensions\":{\"uvm\":true},\"status\":\"ok\",\"errorMessage\":\"\"}"; string exception = string.Empty;

private void test()
{
    try
    {
        var deviceResponse = JsonSerializer.Deserialize(jsonData, typeof(CredentialCreateOptions), context);
        exception = $"worked as expected {JsonSerializer.Serialize(deviceResponse)}";
    }
    catch (Exception ex)
    {
        exception = $"failure {ex.ToString()} | {jsonData}";
    }       
}

`

joegoldman2 commented 10 months ago

This issue looks similar to https://github.com/dotnet/runtime/issues/81709.

iamcarbon commented 10 months ago

@s21-himesh Will you be able to update to .NET 8.0 once it's released? This will likely be fixed once we cross-target .NET8.0 (soon).

s21-himesh commented 10 months ago

@iamcarbon ideally not as we have a released prepared and this is the last outstanding issue. Is there any workaround for this? Can anyone explain why it works local vs app service?

s21-himesh commented 10 months ago

@Regenhardt Hi Marlon, you appear to have the same error message I'm experiencing on this ticket 81709. Can you assist with explaining how this can be resolved?

Regenhardt commented 10 months ago

This can be solved using the Fido2.BlazorWebassembly lib in the frontend. It's not on NuGet yet, but you can just copy the code (or copy the whole project to your own solution, or add as submodule) and it should work. You can check the BlazorWasmDemo for an example, I believe the Program.cs has the needed setup and the UserService the actual usage.

s21-himesh commented 10 months ago

@Regenhardt gave that a try today. Can confirm the same result where it works locally but not in the cloud. To confirm I added BlazorWebAssembly and Fido2.Models to test the concept. I removed the nuget package.

To confirm, the build is .net 7 blazor wasm.

Regenhardt commented 10 months ago

To test a suspicion, locally build your app in release mode. This is what triggered the error for me: Building in release mode stripped the assembly of things it didn't understand I needed.

This was fixed by using the specific SerializerContext to tell the serializer what it needs to know. If you use the source directly, this will even use source generation to generate the serializer code, speeding up serialization.

Off topic: Love the loading circle on your demo app, does it come with the template now or do I have to steal it myself?

Also I love how we built basically the same thing to fix this, mine is still broken over there lol: https://blazor-webauthn.onrender.com/

Regenhardt commented 10 months ago

As far as I can see (assuming the jsonData is correct there), your deserialisation looks ok there. I can check out the code later and try to see if there's anything else I can find.

Regenhardt commented 10 months ago

I believe this will be fixed when either the Blazor lib is released as NuGet package, or all the libs are updated or multi-targeted to net7.0 for newer apps.

The models are packaged for net6.0, losing "unneeded" metadata. Here, the needed metadata is in the saved FidoBlazorSerializerContext, ready to be used for serialization.

In your case, compiling the serializer context in net7.0 creates the newer version of serialization that includes code generation. Unfortunately though, the code cannot be properly generated, as that information isn't in the NuGet package of the models.

You could of course have your app target net6.0 in order to make it use the former serialization logic, until the Fido2 lib is updated or the Blazor lib is on NuGet.

A mid point could be to add another lib to your solution that targets net6.0 and includes the serializer context, but I haven't tested this.

s21-himesh commented 10 months ago

@Regenhardt loader is default when setting up a blazor wasm project in .net 7. I have committed to repo but think the loader is tucked away!

I did see your ticket but couldn't get any of the suggestions to work :)

I can confirm the mid point suggestion works.

s21-himesh commented 10 months ago

For anyone else who hits this issue I've updated the repo available at https://github.com/s21-himesh/WebAuthnIssue with the work around.