.NET 7 WebApp with Https in Docker, secured by Azure AD
Tags: webdev, docker, dotnet, security
Published: 2022-04-14 02:05:26

I have to admit, this was a weird and tough one. All I wanted to do was run an ASP.NET Razor Pages app inside a container using Https and Azure Active Directory authentication. Well, this was definitely a fun one to solve. In this blog post I'll show you exactly how to do the following:

- export the .NET Dev certs to use with Docker
- configure the Azure AD authentication
- configure the SameSite cookie policy in .NET
- configure Docker to use the dev-certs

### Export the .NET Dev certs

In order to be able to work in local dev with Docker and HTTPS we need a certificate. And the easiest way to get a cert is to use the .NET dev certs - they are free and available. 

> Note: this should only be used for local Dev to unblock certain scenarios

Open your favorite terminal and type the following

```
dotnet dev-certs https --clean
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p <your secret>

### If you're using PowerShell, then you have to use the following command instead
dotnet dev-certs https -ep $env:USERPROFILE\.aspnet\https\aspnetapp.pfx -p <your secret>

dotnet dev-certs https --trust
```

### Configure the ASP.NET Core Web App

By default, ASP.NET Core website have HSTS and HTTPS redirection enabled. This means that when running the application, it will expect and redirect all requests to the TLS-enabled port. I like to set mine to the standard `5001` since .NET 6 and 7 are now randomizing the app ports. You can change the default ports in the `Properties\launchSettings.json` file. This is what mine looks like:

```
{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:43362",
      "sslPort": 44382
    }
  },
  "profiles": {
    "AzureADAuthInDocker": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000;https://localhost:5001",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

```
Implement the necessary authentication, you need to add the following NuGet packages:
- Microsoft.Identity.Web 
- Microsoft.Identity.Web.UI 

In the `appsettings.json` file add the following **AzureAD** specific settings:

```
"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<your Azure AD tenant name>.onmicrosoft.com",
    "TenantId": "<your Azure AD tenant ID>",
    "ClientId": "<your Azure AD App Registration client ID>",
    "CallbackPath": "/signin-oidc"
  },
```

Update the `program.cs` add the following code:

```
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages()
    .AddMicrosoftIdentityUI();


// code ommitted

app.UseAuthentication();
app.UseAuthorization();

```

At this point you can run the application locally using `dotnet run` and if everything has been configured correctly, you should be prompted to authenticate with your organizational (Azure AD) account

### Create a Cookie Policy

If you plan on using Cookies in your solution, then you need to add a cookie policy. In `Program.cs` above the authentication settings, add the following code:

```
builder.Services.Configure<CookiePolicyOptions>(
    options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
        options.Secure = CookieSecurePolicy.Always;
    });
```

Then, just above the `app.UseAuthentication()` you need to add this:

```
app.UseCookiePolicy();
```
Run the application and test that everything's working as expected!

If you get a cookie error as per below, make sure you have the right settings. Check everything and try again:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jbnxd3t20x4be88ls6gj.png)

### Configure Docker for our web app
There are some prerequisites when it comes to working with Docker, namely that you need to have Docker Desktop installed and WSL2 enabled if you want to use Linux containers. Make sure to have these installed correctly.

When it comes to creating the Docker files, I like to lean on VS Code to do the heavy lifting for me. You can find the information on how to set things up for your ASP.NET app [here](https://code.visualstudio.com/docs/containers/quickstart-aspnet-core)

Running the appropriate Docker commands from the VS Code pallette, should generate the following 3 files:

- Dockerfile
- docker-compose.yml
- docker-compose.debug.yml

I left my `Dockerfile` mostly unchanged apart from the **ENV ASPNETCORE_URLs** value which I updated to: `http://+:5000;https://+:5001`

My `docker-compose.yml` file looks like this:

```
version: '3.4'

services:
  azureadauthindocker:
    image: azureadauthindocker
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - 5000:5000
```
And finally, the bulk of the changes took place in the `docker-compose.debug.yml`

```
# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.

version: '3.4'

services:
  azureadauthindocker:
    image: azureadauthindocker
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - 5000:5000
      - 5001:5001
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:5001;http://+:5000
      - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
      - ASPNETCORE_Kestrel__Certificates__Default__Password=<your secret>
    volumes:
      - ~/.vsdbg:/remote_debugger:rw
      - C:\Users\chmatsk\.aspnet\https:/https/:ro
```
With these changes, I can run my web app inside Docker directly from VS Code. 

And this is an example with everything working end-to-end

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b1af766lnggl73m43nur.gif)

If you want to run the container from the command line, then this is the command you need to use

```
docker run --rm -it -p 5000:5000 -p 5001:5001 -e ASPNETCORE_URLS="https://+:5001;http://+:5000" -e ASPNETCORE_HTTPS_PORT=5001 -e ASPNETCORE_Kestrel__Certificates__Default__Password="<your secret>" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v C:\Users\chmatsk\.aspnet\https:/https/ <your image>
```
Running this command should produce the following output and your website should be accessible on `https://localhost:5001`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxw2nh6ej4q25b0haukq.png)
 

Reach out if you have any questions!