Fixing External IdentityServer Endpoint URLs in the Discovery Document
Understanding Duende IdentityServer
Duende IdentityServer is a powerful OpenID Connect and OAuth 2.0 framework for ASP.NET Core that provides authentication, single sign-on, and API access control. It’s widely used for securing microservices architectures and enabling secure authentication across various applications.
The Problem
If you’ve worked with Duende IdentityServer, you might have encountered this frustrating issue: you configure your IssuerUri to use your production domain, but when you check the discovery document (.well-known/openid-configuration), only the “issuer” field uses your domain while all other endpoint URLs still point to localhost.
Here’s what the discovery document might look like:
{
"issuer": "https://your-configured-domain.com",
"jwks_uri": "https://localhost:7020/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://localhost:7020/connect/authorize",
"token_endpoint": "https://localhost:7020/connect/token",
// other endpoints also using localhost
}
This happens because IdentityServer uses the incoming HTTP request’s host information to build most of the URLs, not your configured issuer.
The Simple Solution
The simplest way to fix this issue is to add a small piece of middleware to your ASP.NET Core pipeline that modifies the incoming request’s host before IdentityServer processes it:
// In Program.cs where you configure middleware
app.Use(async (context, next) =>
{
var authority = builder.Configuration["ApiAuthentication:Authority"];
if (!string.IsNullOrEmpty(authority))
{
// Override the host name that IdentityServer uses when generating URLs
context.Request.Host = new HostString(new Uri(authority).Host);
context.Request.Scheme = "https";
}
await next();
});
// Important: Add this BEFORE app.UseIdentityServer()
app.UseIdentityServer();
Why This Works
When IdentityServer generates the discovery document, it looks at the current HTTP request to determine what base URL to use for all endpoints.
By adding this middleware before UseIdentityServer(), we’re essentially “tricking” IdentityServer into thinking the request came from our production domain rather than localhost. This causes it to generate all endpoint URLs using our configured domain.
The middleware does two simple things:
- Changes the
Hostheader of the request to match our configured authority - Sets the scheme to “https” to ensure secure connections
The Result
After implementing this middleware, your discovery document will show all endpoints using your configured domain:
{
"issuer": "https://your-configured-domain.com",
"jwks_uri": "https://your-configured-domain.com/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://your-configured-domain.com/connect/authorize",
"token_endpoint": "https://your-configured-domain.com/connect/token",
// all endpoints now use your domain
}
Issue Reference:
https://github.com/DuendeArchive/IdentityServer4/issues/4535
Conclusion
This approach is much simpler than trying to configure various options or properties within IdentityServer. It works with Duende IdentityServer 6.x and doesn’t require any additional packages.
Remember: The key is to add this middleware before app.UseIdentityServer() in your pipeline to ensure it runs first.