2

I have a .net Core 3.1 app running in an azure web app for containers(linux) service. This app is a web api project with an angular 9 front end. It uses Identity server 4 for authorization.

For reference I am using this clean architecture framework template for my project (the add docker support pr).

The app runs in the service just fine. The front end works and I can "log in" using ID4 and I can see that the authorization token are returned when I log in.

THE PROBLEM:

When I make a call to the backend web api from the front end angular client app I get the following error:

HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://*********.azurewebsites.net' is invalid"

I am tempted to add a manual setting for the IssuerUri but the identity server 4 docs recommend against doing this so I did not. Maybe having this run in docker makes it different.

I did have to add support for forwarding headers to get IS4 to work properly in startup.cs configureServices according to these docs for proxy load balancers. I had to add ASPNETCORE_FORWARDEDHEADERS_ENABLED=true to my application settings

When I compare fiddler results for the requests, I can see that the AspNetCore.Antiforgery is the same for login and web api calls but the .AspNetCore.Identity.Application value is different.

I am using nSwag to auto generate api service calls for angular if that makes a difference.

QUESTION:

can someone help me figure out why I can login but all web api requests fail with the unauthorized error above?

thanks in advance. JK

EDIT 1

I used fiddler to get the authorization token for the request and used jwt.io to parse it. The iss value was the same as the app/web api:

"iss": "https://******.azurewebsites.net", 

IS4 used this domain to log in and that worked properly. If that value is correct, is there another thing that might be wrong?

EDIT 2:

Just for more context.

My app uses in statup.cs Configure:

app.UseHsts();
app.UseHttpsRedirection();

As a result I needed to add the following code to make sure the headers get forwarded in the requests between app service's handling of the TSL, load balancer/proxy and my docker container (starup.cs ConfigureServices):

// the following is documented here:
// https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.1#forward-the-scheme-for-linux-and-non-iis-reverse-proxies-1
// it is needed to run kestrel in azure app service in http with header forwarding
if (string.Equals(
    Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
    "true", StringComparison.OrdinalIgnoreCase))
{
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                                   ForwardedHeaders.XForwardedProto;
        // Only loopback proxies are allowed by default.
        // Clear that restriction because forwarders are enabled by explicit 
        // configuration.
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });
}

I get the following error in the logs which confirm the same error above as an Issuer mismatch

Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDX10205: Issuer validation failed. Issuer: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Did not match: validationParameters.ValidIssuer ...

I am using the following default setup for the Jwt token:

 services.AddAuthentication().AddIdentityServerJwt();

If i navigate to the https://*******.azurewebsites.net/.well-known/openid-configuration/jwks I get the following JSON setting for my OIDC setup:

{
  "issuer": "https://*******.azurewebsites.net",
  "jwks_uri": "https://*******.azurewebsites.net/.well-known/openid-configuration/jwks",
  "authorization_endpoint": "https://*******.azurewebsites.net/connect/authorize",
  "token_endpoint": "https://*******.azurewebsites.net/connect/token",
  "userinfo_endpoint": "https://*******.azurewebsites.net/connect/userinfo",
  "end_session_endpoint": "https://*******.azurewebsites.net/connect/endsession",
  "check_session_iframe": "https://*******.azurewebsites.net/connect/checksession",
  "revocation_endpoint": "https://*******.azurewebsites.net/connect/revocation",
  "introspection_endpoint": "https://*******.azurewebsites.net/connect/introspect",
  "device_authorization_endpoint": "https://*******.azurewebsites.net/connect/deviceauthorization",
  "frontchannel_logout_supported": true,
  "frontchannel_logout_session_supported": true,
  "backchannel_logout_supported": true,
  "backchannel_logout_session_supported": true,
  "scopes_supported": [
    "openid",
    "profile",
    "CleanArchitecture.WebUIAPI",
    "offline_access"
  ],
  "claims_supported": [
    "sub",
    "name",
    ....
    "updated_at"
  ],
  "grant_types_supported": [
    "authorization_code",
    "client_credentials",
    "refresh_token",
    "implicit",
    "password",
    "urn:ietf:params:oauth:grant-type:device_code"
  ],
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "id_token token",
    "code id_token",
    "code token",
    "code id_token token"
  ],
  "response_modes_supported": ["form_post", "query", "fragment"],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "client_secret_post"
  ],
  "id_token_signing_alg_values_supported": ["RS256"],
  "subject_types_supported": ["public"],
  "code_challenge_methods_supported": ["plain", "S256"],
  "request_parameter_supported": true
}

I compared the Issuer in this document and they are the same as the one in the token as shown decoded above.

I am still not sure how to debug this to figure out where the issuer mismatch is happening.

NOTE: I have narrowed this down a bit. All calls to the built in/default IS4 endpoints work. Its only the custom webAPI endpoints I define in my controllers that are not validating the token properly.

Any webAPI endpoint with [Authorize] attribute fails with invalid issuer

EDIT 3:

Thanks to @d_f comment I used the IS4 docs for adding local API I added the following call to my services initialization in startu.ca configure services:

services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddLocalApiAuthentication();  // I added this line after the above line

I then changed the [Authorize] attribute at the top of my webAPI controller to:

//[Authorize]
[Authorize(IdentityServerConstants.LocalApi.PolicyName)]

However, I am still getting the same error. Only on my custom webAPI endpoints, the IS4 endpoints all work. Login works but not any web api endpoints that have [Authorize] attribute.

EDIT 4:

I removed the above settings and chnaged my services.AddAUthentication() to the following:

services.AddAuthentication()
    .AddIdentityServerJwt()
    .AddLocalApi(options =>
        options.ExpectedScope = "IdentityServer4");

I also tried:

services.AddAuthentication()
    .AddIdentityServerJwt()
    .AddLocalApi();

I used the policy name "IdentityServer4" because it appears to be a default policy within IS4

Here is what the full context looks like:

services.AddDefaultIdentity<ApplicationUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

services.AddAuthentication()
    .AddIdentityServerJwt() 
    .AddLocalApi(options =>
        options.ExpectedScope = "IdentityServer4");

This works locally on my machine with all these variations. Its just when run inside container in azure web app that I get the issuer failure for my custom webAPI endpoints.

SOLUTION:

I found a solution thanks to all the help here. IS4 out of the box attempts to set the ISS / Issuer automatically. This works locally but in my production environment my container run in azure web apps for containers. Azure places my container inside of another container for load balancing/proxy to handle the https encryption as well. As a result there is a difference between the auto detected IS4 issuer in my container and the azure web app URL.

By manually setting the issuer in my code the error went away and everything works.

You can do this in two places

  1. in your appsettings.jsson like:
     "IdentityServer": {
        "IssuerUri": "https://yourapp.azurewebsites.net", 
  1. or in code like this:
      services.AddIdentityServer(options =>
                {
                    options.IssuerUri = "https://your.azurewebsites.net/";
                })
                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

Hope this helps someone else and thanks again to all who helped here

12
  • What is the issuer in the access token? Does it match the expected issuer in the API? Commented Nov 12, 2020 at 19:13
  • How do I figure that out? Commented Nov 12, 2020 at 20:20
  • Pass the requests to the identity server or to the API through some proxy like Fiddler. Commented Nov 12, 2020 at 21:26
  • 1
    i missed the point you use "all in one" template, assumed your identityserver hosted separately. for all-in-one you can try to switch to services.AddAuthentication().AddLocalApi("token", isAuth => {/*customize validation here*/}); Commented Nov 20, 2020 at 12:56
  • 1
    @d_f thanks for your continued help. I am having trouble finding examples for AddLocalApi() I added Edit #4 to show two ways I tried to use it. Commented Nov 26, 2020 at 19:10

2 Answers 2

3

SOLUTION:

I found a solution thanks to all the help here. IS4 out of the box attempts to set the ISS / Issuer automatically. This works locally but in my production environment my container run in azure web apps for containers. Azure places my container inside of another container for load balancing/proxy to handle the https encryption as well. As a result there is a difference between the auto detected IS4 issuer in my container and the azure web app URL.

By manually setting the issuer in my code the error went away and everything works.

You can do this in two places

  1. in your appsettings.jsson like:
     "IdentityServer": {
        "IssuerUri": "https://yourapp.azurewebsites.net", 
  1. or in code like this:
      services.AddIdentityServer(options =>
                {
                    options.IssuerUri = "https://your.azurewebsites.net/";
                })
                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

Hope this helps someone else and thanks again to all who helped here

Sign up to request clarification or add additional context in comments.

Comments

1

You need to capture your token and use https://jwt.ms to parse it.

According to your error message: invalid token The issuer is invalid, so you should check the iss Claims in the token to make sure it is as expected in the API The issuer matches. see here.

enter image description here

3 Comments

hello, I have checked the authorization token and it shows "iss": "https://********.azurewebsites.net", which is my app running in azure. The IS4 server is running on that domain as well. I have had some problems with ssl setup, is it possible that if the request is http instead of https it would throw this error? thanks
the protocol must match , you cant mix HTTP and HTTPS
@JKing Hi,I have not tested using http, but, as Tore Nestenius said, you cannot mix HTTP and HTTPS.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.