asp.net-web-apiasp.net-core-webapicustom-authentication

Custom authentication asp.net core web api


I want to use a secret key (api key) authorization asp.net core web api. The key will be passed in Authorization header like given below,

ex. Authorization keytype;h43484344343bbhfdjfdfhj34343

I want to write a middleware to read this key from request headers and call an internal api to validate the key.

In web api we can write a message handler to do this, but I am new to asp.net core. I'm seeing a lot of samples but they are using inbuilt JWT token authentication. But I wanted to use my own key and I decrypt this key and validate against a database entry.

Can anyone suggest some code samples on how to do this?


Solution

  • I have used this approach in a solution using asp core 1.1. First define a custom scheme:

    public static class Authentication
    {
        public const string Scheme = "Custom";
    }
    

    You then have to inherit AuthenticationHandler<TOptions>. Here is where the logic for validating the header value will go:

    public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
    {
        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var authorizationHeader = Context.Request.Headers["Authorization"];
            if (!authorizationHeader.Any())
                return Task.FromResult(AuthenticateResult.Skip());
    
            var value = authorizationHeader.ToString();
            if (string.IsNullOrWhiteSpace(value))
                return Task.FromResult(AuthenticateResult.Skip());
    
            // place logic here to validate the header value (decrypt, call db etc)
    
            var claims = new[]
            {
                new Claim(System.Security.Claims.ClaimTypes.Name, "Bob")
            };
    
            // create a new claims identity and return an AuthenticationTicket 
            // with the correct scheme
            var claimsIdentity = new ClaimsIdentity(claims, Authentication.Scheme);
    
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties(), Authentication.Scheme);
    
            return Task.FromResult(AuthenticateResult.Success(ticket));
        }
    }
    

    In order to inherit AuthenticationHandler you must create an options class where you set the AuthenticationScheme-property to the scheme you are using:

    public class MyOptions : AuthenticationOptions
    {
        AuthenticationScheme = Authentication.Scheme;
    }
    

    After this you have to inherit AuthenticationMiddleware<TOptions>. This will create the handler you implemented in the previous step:

    public class MyAuthenticationMiddleware : AuthenticationMiddleware<MyOptions>
    {
        public MyAuthenticationMiddleware(RequestDelegate next, IOptions<MyOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
        {
        }
    
        protected override AuthenticationHandler<MyOptions> CreateHandler()
        {
            return new MyAuthenticationHandler();
        }
    }
    

    In order to easily plug in your middleware you can define these extension methods:

    public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, IConfigurationSection config)
    {
        return app.UseMyAuthentication(options => {});
    }
    
    private static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, Action<MyOptions> configure)
    {
        var options = new MyOptions();
        configure?.Invoke(options);
    
        return app.UseMiddleware<MyAuthenticationMiddleware>(new OptionsWrapper<MyOptions>(options));
    }
    

    Then in your Startup class you can finally add your middleware:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMyAuthentication(Configuration.GetSection("MyAuthenticationOptions"));
    
        // other stuff
    
        app.UseMvc();
    }
    

    Then add the AuthorizeAttribute on your actions specifying the scheme you just created:

    [Authorize(ActiveAuthenticationSchemes = Authentication.Scheme)]
    public IActionResult Get()
    {
        // stuff ...
    }
    

    There are a lot of steps but hopefully this will get you going!