4

Currently building an API using .NET CORE 3.1 and Identity to manage our authentication / authorization within it. We're looking to use generated API keys rather than short life tokens that need to be refreshed (it doesn't suit our use case scenario).

What we want to do is provide the option for users/third parties to generate API keys and allow them to assign Identity Roles to the API key rather than a user. That way we could still use the [Authorize] attribute to grant / restrict their access to particular endpoints.

We have a Users table that handles authentication / authorization within the core application itself and have a table that stores the generated API keys. One user can generate multiple API keys (one with read only permission to endpoints and one with write, for example). We just need to link the API keys to Identity Roles and utilise the [Authorize] attribute when said API key is passed in via the Request Header.

Does anybody have any ideas / advice about how to make this work? Or any advice as to whether this is a bad idea and how it could be done better? Any advice appreciated.

Adam Parker
  • 161
  • 1
  • 13
  • Why not use TLS with a certificate? – jdweng Jul 27 '20 at 11:04
  • 2
    Hi Adam, you can specify the default policy for authorize, this can allow you to set it to your own custom poliicy. I wrote an answer on another question about this, you may find it useful https://stackoverflow.com/a/62950533/5517088 – Kevin Jul 27 '20 at 11:14
  • 2
    @jdweng I think you've misunderstood the question. TLS is a transport protocol and nothing to do with API authentication / authorization – Adam Parker Jul 27 '20 at 11:40
  • Read Wiki : https://en.wikipedia.org/wiki/Transport_Layer_Security – jdweng Jul 27 '20 at 11:45
  • 1
    Agree with Kevin, you could use the custom policy or custom Authorize attribute to achieve the authorization. More detail information, please check the following links: [custom authentication](https://stackoverflow.com/questions/36095076/custom-authentication-in-asp-net-core), [custom AuthorizeAttribute](https://stackoverflow.com/questions/31464359/how-do-you-create-a-custom-authorizeattribute-in-asp-net-core) and [Policy-based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1). – Zhi Lv Jul 28 '20 at 12:08

1 Answers1

0

You could override the [Authorize] attribute that many of the JWT examples use, but its a bit of a hacky solution to bend JWT to do something it wasn't designed to do.

My suggestion would be to add the API key as a request parameter for your API endpoints and put any data payloads in the body of the request. e.g. /api/someendpoint/?apiKey={some_api_key}

You could then create a BaseController class for you API controllers to inherit from to perform a validation check on the key wherever it's needed.

This won't allow you to use the [Authorize] attribute as you wanted, but it would get you up and running for the short term. Creating a custom [AuthorizeApiKey] attribute wouldn't be far off once you've got the main concept working, but that would be something for you to investigate.

Here's some example code.

BaseController.cs

public class BaseController : Controller
{
  ...
  // Returns true if key is valid, returns false if invalid
  protected async Task<bool> ValidateApiKey(string apiKey)
  {
    var result = false;

    // Logic to check API key...

    return result;
  }

  ...
}

SomeApiController.cs

[Area("API")]
[Route("api/[controller]")]
[ApiController]
public class SomeApiController : BaseController
{
   [HttpGet]
   public async Task<IActionResult> GetWidgets(string apiKey)
   {
      var authorized = ValidateApiKey(apiKey);
      
      // If key is invalid, return a relevant response
      if(!authorized) return Unauthorized();

      // Otherwise, process the request and return the expected result
      var widgets = _SomeWidgetService.GetWidgets();

      return Created(widgets);
   }


   [HttpPost]
   public async Task<IActionResult> CreateWidget(string apiKey, [FromBody] WidgetClass widget)
   {
      var authorized = ValidateApiKey(apiKey);
      
      // If key is invalid, return a relevant response
      if(!authorized) return Unauthorized();

      // Otherwise process the rest of the action
      var result = (bool)_SomeWidgetService.CreateWidget(widget);

      if(result)
         return Created(); 
      else
         return BadRequest();
   }
}
Dezzamondo
  • 1,849
  • 2
  • 12
  • 28