System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'System.String' with .NET 5
Asked Answered
A

15

23

I've been implementing IdentityServer4 to provide authorization for my React application. I have this working in my local dev environment, but am running into issues after deployed to IIS in Windows Server 2016. I am able to generate an access token via the /connect/token endpoint, but when I attempt to access a protected API using the token I get the following exception:

System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'System.String'.
 ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'System.String'.
 ---> System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. (dev-drydata-auth.universal-compliance.com:443)
 ---> System.Net.Sockets.SocketException (10060): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|283_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.DefaultConnectAsync(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.ConnectAsync(Func`3 callback, DnsEndPoint endPoint, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.ConnectAsync(Func`3 callback, DnsEndPoint endPoint, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)
   at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel)
   --- End of inner exception stack trace ---
   at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel)
   at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel)
   at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
   --- End of inner exception stack trace ---
   at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.SwaggerUiIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.RedirectToIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
   at Serilog.AspNetCore.RequestLoggingMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

My ConfigureServices as follows:

public void ConfigureServices(IServiceCollection services)
{
    ConfigureDryDataServices(services);

    services.AddControllersWithViews();

    services.AddCors(options =>
    {
        options.AddPolicy("AllOrigins",
        builder =>
        {
            builder.AllowAnyMethod()
                   .AllowAnyHeader()
                   .AllowAnyOrigin();
        });
    });
    services.AddScoped<IClaimsTransformation, WebAppCalimsTransformation>();
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(o =>
    {
        o.Authority = Configuration.GetValue<string>("AppSettings:Auth:ServerUrl");
        o.Audience = Configuration.GetValue<string>("AppSettings:Auth:Audience");
        o.RequireHttpsMetadata = false;
        o.Events = new JwtBearerEvents
        {
            OnTokenValidated = context =>
            {
                if (context.SecurityToken is JwtSecurityToken accessToken && context.Principal.Identity is ClaimsIdentity identity)
                {
                    identity.AddClaim(new Claim("access_token", accessToken.RawData));
                }

                return Task.CompletedTask;
            }
        };
    });

    services.AddAuthorization(options =>
    {
        options.AddPolicy("ApiReader", policy => policy.RequireClaim("scope", "my_api_software"));
        options.AddPolicy("admin", policy => policy.RequireClaim(ClaimTypes.Role, "admin"));
        options.AddPolicy("user", policy => policy.RequireClaim(ClaimTypes.Role, "user"));
    });

    services.AddHttpClient("Auth", config =>
    {
        config.BaseAddress = new Uri(Configuration.GetValue<string>("AppSettings:Auth:ServerUrl"));
    });

    // In production, the React files will be served from this directory
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "ClientApp/build";
    });


    services.AddSwaggerDocument(config => {
        config.OperationProcessors.Add(new OperationSecurityScopeProcessor("JWT token"));
        config.AddSecurity("JWT token", new OpenApiSecurityScheme
        {
            Type = OpenApiSecuritySchemeType.ApiKey,
            Name = "Authorization",
            Description = "Copy 'Bearer ' + valid JWT token into field",
            In = OpenApiSecurityApiKeyLocation.Header
        });
        config.PostProcess = (document) =>
        {
            document.Info.Version = "v1";
            document.Info.Title = "My API API";
            document.Info.Description = "ASP.NET 5.0 My API";
        };
    });
}

This is also working fine when deploying local IIS in my pc

Agc answered 11/12, 2021 at 3:5 Comment(2)
Stack trace says that your OpenId configuration discovery endpoint (.well-known/openid-configuration) is not accessibleSkindeep
@ AndrewSilver, Thank you for showing me the right direction. Seems I'm unable to access OpenId configuration discovery endpoint in the browser inside the server as well. I added the endpoint to the Trusted site list in the internet explorer but still blocking it. I think there should be some configuration inside the server to grant access by the server administrator.Agc
B
20

The problem is that the API can't reach your IdentityServer from within your deployment, as defined in the code here:

}).AddJwtBearer(o =>
{
    o.Authority = Configuration
        .GetValue<string>("AppSettings:Auth:ServerUrl");

So, via networking/DNS make sure the Authority in the API is actually reachable from within your server. Even if they are all reachable from your browser, it does not mean the API can reach your IdentityServer from within the local network on the server side.

One option to explore is to set the MetaDataAddress:

Try to set:

}).AddJwtBearer(o =>
{
    options.MetadataAddress = "http://1.2.3.4/.well-known/openid-configuration"; 
    ...
});

Where 1.2.3.4 is the local IP or internal name of the IdentityServer instance.

In this way, you separate the what should be the address inside the tokens and from where the application gets its metadata.

Belemnite answered 11/12, 2021 at 12:17 Comment(3)
You are correct, seems I'm unable to access OpenId configuration discovery endpoint in the browser inside the server as well. I added the endpoint to the Trusted site list in the internet explorer but still blocking it.Agc
It can also be a router / DNS thingy that the router blocks traffic to "itself". One option is to use an internal service name for traffic between services and use the public name for the public traffic. Alternatively, you could in the hosts file hardcode the IP-address for the domain of your IS. Depends a bit on your deployment setup. But this is a common problem.Belemnite
Did you solve the problem? If so, feel free to accept one of the answers given to you as the acceptable one.Belemnite
R
10

In my case SSL was blocking local access

In terminal

dotnet dev-certs https --trust

Robbery answered 18/4, 2022 at 13:12 Comment(2)
Yes, this link helped me: stackoverflow.com/a/71378016. But I didn't saw this answer.Crine
omg, this is it. I guess we all mess our local certificates at some point, what i don't understand is why after several attempts I was able to connect and sometimes it failed with this error. But after I executed this command, now the app runs properly without delays. Upvote for this answer.Allisan
F
3

In my case, microservices, the UI application auth URL and API auth URL was pointed to different ones.

Fara answered 7/9, 2022 at 7:15 Comment(0)
I
3

I'm connecting to the identity server that is hosted by our IT.

And the problem of mine is the endpoint they provided is not exactly the URL I should fill into appsettings.json.

The error is resolved after I change from

"OpenIDConnect": {
    "Authority": "https://login-itg.external.companyName.com/as/authorization.oauth2",
    "ClientId": "***",
    "ClientSecret": "***"
  }

To

"OpenIDConnect": {
    "Authority": "https://login-itg.external.companyName.com",
    "ClientId": "***",
    "ClientSecret": "***"
  }
Inobservance answered 25/3, 2023 at 3:18 Comment(0)
A
2

In my case, the client and identity server both starts up at the same time (same solution). So there is race condition, the client finishes first and tries to get the config /.well-known/openid-configuration, which is not yet available because the identity server is still in cold start.

Since the client caches the first configuration request, it still won't load even if you refresh the page. You have to wait 5 minutes or more to refresh again.

Some suggest to delay the config request by setting the config DelayLoadMetadata. See https://mcmap.net/q/585121/-unable-to-obtain-configuration-from-well-known-openid-configuration

You can put a thread delay on the client if you can't control the startup sequence.

Ambi answered 25/4, 2023 at 6:51 Comment(0)
H
1

Just posting this to save someone in the future some time. The problem is that the certificate you are using probably does not have a dns entry for the identityserver or the webapplication address.

You can fix this by creating a certificate with multiple dns addresses.

If you are a visual studio user like me you can easily create a certificate using New-SelfSignedCertificate

Horatio answered 21/4, 2022 at 9:52 Comment(0)
A
1

In my case, I was using docker-compose, and one of the projects in the dependencies was unloaded in Visual Studio. But I wasn't getting any compile time errors. I don't know how it was important!

Avoirdupois answered 28/3, 2023 at 9:2 Comment(0)
V
1

I just wanted to put this answer here for anyone that makes the same silly mistake I made. Make sure you have the library installed:

Microsoft.IdentityProtocols.OpenIdConnect

Verditer answered 8/12, 2023 at 13:49 Comment(0)
C
0

If, as @john-hardcash was previously pointing out, you need to create your own self-signed certificate for multiple domains, you might come across the following problem: Visual Studio is only using the certificate created with dev-certs tool. Even if you try to import your own multi-domain certificate by using the dev-certs tool, you may get the following error because your certificate is not marked as development certificate:

Dev-certs import failure

Then, a possible solution could be to follow this steps:

  1. Find or create a new regular development certificate by using dev-cert tools and write down its thumbprint.
  2. Clone that certificate using PowerShell with the following commands:
Set-Location -Path "cert:\CurrentUser\My"
$OldCert = (Get-ChildItem -Path <oldCertificateThumbprint>)
New-SelfSignedCertificate -CloneCert @OldCert -DnsName "localhost","host.docker.internal" -FriendlyName "ASP.NET Core HTTPS development certificate"
  1. Remove the old development certificate by using dev-certs clean option.

  2. Open certmgr.msc and trust your newly cloned certificated by moving it to your Trusted Root Certification Authorities folder.

  3. Export your new trusted certificate as a PFX file. You'll need to provide a name to the file and a password, so write it down because it'll be used after.

  4. Import your new certificate with this command where you'll need to provide the name and password you used in the step before (the -v option will show you all details and additional information for potential errors):

    dotnet dev-certs https --clean --import <myCertName>.pfx -p <myCertPwd> -v

Now, you may need to restart Visual Studio, but the next time you run your application again it will be using your new multi-domain certificate.

Champlin answered 4/7, 2022 at 2:6 Comment(0)
R
0

In my case I was using the correct jwks url in the appsettings.development.json, but I forgot to adjust in secrets.json.

Retort answered 7/11, 2022 at 13:39 Comment(0)
E
0

So this happens when your app can't reach the /.well-known/openid-configuration of the idenity server. Try it yourself in your browser, it should fail for you.

In my case, identityserver4 was running on http, but my other app was trying to reach it on https. So I changed the Authority to 'http' in my config, and it worked.

Epiphysis answered 17/11, 2022 at 9:37 Comment(1)
I can access it in my browser, yet the app still can't reach itBerlinda
P
0

In my situation I was hosting identity server in Azure App Services

Under the Web App -> Configuration -> General Settings -> Incoming Client certificates was set to "Required"

After changing this to "Ignore" it worked as it should.

Note: In a browser the .well-known/openid-configuration worked because I accepted the certificate. However, within postman I was getting the same 403. If you cancel the certificate request in a browser you will get 403 as well.

Pannonia answered 15/2, 2023 at 14:45 Comment(0)
D
0

My experience is, i used Azure to create a token and the token was not correctly stored in System Environment Variables, to be specific, the old one was not replaced with a new one manually. So basically this error means a value/variable, maybe the URL, secret token, or clientId was misconfigured.

Disloyal answered 7/5, 2023 at 11:0 Comment(0)
L
0

In my case

I added the below code to startup.cs, and it fixed my problem:

System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
Lowgrade answered 3/11, 2023 at 20:28 Comment(0)
S
0

For anyone who might be having a similar issue, my issue was a firewall issue.

I had a custom domain managed by Azure front door with a WAF rule that restricted access by IP.

This meant things worked fine on my machine, but when hosted, the calls were being blocked.

I added the host IP to the list of allowed IPs, and everything worked fine.

Stauffer answered 19/4 at 4:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.