Hosting asp.net core + ReactJS web app with SSL containing multiple CN or domain names is causing invalid issuer error

I am facing the following problem while hosting a web app built with asp.net core 3.1 and React.

We have used default visual studio template for React. ASP.NET Identity is used for authentication and authorization.

Authentication and Authorization work as expected as long as we host the website with an SSL certificate issued for single domain or CN. (e.g. example.com)

If we host he website with an SSL with multiple CNs (e.g. example.com, sub1.example.com, sub2.example.com), it works fine for any ONE of the domains. For the remaining domains we get the following behavior:

The login works as expected. The /connect/token path issues valid token. Once logged in, when we try to invoke any api (all apis are hosted under /api route), we get 401 unauthorized error. Error description in the header:

WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://sub1.example.com' is invalid".

I also tried parsing the issued token on jwt.io. The iss field (issuer) is https://sub1.example.com which exactly matches the error description. I cannot fathom why identity engine refuses to identify the issuer for which it issued token for.

Here is relevant snippet from Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    services.AddAuthentication()
            .AddIdentityServerJwt();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseAuthentication();
    app.UseIdentityServer();
    app.UseAuthorization();
}

Any ideas?


This is probably happening as a result of receiving the token from an instance of IdentityServer4 on one CN, and trying to validate it with a request to IdentityServer4 using another CN. The IdentityServer component that's rejecting the token is TokenValidator's ValidateJwtAsync method. This method passes in the issuer into JwtSecurityTokenHandler's ValidateToken as a property of TokenValidationParameters. The issuer is retrieved from either the issuer configured on the IdentityServerOptions in the 'AddIdentityServer' extension method, or is dynamically generated from the request.

I can think of one way to resolve the validation problems, and that is to set the issuer on the IdentityServerOptions using the delegate passed into AddIdentityServer. This will result in the same issuer being set for all tokens issued, regardless of the CN it was accessed from. This would allow IdentityServer a single source of truth for issuer information, and will allow IdentityServer to know which issuer to verify against when a token comes in for validation.

Other solutions of trying to maintain the issuer are heavily restricted by the TokenValidator being an internal class that can't be inherited and easily replaced with an implementation that will validate against a list of valid issuers. Additionally, the IdentityServerOptions that's configured to have the issuer uri is registered as a singleton and cannot have its values changed. Other contrived implementation could be devised like attempting to dynamically change the host value on the HttpContext with a middleware (which I'm not sure is even possible since I've never tried), but anything that goes against IdentityServer4's design decision is not advised.

Host ASP.NET Core on Windows with IIS, The DiscountASP.NET Windows 2016 hosting and Windows 2012 hosting platform supports ASP.NET Core. Formerly known as ASP.NET 5, you can take� Jakartawebhosting provide ASP.NET and .NET Core hosting which support the latest mssql 2016 and MySql Databases, we also support latest asp.met mvc and PHP all hosted on the latest IIS Windows Server.


Please check url http://{url}/.well-known/openid-configuration

This url is should be true

Following codes are worked different domain. Auth Startup

 services.AddIdentityServer(options =>
        {
            options.IssuerUri = Configuration["ServerSettings:Authority"].ToString();
            options.PublicOrigin = Configuration["ServerSettings:Authority"].ToString();
        })
            .AddDeveloperSigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryClients(Config.GetClients())
            .AddProfileService<ProfileService>();

Api Startup

services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = Configuration["ServerSettings:Authority"].ToString(); //"http://localhost:31864";
            options.RequireHttpsMetadata = false;
            options.ApiName = "api";
        });

Works in the same domain but if different domain you should specify this

ASP.NET Hosting, ASP.NET Core Hosting on a scalable cloud with flat rates. Try it free for 30 days. In general, to deploy an ASP.NET Core app to a hosting environment: Deploy the published app to a folder on the hosting server. Set up a process manager that starts the app when requests arrive and restarts the app after it crashes or the server reboots.


The new .Net (.net core) is highly configurable and modular. Usually the extension methods take a delegate which we can use to configure options. However, AddIdentityServerJwt method doesn't follow that convention.

I noticed long time ago that there is a property called ValidIssuers in TokenValidationParameters which can be configured with AddJwtBearer extension method. However, AddIdentityServerJwt extension method doesn't accept any options delegate as parameter.

It turns out that there is a special way to configure options.

services.AddAuthentication()
    .AddIdentityServerJwt();

services.Configure<JwtBearerOptions>(IdentityServerJwtConstants.IdentityServerJwtBearerScheme, options =>
{
    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
    {
        ValidIssuers = new string[] { "https://sub1.example.com", "https://sub2.example.com", "https://sub3.example.com" }
    };
});

Added this code and problem solved. Configuration can also be moved to appsettings.json.

SmarterASP.net, NET Core Hosting provider so you get the ultimate speed? That's A2 Hosting! Your site, application and all of your projects come hosted on our blazing fast� The DiscountASP.NET Windows 2016 hosting and Windows 2012 hosting platform supports ASP.NET Core. Formerly known as ASP.NET 5, you can take advantage of ASP.NET Core with Visual Studio 2015 and 2017. Hosting plan details » Purchase a plan now » We support both Framework-Dependent deployment and "self-contained" deployment.


ASP.NET Core Hosting, ASP.NET hosting starting at $3.95. Our Windows hosting platform supports Full Trust, ASP, Access, SQL, PHP, MySQL. ASP.NET hosting trusted by developers since 2003. Reliable full trust .NET Core, SQL, MVC, Access, ASP hosting.


ASP.NET Core hosting at DiscountASP.NET, Windows Hosting � For Windows developers: Run Windows Server 2019 with the latest versions of ASP.NET 4.8 and .NET Core � IONOS by 1&1. Whenever you create an ASP.NET Core application, by default it contains an internal server provided by a .NET Core that is called Kestrel. Due to this server, we can run ASP.NET Core apps on any platform like Windows, Mac or Linux. Before getting into the details about hosting models, let's first see what is the Kestrel server.


ASP.NET Core Hosting, ASP.NET hosting starting at $3.95. Our Windows hosting platform supports Full Trust, ASP, Access, SQL, PHP, MySQL.