Skip to content

Commit

Permalink
feat: add microsoft entra id authentication scheme (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
tnc1997 authored Jun 12, 2024
1 parent 3afa8eb commit 9549d5c
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 21 deletions.
210 changes: 191 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ Please note that Emulator for Azure App Configuration is unofficial and not endo
## Getting Started

```shell
openssl req -x509 -out ./emulator.crt -keyout ./emulator.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -addext 'subjectAltName=DNS:localhost'
docker build -f ./src/AzureAppConfigurationEmulator/Dockerfile -t azure-app-configuration-emulator .
docker run -e ASPNETCORE_HTTP_PORTS=8080 -e ASPNETCORE_HTTPS_PORTS=8081 -p 8080:8080 -p 8081:8081 -v ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro -v ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro azure-app-configuration-emulator-data-potection-keys:/root/.aspnet/DataProtection-Keys azure-app-configuration-emulator
docker run -p 8080:8080 azure-app-configuration-emulator
```

## Authentication

The emulator supports HMAC authentication but does not support Microsoft Entra ID authentication.
The emulator supports HMAC authentication and Microsoft Entra ID authentication.

The credential and secret can be overridden using the environment variables `Authentication__Schemes__Hmac__Credential` and `Authentication__Schemes__Hmac__Secret` respectively.
### HMAC

The credential and secret may be overridden using the environment variables `Authentication__Schemes__Hmac__Credential` and `Authentication__Schemes__Hmac__Secret` respectively.

```yaml
services:
Expand All @@ -23,14 +24,43 @@ services:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=8080
- Authentication__Schemes__Hmac__Credential=xyz
- Authentication__Schemes__Hmac__Secret=c2VjcmV0
ports:
- "8080:8080"
```
### Postman
#### .NET
The client may authenticate requests using the connection string for the emulator.
```csharp
using Azure.Data.AppConfiguration;

var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings__AzureAppConfiguration");
var client = new ConfigurationClient(connectionString);

var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World");
await client.SetConfigurationSettingAsync(setting);
```

```yaml
services:
azure-app-configuration-emulator:
build:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
console-application:
build:
context: .
dockerfile: ./ConsoleApplication/Dockerfile
depends_on:
- azure-app-configuration-emulator
environment:
- ConnectionStrings__AzureAppConfiguration=Endpoint=http://azure-app-configuration-emulator:8080;Id=abcd;Secret=c2VjcmV0;
```
#### Postman
The authentication related headers may be generated using the following script:
```javascript
const credential = "abcd";
Expand All @@ -48,6 +78,133 @@ pm.request.headers.upsert(`x-ms-content-sha256: ${contentHash}`);
pm.request.headers.upsert(`Authorization: HMAC-SHA256 Credential=${credential}&SignedHeaders=${signedHeaders}&Signature=${signature}`);
```
### Microsoft Entra ID
HMAC authentication is recommended because it does not require a Microsoft Entra tenant and an Azure App Configuration resource.
1. [Register an application](https://learn.microsoft.com/entra/identity-platform/quickstart-register-app) within the Microsoft Entra tenant.
1. On the Overview page, in the Essentials accordion, copy the following values:
* Application (client) ID
* Directory (tenant) ID
2. On the Certificates & secrets page, in the Client secrets tab, add a client secret.
2. [Create an Azure App Configuration resource](https://learn.microsoft.com/azure/azure-app-configuration/quickstart-azure-app-configuration-create) to be emulated.
1. On the Overview page, in the Essentials accordion, copy the following values:
* Endpoint
2. On the Access control (IAM) page, add a role assignment.
1. In the Role tab, select the App Configuration Data Owner role.
2. In the Members tab, assign access to the registered application.
3. [Generate a self-signed certificate](#ssl--tls) with the `<endpoint>` as the [Subject Alternative Name](https://wikipedia.org/wiki/Subject_Alternative_Name).

The metadata address must be set using the environment variable `Authentication__Schemes__MicrosoftEntraId__MetadataAddress`.

```yaml
services:
azure-app-configuration-emulator:
build:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=80
- ASPNETCORE_HTTPS_PORTS=443
- Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
networks:
default:
aliases:
- <endpoint>
volumes:
- ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
- ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro
```

The valid audience should be overriden using the environment variable `Authentication__Schemes__MicrosoftEntraId__ValidAudience`.

```yaml
services:
azure-app-configuration-emulator:
build:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=80
- ASPNETCORE_HTTPS_PORTS=443
- Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/.well-known/openid-configuration
- Authentication__Schemes__MicrosoftEntraId__ValidAudience=https://<endpoint>
networks:
default:
aliases:
- <endpoint>
volumes:
- ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
- ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro
```

#### .NET

The client may authenticate requests using the Microsoft Entra tenant.

```csharp
using Azure.Data.AppConfiguration;
using Azure.Identity;
var tenantId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__TenantId");
var clientId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientId");
var clientSecret = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientSecret");
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var endpoint = Environment.GetEnvironmentVariable("Endpoints__AzureAppConfiguration");
var client = new ConfigurationClient(new Uri(endpoint), credential);
var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World");
await client.SetConfigurationSettingAsync(setting);
```

```yaml
services:
azure-app-configuration-emulator:
build:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=80
- ASPNETCORE_HTTPS_PORTS=443
- Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/.well-known/openid-configuration
- Authentication__Schemes__MicrosoftEntraId__ValidAudience=https://<endpoint>
networks:
default:
aliases:
- <endpoint>
volumes:
- ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
- ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro
console-application:
build:
context: .
dockerfile: ./ConsoleApplication/Dockerfile
depends_on:
- azure-app-configuration-emulator
entrypoint: /bin/sh -c "update-ca-certificates && dotnet ConsoleApplication.dll"
environment:
- Authentication__Schemes__MicrosoftEntraId__ClientId=<client-id>
- Authentication__Schemes__MicrosoftEntraId__ClientSecret=<client-secret>
- Authentication__Schemes__MicrosoftEntraId__TenantId=<tenant-id>
- Endpoints__AzureAppConfiguration=https://<endpoint>
volumes:
- ./emulator.crt:/usr/local/share/ca-certificates/emulator.crt:ro
```

#### Postman

The access token may be obtained using the following configuration:

| Configuration | |
|------------------|-------------------------------------------------------------------|
| Auth Type | OAuth 2.0 |
| Grant Type | Client Credentials |
| Access Token URL | `https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token` |
| Client ID | `<client-id>` |
| Client Secret | `<client-secret>` |
| Scope | `https://<endpoint>/.default` |

## Compatibility

The emulator is compatible with the following operations:
Expand Down Expand Up @@ -156,7 +313,7 @@ services:

The emulator integrates with Azure Event Grid to publish events using the [Event Grid event schema](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics#event-grid-event-schema) when configuration settings are deleted or modified.

The endpoint and key for the [Event Grid Topic](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics) can be set using the environment variables `Messaging__EventGridTopics__xyz__Endpoint` and `Messaging__EventGridTopics__xyz__Credential__Key` respectively where `xyz` is an arbitrary name.
The endpoint and key for the [Event Grid Topic](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics) may be set using the environment variables `Messaging__EventGridTopics__xyz__Endpoint` and `Messaging__EventGridTopics__xyz__Credential__Key` respectively where `xyz` is an arbitrary name.

```yaml
services:
Expand All @@ -165,18 +322,15 @@ services:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=8080
- Messaging__EventGridTopics__Contoso__Credential__Key=a2V5
- Messaging__EventGridTopics__Contoso__Endpoint=https://contoso.uksouth-1.eventgrid.azure.net/api/events
ports:
- "8080:8080"
```

## Observability

The emulator integrates with OpenTelemetry to provide metrics and traces.

The endpoint for the [OpenTelemetry Protocol (OTLP) Exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter) can be overridden using the environment variable `OTEL_EXPORTER_OTLP_ENDPOINT`.
The endpoint for the [OpenTelemetry Protocol (OTLP) Exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter) may be overridden using the environment variable `OTEL_EXPORTER_OTLP_ENDPOINT`.

```yaml
services:
Expand All @@ -187,13 +341,31 @@ services:
depends_on:
- opentelemetry-collector
environment:
- ASPNETCORE_HTTP_PORTS=8080
- OTEL_EXPORTER_OTLP_ENDPOINT=http://opentelemetry-collector:4317
ports:
- "8080:8080"
opentelemetry-collector:
image: otel/opentelemetry-collector-contrib
ports:
- "4317:4317"
- "4318:4318"
```

## SSL / TLS

The emulator may be configured to serve requests over HTTPS using a [self-signed certificate](https://wikipedia.org/wiki/self-signed_certificate).

```shell
openssl req -x509 -out ./emulator.crt -keyout ./emulator.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=azure-app-configuration-emulator' -addext 'subjectAltName=DNS:azure-app-configuration-emulator'
```

The port for HTTPS must be set using the environment variable [`ASPNETCORE_HTTPS_PORTS`](https://learn.microsoft.com/aspnet/core/security/enforcing-ssl#port-configuration).

```yaml
services:
azure-app-configuration-emulator:
build:
context: https://github.com/tnc1997/azure-app-configuration-emulator.git
dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile
environment:
- ASPNETCORE_HTTP_PORTS=8080
- ASPNETCORE_HTTPS_PORTS=8081
volumes:
- ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
- ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.24.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6"/>
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6"/>
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.3"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1"/>
Expand Down
25 changes: 24 additions & 1 deletion src/AzureAppConfigurationEmulator/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Net.Http.Headers;
using Azure.Messaging.EventGrid;
using AzureAppConfigurationEmulator;
using AzureAppConfigurationEmulator.Authentication.Hmac;
Expand All @@ -22,7 +23,29 @@
options.AddOtlpExporter();
});

builder.Services.AddAuthentication().AddHmac();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "MicrosoftEntraId";
})
.AddHmac()
.AddJwtBearer("MicrosoftEntraId", options =>
{
options.TokenValidationParameters.ValidateAudience = true;
options.TokenValidationParameters.ValidateIssuer = true;

options.ForwardDefaultSelector = context =>
{
if (AuthenticationHeaderValue.TryParse(context.Request.Headers.Authorization, out var value))
{
if (value.Scheme.Equals("HMAC-SHA256", StringComparison.OrdinalIgnoreCase))
{
return HmacDefaults.AuthenticationScheme;
}
}

return null;
};
});

builder.Services.AddAuthorization();

Expand Down
4 changes: 3 additions & 1 deletion src/AzureAppConfigurationEmulator/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"Authentication": {
"DefaultScheme": "Hmac",
"Schemes": {
"Hmac": {
"Credential": "abcd",
"Secret": "c2VjcmV0"
},
"MicrosoftEntraId": {
"ValidAudience": "https://azconfig.io"
}
}
},
Expand Down

0 comments on commit 9549d5c

Please sign in to comment.