9

I have a .Net Core 2.0 application that uses JWT tokens to authorize the user. This all works fine but I want to have some sort of API Key mechanism to allow other applications to integrate but I cannot seem to get this to work with the current authentication.

Code:

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
            IMemoryCache cache, IServiceProvider serviceProvider)
{
    app.UseAuthentication();

    ApiKeyMiddlewear(app, serviceProvider);

    app.UseMvc(routes =>
    { 
        routes.MapRoute(
               name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });
    }
}


    private static void ApiKeyMiddlewear(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();
                    var settingsProvider = serviceProvider.GetService<ISettingsService<OmbiSettings>>();
                    var ombiSettings = settingsProvider.GetSettings();
                    var valid = ombiSettings.ApiKey.Equals(headerKey, StringComparison.CurrentCultureIgnoreCase);
                    if (!valid)
                    {
                        context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
                        await context.Response.WriteAsync("Invalid API Key");
                    }
                    else
                    {
                        var identity = new GenericIdentity("API");
                        identity.AddClaim(new System.Security.Claims.Claim("Origin", "Api"));
                        identity.AddClaim(new System.Security.Claims.Claim("role", "Admin"));

                        var principal = new GenericPrincipal(identity, new[] {"ApiUser"});
                        context.User = principal;
                        await next();
                    }
                }
                else
                {
                    await next();
                }
            }
            else
            {
                await next();
            }
        });
    }
}

So in the code above you can see that I am intercepting the HTTP requests that provide a header called ApiKey and then validate it to what I have stored. This part all works but when calling an API Method with the Authorize attribute this does not work and I get the following error logs:

2017-09-19 08:15:17.280 +01:00 [Information] Request starting HTTP/1.1 POST http://localhost:52038/api/v1/Identity/ application/json 372
2017-09-19 08:15:21.967 +01:00 [Information] Authorization failed for user: "API".
2017-09-19 08:15:21.976 +01:00 [Information] Authorization failed for the request at filter '"Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter"'.
2017-09-19 08:15:21.981 +01:00 [Information] Executing ForbidResult with authentication schemes ([]).
2017-09-19 08:15:21.991 +01:00 [Information] AuthenticationScheme: "Bearer" was forbidden.
2017-09-19 08:15:21.996 +01:00 [Information] Executed action "Ombi.Controllers.IdentityController.CreateUser (Ombi)" in 38.8268ms
2017-09-19 08:15:22.004 +01:00 [Information] Request finished in 4723.032ms 403 

Now I'm guessing this is to do with the request only supplying a ApiKey header and not a Authorization header with a correct JWT token.

How am I able to only supply a ApiKey header and when there is no ApiKey header then fallback to requiring a JWT token?

5
  • Are you using Asp.net Identity ? Commented Sep 25, 2017 at 14:59
  • @AramKocharyan Yes Commented Sep 25, 2017 at 21:05
  • Do you use Authorize attribute with Roles property ? Like [Authorize(Roles = “foo”)] Commented Sep 26, 2017 at 3:51
  • Applying Claim("role", "Admin") to GenericPrincipal will not affect anything, because GenericPrincipal have nothing to do with Claims. So if you want to apply admin role, you need to add it in constructor param: new GenericPrincipal(identity, new[] {"Admin", "ApiUser"}); Commented Sep 26, 2017 at 4:02
  • 1
    @AramKocharyan That works! If you don't mind putting an answer so I can grant you the bounty? Commented Sep 26, 2017 at 7:13

1 Answer 1

7
+50

Applying Claim("role", "Admin") to GenericPrincipal will not affect anything, because GenericPrincipal have nothing to do with Role Claims. So if you want to apply admin role to GenericPrincipal, you need to add it in constructor parameter:

 var principal = new GenericPrincipal(identity, new[] {"Admin","ApiUser"});
 context.User = principal;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.