6
votes

Is there any way to tell IdentityServer4's authentication system to allow multiple issuers for the tokens?

I have an application that is using Identity Server to issue bearer tokens, and as long as the front end and the back end use the same URL to get tokens from authentication works fine.

However, I now have a need to have the same site accessed through multiple CNAMEs, meaning that the client will request tokens from two different URLs.

The error that is sent to the logs is:

info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[7]

Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'http://domainb.com'. Did not match: validationParameters.ValidIssuer: 'http://domaina.com' or validationParameters.ValidIssuers: 'null'.

The presence of a ValidIssuers collection seems to indicate that you can set multiple places from which the API will accept tokens, but I cannot find anything like that exposed in options exposed by UseIdentityServerAuthentication.

I am aware of the Authority option, but that only allows me to set a single valid authority.

Is there are any way of setting multiple valid issuers, or setting it to use something other than the hostname as the issuer id?

UPDATE

My identity server configuration on the server side looks like this:

services.AddIdentityServer(options => { 
                             options.IssuerUri = "http://authserver"; })
     .AddAspNetIdentity<ApplicationUser>();

this is from the auth server side of things.

On the client API, the UseIdentityServerAuthentication call looks like this:

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions()
{
    Authority = AppSettingsConfigurationRoot["Authentication:AuthorityEndpoint"],
    RequireHttpsMetadata = false,
    ApiName = "rqapi",
    AutomaticAuthenticate = true,
    ClaimsIssuer = "http://localhost:5001"
});

The address in the {{AppSettingsConfigurationROot["Authentication:AuthorityEndpoint"] is usually set at the public DNS name of the server so that the token issuer as seen by AngularJS matches the URL of the IdentityServer from the point of view of the C# API.

1
Can you please include your .AddIdentityServer() configuration from your Identity Server.Brad
I've added that to the question - thanks!Richard Comish
Remove the IssuerUri in your options. The docs state that the Issuer name will be inferred from the request when it is not provided.Brad
It will - but if I do that then it will work for one CNAME (as long as the API calls the auth service with the same name that the Angular JS app used through the /token endpoint. The other CNAME will throw the invalid issuer error because it's getting a certificate from the 1st CNAME, but expecting one from the 2nd CNAME.Richard Comish
Have you tried removing the ClaimsIssuer and setting all your CNAME's in ValidIssuers?Brad

1 Answers

5
votes

As Original Poster wrote in a comment, the (now, 2020, deprecated) IdentityServer4.AccessTokenValidation package doesn't expose the right options. To read more about the recent deprecation check this blogpost, but if you still are using it, here's how I solved this issue.

The AddIdentityServerAuthentication(...) extension method is a wrapper (the code is super readable!) to combine two authentication schemes:

  1. JwtBearer
  2. OAuth2Introspection

It uses its own configuration class, and simply doesn't expose all the JwtBearer options (possibly just an omission, possibly because some options are not valid for both schemes.

If -like me- you only need JwtBearer you might get away with simply using just that, and using the ValidIssuers array. So:

services.AddAuthentication("Bearer")
    .AddJwtBearer(options =>
    {
        options.Authority = "https://example.org";
        options.Audience = "foo-api"; // options.ApiName in the IDS4 variant
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuers = new[]
            {
                "https://example.org", // first issuer
                "https://example.com", // some other issuer
            },
            NameClaimType = "name", // To mimick IDS4's variant
            RoleClaimType = "role", // To mimick IDS4's variant
        };
    });

As far as I understand, this will use example.org as the Authority and get the openid-configuration and so forth from that domain. But any JWT token offered to this API would be accepted as long as one of the ValidIssuers is the iss (issuer claim) in the token.