Skip to content

Commit

Permalink
docs: publish v2.0 feature docs (#461)
Browse files Browse the repository at this point in the history
  • Loading branch information
stijnmoreels authored Jul 16, 2024
1 parent 4b9bf25 commit c8214f0
Show file tree
Hide file tree
Showing 15 changed files with 2,109 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/versioned_docs/version-v2.0.0/01-index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: "Arcus - Web API"
layout: default
slug: /
sidebar_label: Welcome
sidebar_position: 1
---

# Introduction

The Arcus Web API library provides an easy way to register and configure complex functionality to your application. We provide ways to add shared access key authentication, HTTP correlation, request tracking, strict JSON formatting, and other boilerplate code that can be added with zero effort. It builds on top of [Arcus Observability](https://observability.arcus-azure.net/) to provide its HTTP correlation and request tracking. All functionality described here is also available in our [project templates](https://templates.arcus-azure.net/).

# Installation

The Arcus.WebApi library can be installed via NuGet, for instance:

```shell
PM > Install-Package Arcus.WebApi.Security
```

For more granular packages we recommend reading the documentation.

# License
This is licensed under The MIT License (MIT). Which means that you can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the web application. But you always need to state that Codit is the original author of this web application.

*[Full license here](https://github.com/arcus-azure/arcus.webapi/blob/master/LICENSE)*
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Shared access key authentication with Azure Key Vault integration
The Arcus shared access key authentication is an API security filter to easily and safely share the same access key across a set of API endpoints. This kind of authentication is very common and therefore a popular feature in the Arcus WebApi library.

The API security filter makes use of the [Arcus secret store](https://security.arcus-azure.net/features/secret-store) to retrieve the access keys.

This user guide will walk through the process of adding shared access key authentication to an existing Web API application, using Azure Key Vault to store the access keys.

## Terminology
To fully understand this user guide, some terminology is required:
* **Shared access key**: a single secret that's being used as the authentication mechanism of many API endpoints. Using a single secret means that there's also a single authorization level.
* **Arcus secret store**: central place where the application retrieve its secrets ([more info](https://security.arcus-azure.net/features/secret-store)).

## Sample application
In this user guide, we will use a fictive API application to which we'll add shared access key authentication. We will be working with two major parts.

The initial place where the application will be started:
```csharp
public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRouting();
builder.Services.AddControllers();

WebApplication app = builder.Build();
app.UseRouting();
app.Run();
}
}
```

And the API controller that should be secured:
```csharp
[ApiController]
[Route("api/v1/order")]
public class OrderController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] Order order)
{
// Process order...
return Accepted();
}
}
```

## 1. Installation
To make use of the shared access key authentication, storing its secrets in Azure Key Vault, the following Arcus packages have to be installed.
```shell
PM > Install-Package -Name Arcus.WebApi.Security
PM > Install-Package -Name Arcus.Security.Providers.AzureKeyVault
```

## 2. Use Arcus secret store with Azure Key Vault integration
Once the packages are installed, add the secret store via extensions to the API application:
* 2.1 Use the `.ConfigureSecretStore` to setup the secret store with necessary secret providers
* 2.2 Use the `.AddAzureKeyVaultWithManagedIdentity` to add the Azure Key Vault secret provider that will access the secret store

```csharp
using Arcus.Security.Core.Caching.Configuration;

public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.AddRouting();
builder.AddControllers();

builder.Host.ConfigureSecretStore((config, stores) =>
{
stores.AddAzureKeyVaultWithManagedIdentity("https://your-key.vault.azure.net", CacheConfiguration.Default);
});

WebApplication app = builder.Build();
app.UseRouting();
app.Run();
}
}
```

## 3. Use Arcus shared access key authentication API filter
This user guide will make use of the recommended way of securing API endpoints. This is done by registering the authentication mechanism in the startup code and on the API endpoint itself. That being said, we do support finer-grained authentication. See [our dedicated feature documentation](https://webapi.arcus-azure.net/features/security/auth/shared-access-key) for more information.

The `Arcus.WebApi.Security` package provides all the available authentication and authorization security mechanisms. For shared access key authentication, we will be using the `AddSharedAccessKeyAuthenticationFilterOnHeader` extension which will register a global API authentication security filter that applies on all available API endpoints:
```csharp
using Arcus.Security.Core.Caching.Configuration;

public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.AddRouting();
builder.AddControllers(options =>
{
options.AddSharedAccessKeyAuthenticationFilterOnHeader(
"X-API-Key",
"MyAccessKey_SecretName_AvailableInSecretStore");
});

builder.Host.ConfigureSecretStore((config, stores) =>
{
stores.AddAzureKeyVaultWithManagedIdentity("https://your-key.vault.azure.net", CacheConfiguration.Default);
});

WebApplication app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.Run();
}
}
```

* The `X-API-Key` is the HTTP request header name where the shared access key should be located. Missing or invalid header values will result in `401 Unauthorized` HTTP responses.
* The `MyAccessKey_SecretName_AvailableInSecretStore` is the name of the secret that holds the shared access key in the Arcus secret store. The secret store will try to find the secret by this name in one of its secret providers, in this case Azure Key Vault.

The shared access key API authentication filter will then try to match the found access key secret with the incoming access key secret, located in the HTTP request header. Successful matches will delegate the request further up the application, unsuccessful matches will result in unauthorized results:
```powershell
$headers = @{
'Content-Type'='application/json'
'X-API-Key'='invalid-key'
}
curl -Method POST `
-Headers $headers `
'http://localhost:787/api/v1/order' `
-Body '{ "OrderId": "3", "ProductName": "Fancy desk" }'
# Content: Shared access key in request doesn't match expected access key
# StatusCode : 401
$headers = @{
'Content-Type'='application/json'
'X-API-Key'='valid-key'
}
curl -Method POST `
-Headers $headers `
'http://localhost:787/api/v1/order' `
-Body '{ "OrderId": "3", "ProductName": "Fancy desk" }'
# StatusCode : 202
```

## Conclusion
In this user guide, you've seen how the Arcus shared access key API authentication filter can be added to an existing application, using the Arcus secret store that places the access key in Azure Key Vault.

Besides shared access key authentication, we support several other mechanisms and useful API functionality. See our [feature documentation](https://webapi.arcus-azure.net/) for more information.

## Further reading
* [Arcus Web API documentation](https://webapi.arcus-azure.net/)
* [Shared access key authentication](https://webapi.arcus-azure.net/features/security/auth/shared-access-key)
* [Arcus Security secret store documentation](https://security.arcus-azure.net/features/secret-store)
* [Azure Key Vault integration](https://security.arcus-azure.net/features/secret-store/provider/key-vault)
* [Arcus Web API project template](https://templates.arcus-azure.net/features/web-api-template)
* [Integrating Arcus API Security Filters within F# Giraffe Function Pipelines](https://www.codit.eu/blog/arcus-api-security-filters-giraffe-function-pipelines/)
* [Out-of-the-box Request Tracking, Simplified HTTP Correlation & JWT Authorization in Arcus Web API v1.0](https://www.codit.eu/blog/out-of-the-box-request-tracking-simplified-http-correlation-jwt-authorization-in-arcus-web-api-v1-0/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: "Correlate between HTTP requests/responses in Azure Functions"
layout: default
---

# Correlate between HTTP requests/responses in Azure Functions

The `Arcus.WebApi.Logging.AzureFunctions` library provides a way to add correlation between HTTP requests for Azure Functions.

## How This Works

See [the general HTTP correlation page](correlation.md) to get a grasp on how HTTP correlation works.

🚩 By default, the W3C Trace-Context specification is used as the default HTTP correlation format in Arcus, but you can go back to the (deprecated) Hierarchical system we had before, by passing `HttpCorrelationFormat.Hierarchical` to the `services.AddHttpCorrelation()`.

## Installation

For this feature, the following package needs to be installed:

```shell
PM > Install-Package Arcus.WebApi.Logging.AzureFunctions
```

## Usage for in-process Azure Functions (receiving side)

To make sure the correlation is added to the HTTP response, following additions have to be made in the `.Configure` methods of the function's startup:

```csharp
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Startup))]

namespace MyHttpAzureFunction
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.AddHttpCorrelation();
}
}
}
```

When this addition is added, you can use the `HttpCorrelation` inside your function to call the correlation functionality and use the `IHttpCorrelationInfoAccessor` (like before) to have access to the `CorrelationInfo` of the HTTP request.
This is how you use the W3C HTTP correlation in your application:

```csharp
using Arcus.WebApi.Logging.Correlation;

public class MyHttpFunction
{
private readonly AzureFunctionsInProcessHttpCorrelation _httpCorrelation;

public MyHttpFunction(AzureFunctionsInProcessHttpCorrelation httpCorrelation)
{
_httpCorrelation = httpCorrelation;
}

[FunctionName("HTTP-Correlation-Example")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request");

// Easily set the correlation information to the response if you want to expose it.
_httpCorrelation.AddCorrelationResponseHeaders(req.HttpContext);

// Easily access correlation information in your application.
CorrelationInfo correlationInfo = _httpCorrelation.GetCorrelationInfo();
return new OkObjectResult("This HTTP triggered function executed successfully.");
}
}
```

To use the old Hierarchical HTTP correlation, use the following:
```csharp
using Arcus.WebApi.Logging.Correlation;

public class MyHttpFunction
{
private readonly HttpCorrelation _httpCorrelation;

public MyHttpFunction(HttpCorrelation httpCorrelation)
{
_httpCorrelation = httpCorrelation;
}

[FunctionName("HTTP-Correlation-Example")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request");

if (!_httpCorrelation.TryHttpCorrelate(out string errorMessage))
{
return new BadRequestObjectResult(errorMessage);
}

// Easily access correlation information in your application.
CorrelationInfo correlationInfo = _httpCorrelation.GetCorrelationInfo();
return new OkObjectResult("This HTTP triggered function executed successfully.");
}
}
```

> Note that the `HttpCorrelation` already has the registered `IHttpCorrelationInfoAccessor` embedded but nothing stops you from injecting the `IHtpCorrelationInfoAccessor` yourself and use that one. Behind the scenes, both instances will be the same.
## Usage for isolated Azure Functions (receiving side)

To make sure the correlation is added to the HTTP response, following middleware has to be added in the `Program.cs` file:
```csharp
using Microsoft.Extensions.Hosting;

IHost host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder.UseFunctionContext()
.UseHttpCorrelation();
})
.Build();

host.Run();
```

* The `UseFunctionContext` middleware makes sure that the `FunctionContext` in the Azure Function is accessible via the `IFunctionContextAccessor` function. This is required since the `IHttpCorrelationInfoAccessor` uses the `FunctionContext` to assign the correlation model.
* The `UseHttpCorrelation` middleware adds the HTTP correlation functionality to the request pipeline. This makes sure that the incoming requests results in a correlation model (accessible via the `IHttpCorrelationInfoAccessor`) and the outgoing response is enriched with this correlation model.

The HTTP trigger function can access the `IHttpCorrelationInfoAccessor` but doesn't require any additional changes to make the HTTP correlation work (unlike the in-process Azure Functions variant).

```csharp
public class HttpTriggerFunction
{
private readonly IHttpCorrelationInfoAccessor _correlationAccessor;

public HttpTriggerFunction(IHttpCorrelationInfoAccessor correlationAccessor)
{
_correlationAccessor = correlationAccessor;
}

[Function("http")]
public HttpResponseData Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData request)
{
CorrelationInfo correlationInfo = _correlationAccessor.GetCorrelationInfo();

HttpResponseData response = request.CreateResponse(HttpStatusCode.OK);
return response;
}
}
```
Loading

0 comments on commit c8214f0

Please sign in to comment.