Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core Web API key

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.)

like image 324
Jamie Rees Avatar asked Aug 21 '17 13:08

Jamie Rees


2 Answers

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/

like image 69
Jamie Rees Avatar answered Nov 05 '22 13:11

Jamie Rees


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.

like image 36
Mwiza Avatar answered Nov 05 '22 14:11

Mwiza