Running IdentityServer4 in a Docker Container

Utilizing a secure identity provider is essential to any web platform. Many cloud based solutions such as Auth0, Azure B2C, or AWS SSO are available in the marketplace however, using a cloud service isn’t always the best choice. In the situations where I need full control over my identity platform, IdentityServer4 is my solution of choice. I have used IdentityServer4 on several projects and have always been impressed with the extensiblity, documentation, and overall ease of implementation.

It is important to keep in mind that IdentityServer4 is a framework. This means you are responsible for implementing and hosting it. With the popularity of tools like Docker, one might ask how IdentityServer4 can fit into an overall containerization strategy. Fortunately, there are many sample projects available for IdentityServer4 running in ASP.NET Core. In this article, we will be taking it one step further by building and hosting IdentityServer4 in a Docker Container.

Creating The Solution

The first step is creating an ASP.NET Core Web Application to host our identity server. A new project can be created from Visual Studio 2017 with the File | New | Project menu item. Once selected, the New Project dialog will be displayed. Search for ASP.NET Core Web Application. If this template is missing, the .NET Core cross platform development toolset will need to be installed.

Next, you will be presented with a second dialog. Use the Empty template and select .NET Core as your framework. With one of the primary focuses of this article being Docker, be sure to also select the Enable Docker Support checkbox. If you do not already have Docker Desktop installed on your machine, you can download it here.

The OS field allows us configure our docker container for Windows or Linux. In this post, we will be using a more versatile and lightweight Linux container. In the majority of the cases, Linux is the route you will want to go and the great news is, ASP.NET Core makes this possible!

Configuring for Linux

If you mistakenly configured your project for Windows containers, don’t worry. With a few modifications, we can reconfigure the project to run in a Linux container. First, we must update the project for to target the Linux OS.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    <UserSecretsId>ee362688-ecb3-49a0-a391-ac73e98d07a7</UserSecretsId>
  </PropertyGroup>

  ...

Next, we need to modify the Dockerfile. Updating the following lines will switch to the .NET Core base Linux image.

FROM microsoft/dotnet:2.2-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["JrTech.Identity.Web/JrTech.Identity.Web.csproj", "JrTech.Identity.Web/"]
RUN dotnet restore "JrTech.Identity.Web/JrTech.Identity.Web.csproj"
COPY . .
WORKDIR "/src/JrTech.Identity.Web"
RUN dotnet build "JrTech.Identity.Web.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "JrTech.Identity.Web.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "JrTech.Identity.Web.dll"] 

Adding IdentityServer

At this point, we will have an empty ASP.NET Core web application. The next step is incorporating IdentityServer4 which can be done by adding the IdentityServer4 nuget package. Since this is an ASP.NET Core application, we can do this with the dotnet CLI (sample below) or via the Nuget Package Manager in Visual Studio.

> dotnet add package IdentityServer4

Once installed, we can integrate IdentityServer4 into our web application by adding the following three lines of code in our Startup.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace JrTech.Identity.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            app.UseIdentityServer();

            app.UseMvcWithDefaultRoute();
        }
    }
}

We now have IdentityServer4 built into our web application! That said, there is still a big piece missing. There is no user interface. In previous versions, Identity Server used to distribute the user interface via nuget as well however, this made customizations a bit tricky. In IdentityServer4, the QuickStart UI is available on github and can be installed with the following PowerShell command.

> iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/master/getmaster.ps1'))

After running the command from your project folder, you will notice a QuickStart folder containing MVC Controllers, Views, etc. Personally, I love this extensibility model as it allows consumers of Identity Server to start with a workable sample and customize the functionality till their hearts content!

So lets take a look at what we have now. We run our solution and… wait for it… EXCEPTION! Identity Server requires a set of configuration values such as who can connect (Clients), what resources they are authorized to access (API Resources), and how they can retrieve their tokens (Grant Types). Typically, information of this sort would be stored in a database.

In Memory Configuration

IdentityServer4 ships with an in memory configuration model. This isn’t ideal in most production scenarios however, it is great for getting a quick prototype up an running. In this tutorial, we will be using the configuration found in the following Config class.

https://github.com/IdentityServer/IdentityServer4.Samples/blob/bdbcc4f2652e7589e4315fb9eb9a07aeeaf74e7c/Quickstarts/3_ImplicitFlowAuthentication/src/IdentityServer/Config.cs

With the config class added to our solution, we need to update our startup class to incorporate the values by adding the following “AddInMemory…” functions. Again, in a production scenario we would most likely want to persist this data. Fortunately, IdentityServer4 also works nicely with EntityFramework but, that is not the focus of this article.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddIdentityServer()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApis())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

Running The Solution

We should now be ready to run IdentityServer4 in a Docker container. In the event we get the following error message, we will need to ensure a drive is being shared in our Docker settings.

Your Docker settings can be accessed by right-clicking the Docker icon in the system tray and selecting Settings. If a drive is already being shared, you can try resetting your credentials.

With Identity Server running in a Docker container, we can try logging in with one of the test accounts. While we add more features to our project, Visual Studio Tools for Docker gives us the ability to debug within our container and the means publish our project to a container repository like Docker Hub or a private Azure Container Registry.

Happy Coding!