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.12k stars 158 forks source link

Add support of multiple domains/rp #507

Closed g7ed6e closed 4 months ago

g7ed6e commented 4 months ago

Hi thanks for great work.

I'm working on a .net8 multi tenant application where each tenant is exposed to users from a specific domain. Currently the Fido2.AspNet does not seem to support this scenario as ServerDomain is a string.

I think this could be solved by implementing my own AddFido2 method using named options and keyed services so each tenant of my application would be able to pull the correct Fido2 instance depending of the user requested domain. Can you please confirm ?

Would you be open to add this feature to fido2-net-lib ? If so I would be happy to contribute this.

abergs commented 4 months ago

Hey @g7ed6e.

You're correct, .AddFido2 does not help you too much here.

Now, for adding such a feature, I'm a bit hesitant. Because every power user knob we add makes it a bit more complicated to understand/use. Our goal is to enable broad use of passkeys

If this implementation can be done without making it any harder/more confusing for non-multi-tenant developers, I'd be OK with a code solution.

If not, perhaps a bit of documentation would help.

After all, .AddFido2 does very little and you could do it in any way you see fit once you know how little it does:

CleanShot 2024-02-28 at 18 44 35

g7ed6e commented 4 months ago

Thanks for reply @abergs. I fixed my need using the below extension method called after AddFido2.

public static IServiceCollection FixFido2ToGetMultiTenancySupport(this IServiceCollection services)
    {
        services.AddTransient<IFido2>(provider =>
        {
            var httpContextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
            var httpRequest = httpContextAccessor.HttpContext?.Request;
            if (httpRequest == null)
            {
                return null;
            }

            var metadataService = provider.GetRequiredService<IMetadataService>();
            var defaultFido2Configuration = provider.GetRequiredService<IOptions<Fido2Configuration>>().Value;
            var fido2 = new Fido2(
                new()
                {
                    ServerName = httpRequest.Host.ToString(),
                    ServerDomain = httpRequest.Host.Host,
                    Origins = [$"{httpRequest.Scheme}{Uri.SchemeDelimiter}{httpRequest.Host}"],
                    TimestampDriftTolerance = defaultFido2Configuration.TimestampDriftTolerance,
                    MDSCacheDirPath = defaultFido2Configuration.MDSCacheDirPath
                },
                metadataService
            );
            return fido2;
        });

        return services;
    }
abergs commented 4 months ago

Good!