How can I use IdentityServer4 inside and outside the docker device?

I want to be able to authenticate from Identity Server (STS) outside and inside the docker machine.

I am having trouble setting up the correct authority, which works both inside and outside the container. If I set permissions on the internal name mcoidentityserver:5000 , then the API can authenticate, but the client cannot receive the token, since the client is outside the docker network. If I set the permissions on the external name localhost:5000 , then the client can get the token, but the API does not recognize the name of the permissions (since localhost is the host machine in this case).

What do I need to configure for this body? Or maybe I need to set up a docker network?

Diagram

The red arrow is the part that I'm having problems with. Three docker containers on the network, the client and PostgreSQL Admin, their ports and the red arrow showing where I think the problem lies.

Detail

I am setting up a Windows 10 docker development environment that uses the ASP.NET Core API (on Linux), Identity Server 4 (ASP.NET Core on Linux), and the PostgreSQL database. PostgreSQL is not a problem included in the diagram for completeness. It is mapped to 9876 because at the moment I also have a PostgreSQL instance running on the host. mco is the abbreviated name of our company.

I follow the instructions for Identity Server 4 to start and start.

the code

I do not include docker-compose.debug.yml because it ran commands that were specific to working in Visual Studio.

Docker-compose.yml

 version: '2' services: mcodatabase: image: mcodatabase build: context: ./Data dockerfile: Dockerfile restart: always ports: - 9876:5432 environment: POSTGRES_USER: mcodevuser POSTGRES_PASSWORD: password POSTGRES_DB: mcodev volumes: - postgresdata:/var/lib/postgresql/data networks: - mconetwork mcoidentityserver: image: mcoidentityserver build: context: ./Mco.IdentityServer dockerfile: Dockerfile ports: - 5000:5000 networks: - mconetwork mcoapi: image: mcoapi build: context: ./Mco.Api dockerfile: Dockerfile ports: - 56107:80 links: - mcodatabase depends_on: - "mcodatabase" - "mcoidentityserver" networks: - mconetwork volumes: postgresdata: networks: mconetwork: driver: bridge 

Docker-compose.override.yml

This is created by the Visual Studio plugin to enter additional values.

 version: '2' services: mcoapi: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" mcoidentityserver: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "5000" 

Dockerfile API

 FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Mco.Api.dll"] 

Identity Server Dock File

 FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app COPY ${source:-obj/Docker/publish} . EXPOSE 5000 ENV ASPNETCORE_URLS http://*:5000 ENTRYPOINT ["dotnet", "Mco.IdentityServer.dll"] 

API Startup.cs

Where we say the API to use the identity server and set permissions.

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { // This can't work because we're running in docker and it doesn't understand what localhost:5000 is! Authority = "http://localhost:5000", RequireHttpsMetadata = false, ApiName = "api1" }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } 

Identity Server Startup.cs

 public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } 

Identity Server Config.cs

 public class Config { public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("api1", "My API") }; } public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "client", // no interactive user, use the clientid/secret for authentication AllowedGrantTypes = GrantTypes.ClientCredentials, // secret for authentication ClientSecrets = { new Secret("secret".Sha256()) }, // scopes that client has access to AllowedScopes = { "api1" } } }; } } 

Client

Running in a console application.

 var discovery = DiscoveryClient.GetAsync("localhost:5000").Result; var tokenClient = new TokenClient(discovery.TokenEndpoint, "client", "secret"); var tokenResponse = tokenClient.RequestClientCredentialsAsync("api1").Result; if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return 1; } var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = client.GetAsync("http://localhost:56107/test").Result; if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = response.Content.ReadAsStringAsync().Result; Console.WriteLine(JArray.Parse(content)); } 

Thanks in advance.

+19
docker docker-compose identityserver4
source share
2 answers

Ensure that IssuerUri set to an explicit constant. We had similar problems accessing an instance of Identity Server by IP / hostname, and we solved this as follows:

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

PS Why don't you unify the hostname:5000 URL for hostname:5000 ? Yes, the client and API can call the same hostname:5000 URL hostname:5000 if:

  • Port 5000 is set (I see normally)
  • DNS is resolved inside the container dock.
  • You have access to hostname:5000 (check firewalls, network topology, etc.)

DNS is the hardest part. If you have any problems with this, I recommend that you try to contact Identity Server at the specified IP address, and not at hostname .

+8
source share

To do this, I needed to pass two environment variables to docker-compose.yml and configure CORS on the identity server instance so that the API was allowed to call it. Setting up CORS is beyond the scope of this question; This question is well illuminated.

Docker-compose changes

The identity server requires IDENTITY_ISSUER , that is, the name that the identity server will give itself. In this case, I used the host IP docking station and the identity server port.

  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 credentials are. We can use the docker network name for authority, because the call should not go beyond the docker network, the API only calls the identification server to verify 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 publisher 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

Now we can set Authority for the environment variable.

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

disadvantages

As shown here, docker-compose is not suitable for production, since the hard-code identifier publisher is a local IP. Instead, you will need the correct DNS record that will be mapped to the docker instance with the identity server running on it. To do this, I would create a docker-compose override file and compile the production with an overridden value.

Thanks to Ilya-Chumakov for your help.

edit

In addition to this, I wrote the entire process of creating a Linux + ASP.NET Core 2 + OAuth docker with Identity Server on my blog.

+6
source share

All Articles