Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add microsoft entra id authentication scheme #79

Merged
merged 8 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading