Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature suggestion: Identity Server - multiple valid issuers #5589

Closed
leonkosak opened this issue Sep 23, 2020 · 12 comments
Closed

Feature suggestion: Identity Server - multiple valid issuers #5589

leonkosak opened this issue Sep 23, 2020 · 12 comments

Comments

@leonkosak
Copy link
Contributor

Based on "modern" deployment methods (Docker, reverse proxy,...) there are some challenges how to deal with authentication (especially with "issuer").
For instance: #5125 (comment)

Related posts:

https://leastprivilege.com/2017/10/09/new-in-identityserver4-v2-simplified-configuration-behind-load-balancers-or-reverse-proxies/

https://blog.novacare.no/identity-multiple-valid-issuers-jwt/

Currently in abp (and abp Commercial) is just "Authority" url in appsettings.json configuration file, which is "PublicOrigin" (Issuer URL).

Wouldn't be useful if there would be additional parameter in appsettings.json which would be "PrivateOrigin" (for instance Docker internal URL/IP to Identity Server)? Or maybe comma-separated valid issuers?
Thi "PrivateOrigin" would be empty by default.

With such additional configuration option, this could be additional solution to "loopback" from external IP/URL back to Identity Server in Docker for instance.

Are there any additional security-related concerns if this functionality (configuration) would be implemented?

Thanks for feedback. :)

@leonkosak
Copy link
Contributor Author

Now, when I look for new "Authorization Code Flow for the Angular UI" - is this maybe solution for "issuer problem" described in this topic?
Because of redirects to and from IDS back to Angular app, property "Authority" on API side (appsettings.json) could be an internal address (for instance in Docker network) to IDS?

@hikalkan, @maliming? Do I understand correctly this new workflow for Angular app? Does it solve this particular "issuer" problem?
(In my case - API and IDS are separated)

@leonkosak
Copy link
Contributor Author

leonkosak commented Oct 1, 2020

@hikalkan and @maliming, @ebicoglu I found solution (and is not a hack!) for this.

I suggest, that you add two additional parameters (which) in appsettings.json under AuthServer object in HttpApi.Host project (not in IdentityServer project in case of separated IDS).

"AuthServer": {
"Authority": "https://localhost:44334",
"ValidIssuers": "https://my_first_url.com,https://my_second_url.com", //comma-separated URL/IPs - just like CorsOrigins
"RequireHttpsMetadata": true //default is true
}

"RequireHttpsMetadata" is not related directly, but it would nice to have this also in settings (rather than hardcoded directly in .cs code).

So instead of this code in xyzHttpApiModule.cs:

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = true;
                    options.Audience = "ttbzr";
                });
}

I suggest something like this:

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    context.Services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication("Bearer", options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = true; //TODO: Read from appsettings.json
                    options.TokenValidationParameters.ValidIssuers = new[] { TODO: Read from appsettings.json };
                    options.Audience = "ttbzr";
                }, null);
}

I used different overload for AddIdentityServerAuthentication method.

image

You use the first method (blue underline), but I use the last one (red underline).
Keep in mind that I am not sure that "null" parameter value (last one) is correct.

This is applicable to abp and abp Commercial!

Please, let me know if you will do this change in configuration.
I can confirm, that this is good and elegant solution when application is deployed behind reverse-proxy or in Docker environment when IP addresses are translated multiple times (from "global internet address" via internal IP (10.xxx) to Docker internal IP addresses.

@maliming maliming self-assigned this Oct 29, 2020
@maliming maliming added this to the 4.0-preview milestone Oct 29, 2020
@maliming
Copy link
Member

hi @leonkosak I will check it, Thanks.

@wjkhappy14
Copy link

[14:26:26 INF] Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed.

@wjkhappy14
Copy link

options.TokenValidationParameters.ValidateAudience = false;

@maliming
Copy link
Member

maliming commented Nov 5, 2020

hi @leonkosak

We should continue to use AddJwtBearer instead of AddIdentityServerAuthentication #5247

Maybe we shouldn't add ValidIssuers to the template project, it is better for developers to apply it themselves.

context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
      .AddJwtBearer(options =>
      {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
            options.Audience = "MyProjectName";
            options.TokenValidationParameters.ValidIssuers = configuration.GetSection("AuthServer:ValidIssuers").Get<string[]>();
      });
"AuthServer": {
      "Authority": "https://localhost:44301",
      "RequireHttpsMetadata": "true",
      "ValidIssuers": [
        "https://localhost:44301",
        "https://localhost:44302"
      ]
}

https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/Microsoft.IdentityModel.Tokens/Validators.cs#L230-L252

@maliming maliming removed this from the 4.0-preview milestone Nov 5, 2020
@maliming maliming removed their assignment Nov 5, 2020
@leonkosak
Copy link
Contributor Author

@maliming, any particular reason for that?

@maliming
Copy link
Member

maliming commented Nov 5, 2020

These codes are useful for specific deployment methods (Docker, reverse proxy,...). If developers find a problem, they should understand the problem and use ValidIssuers. This is my thought. : )

@leonkosak
Copy link
Contributor Author

That's true. But a lot of deployments nowadays are at least behind reverse proxy. :) Having "placeholder" in appsettings.json for valid issuers saves time in all aspects (and it's not something so specific like having for instance RabbitMQ part of configuration in appsettings.json).

@DWCS-Dworrall
Copy link

hi @leonkosak

We should continue to use AddJwtBearer instead of AddIdentityServerAuthentication #5247

Maybe we shouldn't add ValidIssuers to the template project, it is better for developers to apply it themselves.

context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
      .AddJwtBearer(options =>
      {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
            options.Audience = "MyProjectName";
            options.TokenValidationParameters.ValidIssuers = configuration.GetSection("AuthServer:ValidIssuers").Get<string[]>();
      });
"AuthServer": {
      "Authority": "https://localhost:44301",
      "RequireHttpsMetadata": "true",
      "ValidIssuers": [
        "https://localhost:44301",
        "https://localhost:44302"
      ]
}

https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/Microsoft.IdentityModel.Tokens/Validators.cs#L230-L252

Adding this code into the API fixed a massive headache that I was having with trying to implement domain resolved multi-tenancy in my application. The identity server in my application is separated from the API and with this architecture, the valid issuer list is required.

This is not currently in the documentation (which is quite sparse for the trouble that I had implementing it!) and even the latest sample released with 4.3 didn't really help as my architecture of Angular with a seperated identity server was not covered.

In my opinion, this solution needs to be added to the template and the documentation for multi-tenancy needs to be updated to handle this and also the configuration of SSL certificates for the sub-domains whilst developing.

@leonkosak
Copy link
Contributor Author

@DWCS-Dworrall, yes - I can confirm that this part of code for "AuthServer:ValidIssuers" is crucial and must-have for most of abp-based installations (especially for containerized installations like Docker/Kubernetes).
If there won't be any response from Volo, I will make PR as soon as possible.

Offtopic:
I would also use .config for Logs.txt file location (this issue) with Serilog.Sinks, but it's also "developer decision".
The funny part is, that ASP.NET Boilerplate (and ASP.NET Zero) have this log configuration inside .config file....

@afresh
Copy link
Contributor

afresh commented Sep 9, 2021

I find this issue because I got a exception.

2021-09-09 08:30:09.161 +08:00 [INF] Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDX10205: Issuer validation failed. Issuer: 'http://127.0.0.1:51511'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'http://auth-server:51511'.
   at Microsoft.IdentityModel.Tokens.Validators.ValidateIssuer(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateIssuer(String issuer, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
2021-09-09 08:30:09.192 +08:00 [INF] Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'http://127.0.0.1:51511'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'http://auth-server:51511'.
2021-09-09 08:30:09.695 +08:00 [INF] Executing endpoint 'Volo.Abp.Identity.ProfileController.GetAsync (Volo.Abp.Identity.HttpApi)'
2021-09-09 08:30:09.754 +08:00 [INF] Route matched with {area = "identity", controller = "Profile", action = "Get"}. Executing controller action with signature System.Threading.Tasks.Task`1[Volo.Abp.Identity.ProfileDto] GetAsync() on controller Volo.Abp.Identity.ProfileController (Volo.Abp.Identity.HttpApi).
2021-09-09 08:30:09.969 +08:00 [INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
2021-09-09 08:30:10.018 +08:00 [WRN] ---------- RemoteServiceErrorInfo ----------
{
  "code": "Volo.Authorization:010001",
  "message": "Authorization failed! Given policy has not granted.",
  "details": null,
  "data": {},
  "validationErrors": null
}

Thanks for DWCS-Dworrall's answer.

Added docker-compose.override.yml.

    environment:
      - AuthServer__Authority=http://auth-server:51511
      - AuthServer__ValidIssuers__0=http://auth-server:51511
      - AuthServer__ValidIssuers__1=http://127.0.0.1:51511

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants