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

docker, docker-compose, identityserver4

Solution

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`.

Problem

I want to be able to authenticate against an Identity Server (STS) from outside and inside a docker machine. I am having trouble with setting the correct authority that works both inside and outside the container. If I set the authority to the internal name `mcoidentityserver:5000` then the API can authenticate but the client cannot get a token as the client lies outside of the docker network. If I set the authority to the external name `localhost:5000` then the client can get a token but the API doesn't recognise the authority name (because `localhost` in this case is host machine). What should I set the Authority to? Or perhaps I need to adjust the docker networking? Diagram The red arrow is the part that I'm having trouble with. Detail I am setting up a Windows 10 docker development environment that uses an ASP.NET Core API (on Linux), Identity Server 4 (ASP.NET Core on Linux) and a PostgreSQL database. PostgreSQL isn't a problem, included in the diagram for completeness. It's mapped to 9876 because I also have a PostgreSQL instance running on the host for now. `mco` is a shortened name of our company. I have been following the Identity Server 4 instructions to get up and running. Code I'm not including the `docker-compose.debug.yml` because it has run commands pertinent only to running 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 inject extra values. ``` version: '2' services: mcoapi: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" mcoidentityserver: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "5000" ``` API Dockerfile ``` FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Mco.Api.dll"] ``` Identity Server Dockerfile ``` 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 tell the API to use the Identity Server and set the Authority. ``` 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 app. ``` 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.

Original source

Related problems