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

AddAuthentication with AddOpenIdConnect, and webapp behind a proxy produce different callback URIs depending on client side rendering or server side rendering #57916

Open
1 task done
jan-johansson-mr opened this issue Sep 17, 2024 · 5 comments
Labels
area-security Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. Needs: Repro Indicates that the team needs a repro project to continue the investigation on this issue

Comments

@jan-johansson-mr
Copy link

jan-johansson-mr commented Sep 17, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

The environment have a reverse proxy server (yarp) (front-end facing), with an identity server (using OpenIddict) and a web app (Blazor). All nodes runs on docker images. I have two solutions for the web app. One is client side rendering and the other is server side rendering (dotnet 8). The proxy server forward headers to the nodes behind, and each node behind is configured to handle the forwarded headers, i.e.

    var forwardedHeaderOptions = new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
    };

    forwardedHeaderOptions.KnownNetworks.Clear();
    forwardedHeaderOptions.KnownProxies.Clear();

    app.UseForwardedHeaders(forwardedHeaderOptions);

Both web apps are identical with the core logic and configuration, i.e.

    builder.Services
        .AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.SignOutScheme = OpenIdConnectDefaults.AuthenticationScheme;
            options.Authority = "https://my.domain.net/identity";
            options.ResponseType = OpenIdConnectResponseType.Code;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.ClientId = "mvc";
            options.ClientSecret = "...";
            options.UsePkce = true;
            options.Scope.Add("email");
            options.Scope.Add("roles");
            options.MapInboundClaims = false;
            options.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
            options.TokenValidationParameters.RoleClaimType = "roles";
            options.CallbackPath = "/callback/login";
            options.RemoteSignOutPath = "/local_logout";
            options.SignedOutCallbackPath = "/callback/logout";
            options.SignedOutRedirectUri = "https://my.domain.net/cookie_logout";
            options.Events.OnRemoteSignOut = (context) =>
            {
                return Task.CompletedTask;
            };
        });

The login path works great, with the correct callback login URI (my.domain.net/callback/login) regardless of client side rendering or server side rendering. The logout path differs. It has the correct callback logout URI (my.domain.net/callback/logout) with the client side rendering solution, but wrong callback logout URI (my.docker.node.host/callback/logout) with the server side rendering solution (I'm running one solution at a time, on the same docker host, i.e my.docker.node.host). I have no idea why it should differ, since the environments are identical, with the only difference is that one solution is client side rendering and the other is server side rendering. And even more confusing is that both solutions have the correct callback login.

I also verified the difference by setting a break point at the OnRemoteSignOut event, and exploring the context object. The Host attribute with the request to the identity server have my.docker.node.host value, and not the expected my.domain.net value when running the server side rendering solution.

Expected Behavior

That the forwarded headers with host information should apply to the HttpContext Host value, and be reflected in the produced callback URI given to the Identity Server. This behavior is correct in both solutions when login, but differs when logout. Both solutions have the same configuration, and core logic.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

Microsoft.AspNetCore.App 8.0.8, and Microsoft.NETCore.App 8.0.8

Anything else?

No response

@jan-johansson-mr
Copy link
Author

jan-johansson-mr commented Sep 18, 2024

As a follow-up to the issue.

When I switch the order of how I do a sign-out, using CookieAuthenticationDefaults.AuthenticationScheme (instead of OpenIdConnectDefaults.AuthenticationScheme) and using the event OnRemoteSignOut to redirect the sign-out flow to an endpoint that manually sign-out with the identity server, using OpenIdConnectDefaults.AuthenticationScheme, then the callback URI is correct and the flow works, both with sign-in and sign-out, independent of where the rendering takes place (client or server).

I'm still puzzled about why the issue exists in the first place.

Thanks!

@mkArtakMSFT
Copy link
Member

Thanks for contacting us.
This looks like an issue with the reverse proxy setup, rather than the auth configuration.

Is there a call to the .UseAuthentication() after your app.UseForwardedHeaders call?

@mkArtakMSFT mkArtakMSFT added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Sep 18, 2024
@jan-johansson-mr
Copy link
Author

jan-johansson-mr commented Sep 18, 2024

Thanks @mkArtakMSFT,

No, I have app.UseForwardedHeaders() as the first middleware, and then later app.UseAuthorization(), and before app.UseAntiforgery().

This is the middleware setup

  • UseForwardedHeaders
  • UsePathBase
  • UseHttpsRedirection
  • UseAuthorization
  • UseStaticFiles
  • UseAntiforgery
  • MapRazorComponents
  • AddInteractiveServerRenderMode

Maybe this will help!

@dotnet-policy-service dotnet-policy-service bot added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Sep 18, 2024
@MackinnonBuck
Copy link
Member

Thanks, @jan-johansson-mr.

Could you please provide us with a minimal repro project so that we can investigate this further? Please also include a docker compose file so that we can ensure that our environment matches yours. Ideally the project would be hosted in a public GitHub repository.

@MackinnonBuck MackinnonBuck added Needs: Repro Indicates that the team needs a repro project to continue the investigation on this issue and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Sep 19, 2024
Copy link
Contributor

Thank you for filing this issue. In order for us to investigate this issue, please provide a minimal repro project that illustrates the problem without unnecessary code. Please share with us in a public GitHub repo because we cannot open ZIP attachments, and don't include any confidential content.

@dotnet-policy-service dotnet-policy-service bot added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Sep 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-security Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. Needs: Repro Indicates that the team needs a repro project to continue the investigation on this issue
Projects
None yet
Development

No branches or pull requests

3 participants