.NET Core Web API key

25,256

Solution 1

This is what I did in the end:

 public static void ApiKeyMiddlewear(this IApplicationBuilder app, IServiceProvider serviceProvider)
    {
        app.Use(async (context, next) =>
        {
            if (context.Request.Path.StartsWithSegments(new PathString("/api")))
            {
                // Let's check if this is an API Call
                if (context.Request.Headers["ApiKey"].Any())
                {
                    // validate the supplied API key
                    // Validate it
                    var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                    await ValidateApiKey(serviceProvider, context, next, headerKey);
                }
                else if (context.Request.Query.ContainsKey("apikey"))
                {
                    if (context.Request.Query.TryGetValue("apikey", out var queryKey))
                    {
                        await ValidateApiKey(serviceProvider, context, next, queryKey);
                    }
                }
                else
                {
                    await next();
                }
            }
            else
            {
                await next();
            }
        });
    }

    private static async Task ValidateApiKey(IServiceProvider serviceProvider, HttpContext context, Func<Task> next, string key)
    {
        // validate it here
        var valid = false;
        if (!valid)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            await context.Response.WriteAsync("Invalid API Key");
        }
        else
        {
            var identity = new GenericIdentity("API");
            var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
            context.User = principal;
            await next();
        }
    }

This has changed quite a bit since I answered the original question (Answer is still valid). But you can read about this here: https://tidusjar.github.io/2019/03/25/net-core-jwt-api-key/

Solution 2

There is a nice article on using api keys in header requests on this link: http://www.mithunvp.com/write-custom-asp-net-core-middleware-web-api/

To summarise, in ASP.NET Core , you can use Middleware to control the http pipeline configuration. Middleware effectively replaces HttpHandlers, which were used in ealier versions of asp.net MVC.

Share:
25,256

Related videos on Youtube

Jamie Rees
Author by

Jamie Rees

Code enthusiast. Blog The person behind Ombi

Updated on March 03, 2022

Comments

  • Jamie Rees
    Jamie Rees about 2 years

    I am developing an application that users can authenticate via username and password and we provide a JWT token that then gets validated on the server.

    One thing I would like to add is the ability to have a special API Key (guid) that the users can use when integrating with this application instead of using a username and password.

    I am unsure how to do this since the authentication part seems to be a bit of a black box (using Aspnet Identity).

    Here is some of my code for the authentication setup.

    Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddDbContext<OmbiContext>(options =>
            options.UseSqlite("Data Source=Ombi.db"));
    
        services.AddIdentity<OmbiUser, IdentityRole>()
            .AddEntityFrameworkStores<OmbiContext>()
            .AddDefaultTokenProviders();
    
        services.Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequiredLength = 1;
            options.Password.RequireLowercase = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = false;
        });
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IMemoryCache cache)
    {
        var tokenOptions = (IOptions<TokenAuthentication>)app.ApplicationServices.GetService(
            typeof(IOptions<TokenAuthentication>));
    
        var ctx = (IOmbiContext)app.ApplicationServices.GetService(typeof(IOmbiContext));
    
        var tokenValidationParameters = new TokenValidationParameters
        {
    
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.Value.SecretKey)),
    
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ValidAudience = "Ombi",
            ValidIssuer = "Ombi",
            ClockSkew = TimeSpan.Zero
        };
    
        app.UseJwtBearerAuthentication(new JwtBearerOptions()
        {
            Audience = "Ombi",
            AutomaticAuthenticate = true,
            TokenValidationParameters =  tokenValidationParameters,
    
        });
     //....
    }
    

    The above code works when having the [Authorized] attributes on controllers and checking for the roles and such.

    Anyone have any idea how I can pass some sort of Api-Key header on all requests containing this special API Key for it to pass the [Authorized] attributes? (The key is stored in the database.)

    • stuartd
      stuartd over 6 years
    • Jamie Rees
      Jamie Rees over 6 years
      @stuartd Not sure if the above applies, looking at it i'd need to define that policy for every controller, in that case the API Key header would then always need to be present. Basically i'm looking for a way to provide a secret to the server that authorises me.
    • joakimja
      joakimja over 6 years
      You supply the bearer token with Authorization header in request. And depending on the authorization server are located you need to validate it. I.e. In azure AD you add a api under appregistration if you just want to validate against a hardcoded key in server you can validate by you own claim validator, check AuthorizationHandler class
  • Guido Dizioli
    Guido Dizioli almost 6 years
    Could you elaborate what you want to achieve on the else body of the ValidateApiKey? My concerns are towards validating the ApiKey, but then getting rejected on actions with the attribute [Authorize]. How to you handle the middleware to step over that attribute?
  • Jamie Rees
    Jamie Rees about 5 years
    Basically await context.Response.WriteAsync("Invalid API Key"); then ` await next.Invoke(context);`. You can see this here: github.com/tidusjar/Ombi/blob/master/src/Ombi/…
  • rafaelvascc
    rafaelvascc about 5 years
    Hey @JamieRees thanks for this solution, it helped me a lot! What do you think of insted of checking for an ApiKey, we look for the client_id and check if the client_id has access to a protected scope (like an Api resource)?