How can I use IdentityServer4 from inside and outside a docker machine?

If you are running your docker containers in same network, you can do the followings:

  1. Add IssuerUri in your identity server.
services.AddIdentityServer(x =>
            {
                x.IssuerUri = "http://<your_identity_container_name>";
            })

This will set your identity server's URI. Therefore, your other web api services can use this URI to reach your identity server.

  1. Add Authority in your web api that has to use identity server.
services.AddAuthentication(options =>
          {
              options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          }).AddJwtBearer(o =>
          {
              o.Authority = "http://<your_identity_container_name>";
              o.Audience = "api1"; // APi Resource Name
              o.RequireHttpsMetadata = false;
              o.IncludeErrorDetails = true;
          });

Ensure IssuerUri is set to an explicit constant. We had similar issues with accessing Identity Server instance by the IP/hostname and resolved it this way:

services.AddIdentityServer(x =>
{
    x.IssuerUri = "my_auth";
})

P.S. Why don't you unify the authority URL to hostname:5000? Yes, it is possible for Client and API both call the same URL hostname:5000 if:

  • 5000 port is exposed (I see it's OK)
  • DNS is resolved inside the docker container.
  • You have access to hostname:5000 (check firewalls, network topology, etc.)

DNS is the most tricky part. If you have any trouble with it I recommend you try reaching Identity Server by its exposed IP instead of resolving hostname.


To make this work I needed to pass in two environment variables in the docker-compose.yml and setup CORS on the identity server instance so that the API was allowed to call it. Setting up CORS is outside the remit of this question; this question covers it well.

Docker-Compose changes

The identity server needs IDENTITY_ISSUER, which is name that the identity server will give itself. In this case I've used the IP of the docker host and port of the identity server.

  mcoidentityserver:
    image: mcoidentityserver
    build:
      context: ./Mco.IdentityServer
      dockerfile: Dockerfile
    environment:
      IDENTITY_ISSUER: "http://10.0.75.1:5000"
    ports:
       - 5000:5000
    networks:
     - mconetwork

The API needs to know where the authority is. We can use the docker network name for the authority because the call doesn't need to go outside the docker network, the API is only calling the identity server to check the token.

  mcoapi:
    image: mcoapi
    build:
      context: ./Mco.Api
      dockerfile: Dockerfile
    environment:
      IDENTITY_AUTHORITY: "http://mcoidentityserver:5000"
    ports:
       - 56107:80
    links:
     - mcodatabase
     - mcoidentityserver
    depends_on:
     - "mcodatabase"
     - "mcoidentityserver"
    networks:
     - mconetwork

Using these values in C#

Identity Server.cs

You set the Identity Issuer name in ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        var sqlConnectionString = Configuration.GetConnectionString("DefaultConnection");

        services
            .AddSingleton(Configuration)
            .AddMcoCore(sqlConnectionString)
            .AddIdentityServer(x => x.IssuerUri = Configuration["IDENTITY_ISSUER"])
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(Config.GetClients())
            .AddCorsPolicyService<InMemoryCorsPolicyService>()
            .AddAspNetIdentity<User>();
    }

API Startup.cs

We can now set the Authority to the environment variable.

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        Authority = Configuration["IDENTITY_AUTHORITY"],
        RequireHttpsMetadata = false,
        ApiName = "api1"
    });

Drawbacks

As shown here, the docker-compose would not be fit for production as the hard coded identity issuer is a local IP. Instead you would need a proper DNS entry that would map to the docker instance with the identity server running in it. To do this I would create a docker-compose override file and build production with the overridden value.

Thanks to ilya-chumakov for his help.

Edit

Further to this, I have written up the entire process of building a Linux docker + ASP.NET Core 2 + OAuth with Identity Server on my blog.