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.18k stars 168 forks source link

Publish for multiple targets #380

Closed Regenhardt closed 5 days ago

Regenhardt commented 1 year ago

Currently, $SupportedTargetFrameworks only contains net6.0 - some other files do however contain logic that points to this libs having been available for other framework version too, like net5 or net472 (there are checks failing the build for net46x).

So to offer this lib for older applications but also be able to use compiler features of newer versions like source generation (see e.g. https://github.com/dotnet/runtime/issues/81709), can we add other targets to the $SupportedTargetFrameworks, or would that jeopardize other features?

iamcarbon commented 1 year ago

@Regenhardt .NET5.0 is out of service and the library dropped support for the .NET Framework in the 3.0 release cycle. Since since, the library has utilized many features to improve the security, performance, and maintainability of the codebase from .NET CORE (.NET).

Are there any specific files reference .NET Framework? These should be removed or updated.

Regenhardt commented 1 year ago

fido2.targets seems to only exist to tell netstandard users that the lib doesn't work with net46x, as is this doc: https://github.com/passwordless-lib/fido2-net-lib/blob/master/Documentation/NET46X.md .
There was an issue asking how to use this in .net48 so I thought it would be nice.

Should i make a PR deleting those two files then?

Regenhardt commented 1 year ago

Also what about adding net7 to the targets to get those new source generation features for people already using net7?

Simonl9l commented 1 year ago

@Regenhardt @iamcarbon I assume you have an issue with the IJsonTypeInfoResolver in that your tying to have an MVC/Razor/Minimal API app handle the de/serialization of say AuthenticatorAttestationRawResponse in net7.0 targets.

I note this issue https://github.com/dotnet/runtime/issues/81709 refers specifically to a comment that "Contexts generated using the v6 source generator are not compatible with contract customization" as per net7.0.

Whilst there appears to be work arounds if you are dealing with the serialization inline, if we want to plug into the middleware, this seem problematic.

I've tried

combinedResolver = JsonTypeInfoResolver.Combine(FidoSerializerContext.Default, MySerializerContext.Default );

 builder.Services
            .Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
            {
                options.SerializerOptions.TypeInfoResolver = combinedResolver;
            })
           ...

and get the following error:

      Connection id "0HMS7SSR7DCN9", Request id "0HMS7SSR7DCN9:00000002": An unhandled exception was thrown by the application.
      System.InvalidOperationException: The IJsonTypeInfoResolver returned a JsonTypeInfo instance whose JsonSerializerOptions setting does not match the provided argument.
         at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ResolverTypeInfoOptionsNotCompatible()
         at System.Text.Json.Serialization.JsonSerializerContext.System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfoResolver.CombiningJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options)
         at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type)
         at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
         at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Boolean resolveIfMutable)
         at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType)
         at System.Text.Json.JsonSerializer.DeserializeAsync(Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|90_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass90_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

I've also tried to add the Fido lib types into my local serialization context but get the following error:

Connection id "0HMS7SH3U9O2T", Request id "0HMS7SH3U9O2T:00000004": An unhandled exception was thrown by the application.
      System.NotSupportedException: Metadata for type 'Fido2NetLib.AuthenticatorAttestationRawResponse' was not provided by TypeInfoResolver of type 'MySerializationContext'. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
         at System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type type, IJsonTypeInfoResolver resolver)
         at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Boolean resolveIfMutable)
         at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType)
         at System.Text.Json.JsonSerializer.DeserializeAsync(Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|90_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass90_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

As you suggest to would be great if the maintainers could add net7.0 and soon net8.0 into the library targets! But additionally this is how the folks at StrongGrid solved a similar issue

Simonl9l commented 1 year ago

FYI my current workaround, per above, is to change the JsonTypeInfoResolver to chain in the DefaultJsonTypeInfoResolver that uses reflection.

Whilst this is not ideal (and not AoT'able) - it can provide a stop-gap 'til .net8.0 is LTS and hopeful this package will be updated to support it.

var combinedResolver = JsonTypeInfoResolver.Combine(MySerializationContext.Default, new DefaultJsonTypeInfoResolver());

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolver = combinedResolver;
})
Regenhardt commented 1 year ago

When the frontend lib is merged, it includes a serializer context that works in a net7 frontend too. I just tried setting the applications (Blazor server + client) to net7 keeping the libs at net6 and it worked without any other modifications. You can see the frontend serializer used here, initialized in line 18 and used e.g. in line 78.

Simonl9l commented 1 year ago

@Regenhardt - per this reply https://github.com/passwordless-lib/fido2-net-lib/issues/371#issuecomment-1652780402 we're using the nugget packages not project references!