Search code examples
asp.net-coreasp.net-identityclient-certificates

Multiple Authentication Scheme including Client Certificate and Identity User


After doing Client Certificate X.509 authentication, how to authenticate Client Certificate-Id against AspNetUsers table in Database.

If I put SignInManager with OnValidateCertificate, it is getting called on every request and SignInManager is signing User on every request.

Please advise how to call SignInManager only once for authentication with AspNetUsers. Please note Client Certificate-Id is stored as UserName in AspNetUsers.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddDefaultIdentity<ApplicationUser>()
            .AddRoles<IdentityRole>()
            .AddDefaultUI(UIFramework.Bootstrap4)
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate(options =>
            {
                options.Events = new CertificateAuthenticationEvents
                {
                    OnValidateCertificate = context =>
                    {
                        var claims = new[]
                        {
                            new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
                            new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
                        };

                        context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
                        context.Success();

                        return Task.CompletedTask;
                    }
                };
            });

        services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                             .RequireAuthenticatedUser()
                             .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
        });
    }

Solution

  • As per Barry Dorrans response from below URL

    https://github.com/blowdart/idunno.Authentication/issues/29

    Oh interesting. Is there a reason that the certificate can't contain the user information and you need to go to a database to replace the generated principal? Identity wasn't meant to be used outside of cookie auth.

    The problem you have here is that yes, certificate validation is called on every request. It's an unfortunate side effect of trying to make this cross compatible, and configurable in the normal asp.net core style.

    The only thing I can think of is to cache the user information you get back from identity's signin manager in redis or other in memory cache, and use the sha256 hash of the raw data of the certificate as the key, then you'd miss the database hit. You'd have to clear the cache after a while if you want to reflect changes of course.