openiddict-core icon indicating copy to clipboard operation
openiddict-core copied to clipboard

How to set base path?

Open SebastianStehle opened this issue 3 years ago • 7 comments

Confirm you've already contributed to this project or that you sponsor it

  • [X] I confirm I'm a sponsor or a contributor

Version

3.x

Question

Hi, I have migrated from Identity Server a while ago, and my setup was kind of awkward to be honest.

What I have done before was to use distinct areas, e.g.

app.Map("/identity-server", builder => {
    // Use OpenidDict stuff
});

But it was working for OpenIddict core, but causes some issues with controllers.

So I am trying to fix this and I just use normal routing. So what I want to do is to make the endpoints available under /identity-server (for backwards compatibility).

So what I changed was this:

builder
   .SetConfigurationEndpointUris("/identity-server/.well-known/openid-configuration")

This is actually enough to make this endpoint look good, e.g. I get this now:

"authorization_endpoint": "https://localhost:5001/identity-server/connect/authorize",

Ufortunately my controllers do not work anymore, I always get an exception here:

// /identity-server/connect/authorize
var request = HttpContext.GetOpenIddictServerRequest();
if (request == null)
{
  throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
}

But when I change my endpoint URL to

.SetAuthorizationEndpointUris("identity-server/connect/authorize")

my inspection file looks like this

"authorization_endpoint": "https://localhost:5001/identity-server/identity-server/connect/authorize",

SebastianStehle avatar Jan 24 '22 12:01 SebastianStehle

Edit: I have found out that the paths in the configuration file are relative to the issuer URL. this makes sense.

But I guess it clashes with this code, because here the full path is checked: https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs#L89

So I am not sure how to handle this.

SebastianStehle avatar Jan 24 '22 12:01 SebastianStehle

Edit 2: I have solved it by setting the URLs manually to absolute Uris:

services.Configure<OpenIddictServerOptions>((services, options) =>
            {
                var urlGenerator = services.GetRequiredService<IUrlGenerator>();

                var issuerUrl = Constants.PrefixIdentityServer;

                options.Issuer = new Uri(urlGenerator.BuildUrl(issuerUrl, false));

                options.AuthorizationEndpointUris.Add(
                     new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/authorize", false)));

                options.IntrospectionEndpointUris.Add(
                     new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/introspect", false)));

                options.LogoutEndpointUris.Add(
                     new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/logout", false)));

                options.TokenEndpointUris.Add(
                     new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/token", false)));

                options.UserinfoEndpointUris.Add(
                     new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/userinfo", false)));
            });
            ```

SebastianStehle avatar Jan 24 '22 12:01 SebastianStehle

Hey @SebastianStehle,

So what I changed was this:

builder
   .SetConfigurationEndpointUris("/identity-server/.well-known/openid-configuration")

This is actually enough to make this endpoint look good, e.g. I get this now:

"authorization_endpoint": "https://localhost:5001/identity-server/connect/authorize",

Ufortunately my controllers do not work anymore, I always get an exception here:

This scenario should work, but this requires changing the route assigned to your authorization endpoint to /identity-server/connect/authorize so that the two match.

As you figured out, things get much more complicated when mixing a custom issuer that includes a path base (e.g domain.com/identity-server) because OpenIddict uses it to compute the URLs returned as part of the configuration document but then, the classical PathString comparison used by the ASP.NET Core host for relative paths doesn't take the initial path present in the issuer into account.

This is unfortunate and the reason is mostly historical: 1.x and 2.x used PathString to represent endpoint addresses. When decoupling the OpenIddict server stack from ASP.NET Core, the reference to PathString had to be removed and was replaced by Uri (that is part of the .NET BCL). This was also a good opportunity to support absolute URLs to allow scenarios where endpoints are handled in different applications (typically, a userinfo endpoint that is not managed by the OpenIddict server but by a different, separate API, possibly on a different domain).

The problem with Uri is that it has many more possibilities that the simpler PathString used by ASP.NET Core doesn't have: since it was only intended to be used as a replacement to PathString, that MUST start with a /, things like ./path, ../path or path don't work correctly. This is a problem and it clearly needs an overhaul to fix that.

That said, I see you fixed that by using absolute URLs, so it's great. If you have any suggestion on how things could be improved, don't hesitate 😃

kevinchalet avatar Jan 24 '22 15:01 kevinchalet

Related tickets:

  • https://github.com/openiddict/openiddict-core/issues/1255
  • https://github.com/openiddict/openiddict-core/issues/1363

kevinchalet avatar Jan 24 '22 15:01 kevinchalet

Sorry, that I have to come back to this issue. My solution with the absolute paths worked, but when you make a request with another host name, the routing does not work anymore. Makes sense.

I tried this:

var issuerUrl = Constants.PrefixIdentityServer; // /identity-server

                options.AuthorizationEndpointUris.Add(
                     new Uri($"{issuerUrl}/connect/authorize", UriKind.Relative));

                options.IntrospectionEndpointUris.Add(
                     new Uri($"{issuerUrl}/connect/introspect", UriKind.Relative));

                options.LogoutEndpointUris.Add(
                     new Uri($"{issuerUrl}/connect/logout", UriKind.Relative));

                options.TokenEndpointUris.Add(
                     new Uri($"{issuerUrl}/connect/token", UriKind.Relative));

                options.UserinfoEndpointUris.Add(
                     new Uri($"{issuerUrl}/connect/userinfo", UriKind.Relative));

                options.CryptographyEndpointUris.Add(
                     new Uri($"{issuerUrl}/.well-known/jwks", UriKind.Relative));

                options.ConfigurationEndpointUris.Add(
                     new Uri($"{issuerUrl}/.well-known/openid-configuration", UriKind.Relative));

                options.Issuer = new Uri(urlGenerator.BuildUrl()); // Just https://domain.com/

I mean the issuer does not really matter, right? It is only used the validate the jwt token.

The openid-configuration endpoint looks almost good, except the jwks endpoint:

{
 "issuer": "https://localhost:5001/",
 "authorization_endpoint": "https://localhost:5001/identity-server/connect/authorize",
 "token_endpoint": "https://localhost:5001/identity-server/connect/token",
 "introspection_endpoint": "https://localhost:5001/identity-server/connect/introspect",
 "end_session_endpoint": "https://localhost:5001/identity-server/connect/logout",
 "userinfo_endpoint": "https://localhost:5001/identity-server/connect/userinfo",
 "jwks_uri": "https://localhost:5001/.well-known/jwks",
}
`` 

SebastianStehle avatar Mar 30 '22 13:03 SebastianStehle

Okay, stupid me. I have forgotten to clear the existing endpoints. The issuer URL should not really matter, right?

SebastianStehle avatar Mar 30 '22 13:03 SebastianStehle

The issuer URL should not really matter, right?

Well, as you said, it's used for token validation. Clients are also expected to validate it to ensure it exactly matches the issuer configured in their options (e.g the new OpenIddict client does that), so yeah, it's better to use the correct value 😄

kevinchalet avatar Mar 30 '22 15:03 kevinchalet