Search code examples
c#asp.net-coreowinwindows-authenticationasp.net-core-authenticationhandler

Dynamically chose authentication scheme in ASP.net Core Wep Api


I am porting a self-hosted web API built using OWIN and the .NET Framework to a ASP.NET Core Web API (using .NET 6.0)

In the original API, I have a custom authentication mechanism that selects the authentication scheme for each call dynamically, based on a header in the request:

HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemeSelectorDelegate = new AuthenticationSchemeSelector((httpRequest) =>
{
    if(httpRequest.Headers.AllKeys.Any(k => k == "MyCustomHeader"))
    {
        return AuthenticationSchemes.Ntlm;
    }
    else
    {
        return AuthenticationSchemes.Anonymous;
    }
});

Basically, for each request I check a specific header in the request and based on that I chose whether to force the request to use Windows Authentication or to allow the request to proceed anonymously.

How can I replicate this behavior in ASP.net Core web api? I found out how to use Windows Authentication by using the Microsoft.AspNetCore.Authentication.Negotiate NuGet package and configuring:

services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
                .AddNegotiate();

however, I don't know how to dynamically select whether to use that scheme or allow anonymous call based on the header like I did before.

Is this possible? How can I do it?


Solution

  • Here is a kind of approach

    services.AddAuthentication(opts =>
        {
            opts.DefaultScheme = "DynamicAuthenticationScheme";
        })
        .AddScheme<SystemSessionAuthenticationRelatedOptions, SystemAuthenticationRelatedHandler>(
            CommonConstants.SessionAuthentication, x => x.Test = "Ran in here")
        .AddCookie("CookieScheme")
        .AddJwtBearer(options =>
        {
            options.Authority = identityUrl;
            options.Audience = "shipping";
            options.RequireHttpsMetadata = false;
        })
        .AddPolicyScheme("DynamicAuthenticationScheme", "Default system policy",
            cfgOpts => cfgOpts.ForwardDefaultSelector = ctx =>
                ctx.Request.Headers.ContainsKey("IsTheSecretHeaderPresent?")
                    ? "CookieScheme"
                    : JwtBearerDefaults.AuthenticationScheme);
    

    The idea was specify a default authentication scheme to DynamicAuthenticationScheme, and we add 2 more authentication scheme named CookieScheme and JwtBearerDefaults.AuthenticationScheme constant for Cookie and Jwt authentication correspondingly.

    Then define our default authentication scheme as a routing for authentication mechanism based on the header info.