Authentication with shared access keys
The Arcus.WebApi.Security
package provides a mechanism that uses shared access keys to grant access to a web application.
This authentication process consists of two parts:
- Find the configured parameter that holds the shared access key, this can be a request header, a query parameter or both.
- Shared access key matches the value with the secret stored, determined via configured secret provider
Installation
This feature requires to install our NuGet package
PM > Install-Package Arcus.WebApi.Security
Globally enforce shared access key authentication
Introduction
The SharedAccessKeyAuthenticationFilter
can be added to the request filters in an ASP.NET Core application.
This filter will then add authentication to all endpoints via a shared access key configurable on the filter itself.
Usage
The authentication requires an ICachedSecretProvider
or ISecretProvider
dependency to be registered with the services container of the ASP.NET request pipeline. This is typically done in the ConfigureServices
method of the Startup
class.
Once this is done, the SharedAccessKeyAuthenticationFilter
can be added to the filters that will be applied to all actions:
using Arcus.Security.Core.Caching;
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddSingleton<ICachedSecretProvider>(serviceProvider => new MyCachedSecretProvider());
services.AddMvc(options => options.Filters.Add(new SharedAccessKeyAuthenticationFilter(headerName: "http-request-header-name", queryParameterName: "api-key", secretName: "shared-access-key-name")));
}
}
Enforce shared access key authentication per controller or operation
Introduction
The SharedAccessKeyAuthenticationAttribute
can be added on both controller- and operation level in an ASP.NET Core application.
The shared access key authentication will then be applied to the endpoint(s) that are decorated with the SharedAccessKeyAuthenticationAttribute
.
Usage
The authentication requires an ICachedSecretProvider
or ISecretProvider
dependency to be registered with the services container of the ASP.NET request pipeline. This is typically done in the ConfigureServices
method of the Startup
class:
using Arcus.Security.Core.Caching;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddSingleton<ICachedSecretProvider>(serviceProvider => new CachedSecretProvider(new MySecretProvider()));
services.AddMvc();
}
}
After that, the SharedAccessKeyAuthenticationAttribute
attribute can be applied on the controllers, or if more fine-grained control is needed, on the operations that requires authentication:
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
[ApiController]
[SharedAccessKeyAuthentication(headerName: "http-request-header-name", queryParameterName: "api-key", secretName: "shared-access-key-name")]
public class MyApiController : ControllerBase
{
[HttpGet]
[Route("authz/shared-access-key")]
public Task<IActionResult> AuthorizedGet()
{
return Task.FromResult<IActionResult>(Ok());
}
}
Behavior in validating shared access key parameter
The package supports different scenarios for specifying the shared access key parameter and is supported for global or per controller/operation use cases.
- Use header only - Only the specified request header will be validated for the shared access key, any supplied query parameter will not be taken into account.
using Arcus.Security.Core.Caching;
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddSingleton<ICachedSecretProvider>(serviceProvider => new MyCachedSecretProvider());
services.AddMvc(options => options.Filters.Add(new SharedAccessKeyAuthenticationFilter(headerName: "http-request-header-name", secretName: "shared-access-key-name")));
}
}
- Use query parameter only - Only the specified query parameter will be validated for the shared access key, any supplied request header will not be taken into account.
using Arcus.Security.Core.Caching;
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddSingleton<ICachedSecretProvider>(serviceProvider => new MyCachedSecretProvider());
services.AddMvc(options => options.Filters.Add(new SharedAccessKeyAuthenticationFilter(queryParameterName: "api-key", secretName: "shared-access-key-name")));
}
}
- Support both header & query parameter - Both the specified request header and query parameter will be validated for the shared access key.
using Arcus.Security.Core.Caching;
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
using Microsoft.Extensions.DepdendencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddSingleton<ICachedSecretProvider>(serviceProvider => new MyCachedSecretProvider());
services.AddMvc(options => options.Filters.Add(new SharedAccessKeyAuthenticationFilter(headerName: "http-request-header-name", queryParameterName: "api-key", secretName: "shared-access-key-name")));
}
}
If both header and query parameter are specified, they must both be valid or an Unauthorized
will be returned.
Bypassing authentication
The package supports a way to bypass the shared access key authentication for certain endpoints. This works with adding one of these attributes to the respectively endpoint:
BypassSharedAccessKeyAuthentication
AllowAnonymous
Works on both method and controller level, using either the shared access key filter or attribute.
using Arcus.WebApi.Security.Authentication.SharedAccessKey;
[ApiController]
[SharedAccessKeyAuthentication("MySecret", "MyHeader")]
public class SystemController : ControllerBase
{
[HttpGet('api/v1/health')]
[BypassSharedAccessKeyAuthentication]
public IActionResult GetHealth()
{
return Ok();
}
}