Action Filter

I needed a way to apply a custom filter to a single action and didnt want to build authorization policys as the filters were temporary.

Ideally the examples below should rather validate a JSON Web Token (JWT), its not best practice to pass the client key and secret with each request but rather exchange these using the OAuth2 Client Credentials Flow.

Creating a filter

  1. Create the filter using IActionFilter or IAsyncActionFilter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace AuthService.Application.Filters
{
public class ValidateClientCredentials : IActionFilter
{
private readonly RegisteredClientOptions _registeredClientOptions;

public ValidateClientCredentials (IOptions<RegisteredClientOptions> registeredClientOptions)
{
_registeredClientOptions = registeredClientOptions.Value;
}

public void OnActionExecuted(ActionExecutedContext context) { }

public void OnActionExecuting(ActionExecutingContext context)
{
var clientId = context.HttpContext.Request.Headers["Client-Id"].ToString();
var clientSecret = context.HttpContext.Request.Headers["Client-Secret"].ToString();
var bffOptions = _registeredClientOptions.PorkyBff;

if (bffOptions != null &&
bffOptions.ClientId == clientId &&
bffOptions.Secret == clientSecret)
{
return;
}

context.Result = new ObjectResult(new ProblemDetails
{
Detail = "Unauthorized",
Status = (int)HttpStatusCode.Unauthorized
});
}
}
}
  1. Register the filter
1
2
3
4
5
6
7
8
9
10
namespace AuthService.Application.Extensions
{
public static class ServiceExtension
{
public static void RegisterDependencies(this IServiceCollection services)
{
services.AddScoped<ValidateClientCredentials>();
}
}
}
  1. Apply the filter
1
2
3
4
5
6
7
8
9
10
namespace AuthService.Controllers
{
[ApiController]
[Route("/credentials")]
public class CredentialsController : ControllerBase
{
[ServiceFilter(typeof(ValidateClientCredentials))]
[HttpPatch("upsert")]
public async Task<IActionResult> Update(string username, [FromBody] CredentialModels updated)
{
  1. Now all PATCH requests to /credentials/upsert will need to include header values that match the injected options for:
  • Client-Id
  • Client-Secret