Unable to Call API using my identity server from a native application

Someone asked on Stack Overflow:

I would like to make a Identity server and the API in two separate dockers for a native application (client mobile). It is running on an NGINX Reverse Proxy and Let’s Encrypt.

                               Dockers
                     ---------------------------
                    |       Reverse Proxy       |
                    |  -----------------------  |
   --------         | |    ----------------   | |
  | Mobile | ---------|-> | IdendityServer |  | |
   --------         | |   |   Port: 5000   |  | |
       |            | |    ----------------   | |
       |            | |            |          | |
       |            | |    ----------------   | |
       ---------------|-> |      API       |  | |
                    | |   |   Port: 5001   |  | |
                    | |    ----------------   | |
                    |  -----------------------  |
                    |                           |
                    |      ----------------     |
                    |     |   PostgreSQL   |    |
                    |     |   Port: 5432   |    |
                    |      ----------------     |
                     ---------------------------

With my current configuration:

  • The Reverse Proxy with Let’s Encrypt is well working from the mobile
  • The call API without [Authority] is well working from the mobile
  • The Identity server connection with the Hybrid flow is working and my user’s claims is listed

My codes are below.

IdentityServer Dockerfile

FROM microsoft/dotnet:2.0-sdk
COPY is4/* /app/
WORKDIR /app

ENV ASPNETCORE_URLS http://*:5000
EXPOSE 5000

ENTRYPOINT ["dotnet", "IdentityServer.dll"]

API Dockerfile

FROM microsoft/dotnet:2.0-sdk
COPY api/* /app/
WORKDIR /app

ENV ASPNETCORE_URLS http://*:5001
EXPOSE 5001

ENTRYPOINT ["dotnet", "ApiServer.dll"]

DockerCompose

version: '3'

services:
  identityserver:
    image: identityserver
    build:
      context: .
      dockerfile: IdentityServer/Dockerfile
    container_name: ids
    restart: always
    ports:
      - 5000:5000
#    expose:
#      - "5000"
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      VIRTUAL_PORT: 5000
      VIRTUAL_HOST: ids.mydomain.com
      LETSENCRYPT_HOST: ids.mydomain.com
      LETSENCRYPT_EMAIL: myuser@mydomain.com
      IDENTITY_ISSUER: "https://ids.mydomain.com"
      IDENTITY_REDIRECT: "com.mobiletest.nativeapp"
      IDENTITY_CORS_ORIGINS: "https://ids.mydomain.com"
    depends_on:
      - db
  apiserver:
    image: apiserver
    build:
      context: .
      dockerfile: ApiServer/Dockerfile
    container_name: api
    restart: always
    ports:
      - 5001:5001
#    expose:
#      - "5001"
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      VIRTUAL_PORT: 5001
      VIRTUAL_HOST: api.mydomain.com
      LETSENCRYPT_HOST: api.mydomain.com
      LETSENCRYPT_EMAIL: myuser@mydomain.com
      IDENTITY_AUTHORITY: "http://identityserver:5000"
      CLIENT_CORS_ORIGINS: "com.mobiletest.nativeapp"
    depends_on:
      - identityserver
      - db
    links:
      - identityserver
  db:
    image: postgresql:10
    build:
      context: .
      dockerfile: PostgreSQL/Dockerfile
    container_name: db
    restart: always
    ports:
      - "5432:5432"
    volumes:
      - /www/database:/var/lib/postgresql/data
    environment:
      - PGDATA=/var/lib/postgresql/data/pgdata

networks:
    default:
       external:
         name: nginx-proxy

IdentityServer Startup Code

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();

    services.AddMvc();

    // Configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer(opt =>
    {
        opt.IssuerUri = Configuration["IDENTITY_ISSUER"];
        opt.PublicOrigin = Configuration["IDENTITY_ISSUER"];
    })
    .AddCorsPolicyService<InMemoryCorsPolicyService>() // Add the CORS service
    .AddDeveloperSigningCredential()
    .AddInMemoryPersistedGrants()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>();

    services.AddAuthentication();

    // preserve OIDC state in cache (solves problems with AAD and URL lenghts)
    services.AddOidcStateDataFormatterCache("aad");

    // add CORS policy for non-IdentityServer endpoints
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy", policy =>
        {
            policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
        });
    });
} // ConfigureServices()

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentityServer();

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
} // Configure()

IdentityServer Config Code

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API")
        {
            ApiSecrets = { new Secret("secret".Sha256()) }
        }
    };
}

public static IEnumerable<Client> GetClients()
{
    // client credentials client
    return new List<Client>
    {
        new Client
        {
            ClientId = "native.hybrid",
            ClientName = "Native Client (Hybrid with PKCE)",
            AllowedGrantTypes = GrantTypes.Hybrid,
            RequirePkce = true,
            RequireConsent = false,
            //RequireClientSecret = false,
            ClientSecrets = { new Secret("secret".Sha256()) },                   
            RedirectUris = { Configuration["IDENTITY_REDIRECT"] + "://signin-oidc" },
            PostLogoutRedirectUris = { Configuration["IDENTITY_REDIRECT"] + "://signout-callback-oidc" },
            AllowedScopes = { "openid", "profile" },
            AllowedCorsOrigins = { Configuration["IDENTITY_CORS_ORIGINS"] },
            AllowOfflineAccess = true,
            //AllowAccessTokensViaBrowser = true
            RefreshTokenUsage = TokenUsage.ReUse
        }
    };
} // GetClients()

Api Config code

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore()
        .AddAuthorization()
        .AddJsonFormatters();
    if (Configuration["CLIENT_CORS_ORIGINS"] == "")
    {
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder
                .AllowAnyMethod()
                .AllowAnyOrigin()
                .AllowAnyHeader());
        });
    }
    else
    {
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder
                .AllowAnyHeader()
                .AllowAnyMethod()
                .WithOrigins(Configuration["CLIENT_CORS_ORIGINS"]));
        });
    }
    services.AddAuthentication("Bearer");
    services.AddAuthentication(options => //adds the authentication services to DI
    {
        //We are using a cookie as the primary means to authenticate a user (via “Cookies” as the DefaultScheme). We set the DefaultChallengeScheme to “oidc” because when we need the user to login, we will be using the OpenID Connect scheme.
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
        .AddCookie("Cookies")       //add the handler that can process cookies
        .AddOpenIdConnect("oidc", options => //configure the handler that perform the OpenID Connect protocol
        {
            options.SignInScheme = "Cookies"; //is used to issue a cookie using the cookie handler once the OpenID Connect protocol is complete
            options.Authority = Configuration["IDENTITY_AUTHORITY"]; //indicates that we are trusting IdentityServer
            options.RequireHttpsMetadata = false;
            options.ClientId = "native.hybrid";
            options.SaveTokens = true;
            options.ClientSecret = "secret"; //used to persist the tokens from IdentityServer in the cookie
            options.ResponseType = "code id_token";
        });

    services.AddMvc();
} // ConfigureServices()

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();

    app.UseCors("CorsPolicy");

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseMvc();
} // Configure()

API Controller code

[Route("api/[controller]")]
[EnableCors("CorsPolicy")]
[Authorize]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "testvalue1", "testvalue2" };
    }
} 

On Xamarin for the client mobile

var options = new OidcClientOptions
{
    Authority = "https://ids4.syladebox.com",
    ClientId = "native.hybrid",
    ClientSecret = "secret",
    //Scope = "openid profile api1 offline_access",
    Scope = "openid profile offline_access",
    ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

    RedirectUri = "com.mobiletest.nativeapp://signin-oidc",
    PostLogoutRedirectUri = "com.mobiletest.nativeapp://signout-callback-oidc",

    //Flow = OidcClientOptions.AuthenticationFlow.Hybrid,
    //Policy = policy,

    //Browser = new SFAuthenticationSessionBrowser()
    // new in iOS 12
    Browser = new ASWebAuthenticationSessionBrowser()
    //Browser = new PlatformWebView()
};

_client = new OidcClient(options); 

var result = await _client.LoginAsync(new LoginRequest());

if (result.IsError)
{
    OutputText.Text = result.Error;
    return;
}

if (result.AccessToken != null)
{
    var client = new HttpClient();
    client.SetBearerToken(result.AccessToken);
    var response = await client.GetAsync("https://api.mydomain.com/api/values");
    if (!response.IsSuccessStatusCode) 
    {
        OutputText.Text = response.ReasonPhrase;
        return;
    }

    var content = await response.Content.ReadAsStringAsync();
    OutputText.Text = JArray.Parse(content).ToString();
}

Problem not resolved:

The problem is to unable to invoke the API on behalf of my user. It returns either or “No authorized” either or “Bad gateway” after:

response = await client.GetAsync("https://api.mydomain.com/api/values"); 

These errors are depending of the environment variables in the IdentityServer and Api dockers.

My current environment variables are:

IDENTITY_ISSUER: “https://ids.mydomain.com

IDENTITY_REDIRECT: “com.mobiletest.nativeapp”

IDENTITY_CORS_ORIGINS: “https://ids.mydomain.com

IDENTITY_AUTHORITY: “http://identityserver:5000

CLIENT_CORS_ORIGINS: “com.mobiletest.nativeapp”

The API call (https://api.mydomain.com/api/values) returns “Bad gateway”.

I think that IDENTITY_ISSUER, IDENTITY_REDIRECT are corrects because the Identity server connexion is successful.

Is the problem coming from the other environment variables (IDENTITY_CORS_ORIGINS, IDENTITY_AUTHORITY and CLIENT_CORS_ORIGINS) or the Identity Server/API codes?

Update January, 26:

To make sure if my API program works, I redid the API program to the simplest:

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

            services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>
                 {
                     options.Authority = Configuration["IDENTITY_AUTHORITY"];
                     options.ApiName = "api";
                     //options.ApiSecret = "secret";
                 });

            // Add CORS policy for non-IdentityServer endpoints
            services.AddCors(options =>
            {
                options.AddPolicy("api", policy =>
                {
                    policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
                });
            });
        } // ConfigureServices()

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseAuthentication();

            app.UseCors("api");

            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            app.UseMvc();
        } // Configure()

with the api controller:

    [Route("api/[controller]")]
    [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)]
    public class ValuesController : Controller
    {
        // GET api/values
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "testvalue1", "testvalue2" };
        }
     }

and I use in my first test the other identity server :

demo.identityserver.io

For this test, I make the following configuration:

  apiserver:
    ...
    ports:
      - 5001:80
    environment:
      ...
      IDENTITY_AUTHORITY: "https://demo.identityserver.io"
      #CLIENT_CORS_ORIGINS (omitted in the code)

My OidcClientOptions in the client code is:

            var options = new OidcClientOptions
            {
                Authority = "https://demo.identityserver.io",
                ClientId = "native.hybrid",
                Scope = "openid profile email api offline_access",
                ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

                RedirectUri = "com.mobiletest.nativeapp://callback",
                PostLogoutRedirectUri = "com.mobiletest.nativeapp://callback",
                Browser = new ASWebAuthenticationSessionBrowser()
            };

The login function is in my first topic.

  • The Identity server connection with the Hybrid flow is working
  • The API call is successful!

As demo.identityserver.io is the demonstration identity server, I have a doubt if it works as the case of production then I tested an other identity server (Okta) with the same API program:

dev-xxxxxx.okta.com

For this test, I make the following configuration:

  apiserver:
    ...
    ports:
      - 5001:80
    environment:
      ...
      IDENTITY_AUTHORITY: "https://dev-xxxxxx.okta.com"
      #CLIENT_CORS_ORIGINS (omitted in the code)

My OidcClientOptions in the client code is:

            var options = new OidcClientOptions
            {
                Authority = "https://dev-xxxx.okta.com",
                ClientId = "xxxxxxxxxxxxxxxxxxx", // ClientId is hidden in this topic

                Scope = "openid profile email offline_access",
                ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,

                RedirectUri = "com.okta.dev-xxxxxx:/callback",
                PostLogoutRedirectUri = "com.okta.dev-xxxxxx:/callback",

                Browser = new ASWebAuthenticationSessionBrowser()
            };
  • The Identity server connection with the Hybrid flow is working
  • The API call isn’t working, it returns the message “Unauthorized”.

With the two tests, I can not tell if my API program works well.

Could you help me? Thank you very much!

I posted the following answer, which was chosen as the accepted answer:

Have you tried using .AddJwtBearer() in your API instead of just .AddAuthentication("Bearer")?

Something like this:

services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.Audience = "api1";
    });

Source: http://docs.identityserver.io/en/latest/quickstarts/1_client_credentials.html

While the source is referring to client credentials, I think you probably want to be using Jwt Tokens regardless of the client type.


Originally posted on Stack Overflow — 0 upvotes (accepted answer). Licensed under CC BY-SA.

signed letter b

Dad. Geek. Gamer. Software developer. Cloud user. Old Car enthusiast.  Blogger.


Top Posts


profile for Nate on Stack Exchange, a network of free, community-driven Q&A sites
a proud member of the blue team of 512KB club
Thoughts, opinions, and ideas shared here are my own. © 2026 Nate Bross.