DuendeSoftware / Support

Support for Duende Software products
21 stars 0 forks source link

Exception invoking back-channel logout and PostLogout url is always null #1180

Closed apetrut closed 5 months ago

apetrut commented 8 months ago

Which version of Duende IdentityServer are you using? v6.0.2 Which version of .NET are you using? 6 Describe the bug

image

image

A clear and concise description of what the bug is.

I have an Angular app that is redirecting to the login page for login. Once the login is finished it redirects back to the FE app. When the user logs out with success from FE, he is redirected to the Duende logout page and the session is cleared. If the user tries to log back again, after login he is redirected to the Duende Identity Server page instead of the FE app landing page.

For the login flow the redirect url appears in the query string, but for the logout flow it's not. PostLogoutRedirectUri is always null.

Here are a few settings I have added:

options.UserInteraction.LoginReturnUrlParameter = "https://subdomain.domain.com";
options.UserInteraction.LogoutUrl = "https://subdomain.domain.com/Account/Logout";
options.UserInteraction.AllowOriginInReturnUrl = true;
options.DynamicProviders.SignOutScheme = IdentityConstants.ApplicationScheme;

Also, I have added a few logs:

2024-03-26T21:14:42.6388019Z [21:14:42 DBG] Attempting to bind parameter 'returnUrl' of type 'System.String' ...
2024-03-26T21:14:42.6399944Z [21:14:42 DBG] Attempting to bind parameter 'returnUrl' of type 'System.String' using the name '' in request data ...
2024-03-26T21:14:42.6407905Z [21:14:42 DBG] Could not find a value in the request with name '' for binding parameter 'returnUrl' of type 'System.String'.
2024-03-26T21:14:42.6420321Z [21:14:42 DBG] Done attempting to bind parameter 'returnUrl' of type 'System.String'.
2024-03-26T21:14:42.6484580Z [21:14:42 DBG] Done attempting to bind parameter 'returnUrl' of type 'System.String'.
2024-03-26T21:14:42.6493013Z [21:14:42 DBG] Attempting to validate the bound parameter 'returnUrl' of type 'System.String' ...
2024-03-26T21:14:42.6503347Z [21:14:42 DBG] Done attempting to validate the bound parameter 'returnUrl' of type 'System.String'.
2024-03-26T21:14:42.6511657Z [21:14:42 VRB] Action Filter: Before executing OnActionExecutionAsync on filter Microsoft.AspNetCore.Mvc.Filters.ControllerActionFilter.
2024-03-26T21:14:42.6578087Z [21:14:42 VRB] Action Filter: Before executing OnActionExecuting on filter Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter.
2024-03-26T21:14:42.6590206Z [21:14:42 VRB] Action Filter: After executing OnActionExecuting on filter Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter.
2024-03-26T21:14:42.6597912Z [21:14:42 VRB] Action Filter: Before executing OnActionExecutionAsync on filter HTC.IdentityServer.STS.Identity.Helpers.SecurityHeadersAttribute.
2024-03-26T21:14:42.6608224Z [21:14:42 INF] Executing action method HTC.IdentityServer.STS.Identity.Controllers.AccountController<HTC.IdentityServer.Admin.EntityFramework.Shared.Entities.Identity.UserIdentity, string>.Login (HTC.IdentityServer.STS.Identity) - Validation state: Valid
2024-03-26T21:14:42.6617106Z [21:14:42 VRB] Executing action method HTC.IdentityServer.STS.Identity.Controllers.AccountController<HTC.IdentityServer.Admin.EntityFramework.Shared.Entities.Identity.UserIdentity, string>.Login (HTC.IdentityServer.STS.Identity) with arguments ([""])
2024-03-26T21:14:42.6681790Z [21:14:42 VRB] returnUrl is not valid
2024-03-26T21:14:42.6693296Z [21:14:42 VRB] No AuthorizationRequest being returned
2024-03-26T21:14:42.6700311Z [21:14:42 VRB] No AuthorizationRequest being returned
2024-03-26T21:14:42.6707500Z [21:14:42 INF] Executed action method HTC.IdentityServer.STS.Identity.Controllers.AccountController<HTC.IdentityServer.Admin.EntityFramework.Shared.Entities.Identity.UserIdentity, string>.Login (HTC.IdentityServer.STS.Identity), returned result Microsoft.AspNetCore.Mvc.ViewResult in 0.1018ms.
  1. How can I setup the flow in order to logout but still keep the redirect url if I try to login again?

  2. Is there a way to increase the timeout for the BackChannelLogoutHttpClient?

Thanks.

RolandGuijt commented 8 months ago

When a session is ended at the identity provider there is a mechanism in place that notifies other clients (using the same session id) of that fact so that they can end their own session.

There are two flavors of this mechanism called frontchannel logout notifications and backchannel logout notifications. Your first screenshot showing the exception suggests you are using backchannel logout notifications.

However, it looks like you're using a frontend without a backend. Or are you using the BFF pattern? From your post it looks like you're interacting with the identity provider from javascript directly and that here is no server-side part to the front-end. A backchannel request is basically a request being done to the server-side application so that the browser doesn't know about the request. Since there is no server-side in your case, backchannel logout notifications can't be used.

Please disable backchannel logout notifications and try if the problem goes away. Chances are that you don't have to do much else, assuming you are using spec compliant libraries like oidc-client. These work by performing monitoring on a special iFrame called check_session_iframe. Here is some more information about client notifications.

Also, some settings on the IdentityServer side might require some explanation. These could also be part of the problem:

options.UserInteraction.LoginReturnUrlParameter = "https://subdomain.domain.com";

This should be the url query parameter name the returnUrl should be taken from.

options.UserInteraction.LogoutUrl = "https://subdomain.domain.com/Account/Logout";

This normally is a relative URL to the logout page.

apetrut commented 8 months ago

Hi @RolandGuijt , I am not using BFF. It's just a simple angular app that uses this OAuth library to login/logout (https://github.com/manfredsteyer/angular-oauth2-oidc). The back-end is skoruba, but behind the scenes it's using the Duende software.

I removed all the back and front channel settings and now I get these logs:

2024-03-27T18:32:59.065798019Z [18:32:59 INF] Request finished HTTP/1.1 GET http://mydomain-sts.azurewebsites.net/Account/Logout?logoutId=CfDJ8JdVAPpK845GrVQw6N-VAdR1nkdG0NCCO2i7UpKd-epjteeUuz5RcW7vgbGAAwGt2fVqJ4QkTDJn1xoo70HUQku6DtSHQFEEE4zeojXh1Xk58RvQXDlkEQxOkfuFPIXcED-8GORm_hPqFQRrMvjdB8rPwgnUTkGgNJ5l0lsk5Cc0vtdM__WSikcdyn3sXeGUSYtbAsC8BBUsSE2Hp7jedfDWx4Yl8P2jWscP15FrZGmKFoNND_LHq-tHLKQQlu5qlilPWpq2Hm64IfcpqmDhBeaWkImqcLKdiwGr4h9MXkN5BWqsvoXOJggIScJzlP3Hvrx_XGNfIy7Tjyt3Q1e6iS4 - 0 - 200 - text/html;+charset=utf-8 444.1191ms 2024-03-27T18:32:59.167651376Z [18:32:59 INF] Request starting HTTP/1.1 GET http://mydomain-sts.azurewebsites.net/connect/endsession/callback?endSessionId=CfDJ8JdVAPpK845GrVQw6N-VAdR08Wd1sTco0vOr5YFq3fRRY-pr67JSp4s4HYmrywI8EBJarWZ_yi-Xl4DGZJ5tr20TpTo-ceMglYD6cGWjrhYo4i6TfD3eUfcKC9vtMpNGnjslaNvRmQIDxJNcN5eJk1XcWoYgfFd3vL_RgATWVXhsBgJhfrOEZ9SPxunY0HW7DO3lzwXAJhgi9VtmTU3DJBwLDmXdghEfNoHEZ3n70f4MspdFqPtBN_B8XbEMadERhXewgf1j1uDlWTPTa1jkEtfA8fHE0slhQMYBVVS674TIEejVaHIE7XQQa3sRmxv-Rg - 0

and also

2024-03-27T18:32:57.882922829Z {"ClientId": null, "ClientName": null, "SubjectId": "7becac79-c29d-4093-897c-e5671dbad0df", "PostLogOutUri": null, "State": null, "Raw": {"post_logout_redirect_uri": "https://sub.domain.com"}, "$type": "EndSessionRequestValidationLog"} 2024-03-27T18:32:57.882936829Z [18:32:57 DBG] Success validating end session request from null

My issue is now similar to this one that was raised here: https://github.com/DuendeSoftware/Support/issues/282

I also linked the angular app with the https://demo.duendesoftware.com/ . In this case the redirect_uri is correctly passed in the url after logout and the redirect is done after second login. Can you indicate what settings are done for this url: demo.duendesoftware.com that we are missing?

Thanks.

RolandGuijt commented 7 months ago

It seems like the identity token is correctly passed to the logout endpoint and the session is ending after which the session is ended. IdentityServer knows which client ended the session because that's a claim in the token. That's why no redirect_uri is needed. What you're probably seeing on your identity provider is a logout screen with a link to return to the client. The reason it works on our demo server is probably because there's a small javascript file loaded in the logout page that takes care of that. It is however disabled by default and it is controlled by the AutomaticRedirectAfterSignOut property in LogoutOptions in our quickStart samples. When you examine LoggedOut.cshtml you can see the boolean being used at the bottom.

apetrut commented 7 months ago

I enabled the Automatic redirect flag and now I got these errors in the logs:

`2024-03-29T20:15:03.085209846Z [20:15:03 DBG] Attempting to bind parameter 'returnUrl' of type 'System.String'

2024-03-29T20:15:03.086656042Z [20:15:03 DBG] Attempting to bind parameter 'returnUrl' of type 'System.String' using the name '' in request data

2024-03-29T20:15:03.087348141Z [20:15:03 DBG] Could not find a value in the request with name '' for binding parameter 'returnUrl' of type 'System.String'.

2024-03-29T20:15:03.087365341Z [20:15:03 DBG] Done attempting to bind parameter 'returnUrl' of type 'System.String'.
2024-03-29T20:15:03.087936639Z [20:15:03 DBG] Done attempting to bind parameter 'returnUrl' of type 'System.String'.
2024-03-29T20:15:03.087954039Z [20:15:03 DBG] Attempting to validate the bound parameter 'returnUrl' of type 'System.String'
2024-03-29T20:15:03.088681637Z [20:15:03 DBG] Done attempting to validate the bound parameter 'returnUrl' of type 'System.String'.
2024-03-29T20:15:03.088700837Z [20:15:03 VRB] Action Filter: Before executing OnActionExecutionAsync on filter Microsoft.AspNetCore.Mvc.Filters.ControllerActionFilter.

2024-03-29T20:15:03.094344024Z [20:15:03 VRB] Action Filter: Before executing OnActionExecuting on filter Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter.

2024-03-29T20:15:03.101046508Z [20:15:03 VRB] Action Filter: After executing OnActionExecuting on filter Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter.

2024-03-29T20:15:03.101121608Z [20:15:03 VRB] Action Filter: Before executing OnActionExecutionAsync on filter HTC.IdentityServer.STS.Identity.Helpers.SecurityHeadersAttribute.

2024-03-29T20:15:03.101133408Z [20:15:03 INF] Executing action method 

HTC.IdentityServer.STS.Identity.Controllers.AccountController<HTC.IdentityServer.Admin.EntityFramework.Shared.Entities.Identity.UserIdentity, string>.Login (HTC.IdentityServer.STS.Identity) - Validation state: Valid

2024-03-29T20:15:03.101138908Z [20:15:03 VRB] Executing action method 

HTC.IdentityServer.STS.Identity.Controllers.AccountController<HTC.IdentityServer.Admin.EntityFramework.Shared.Entities.Identity.UserIdentity, string>.Login (HTC.IdentityServer.STS.Identity) with arguments ([""])

2024-03-29T20:15:03.101143708Z [20:15:03 VRB] returnUrl is not valid

2024-03-29T20:15:03.101147908Z [20:15:03 VRB] No AuthorizationRequest being returned

Why would the return url not be valid even if I have setup the PostLogoutRedirectURI? What would a valid url look like?

RolandGuijt commented 7 months ago

It looks like the login endpoint on the account controller is accessed right after the redirect to the client occurred. Maybe you're redirecting to a protected page after logout?

apetrut commented 7 months ago

I have setup the redirect uri and also the post logout uri to point to the Login endpoint which has the [AllowAnonymous] attribute so I don't expect it to be protected.

However, I don't understand why the PostLogoutRedirectURI is null? I have checked and I have a logoutId in the query string which in turn should build the LogoutViewModel with the correct value.

AndersAbel commented 7 months ago

To make it easier to follow the flow, could you please use the browser dev tools to record the flow and share a screenshot? I would like to see the redirects step by step. To only show the redirects you can use the "Doc" filter in the dev tools.

RolandGuijt commented 7 months ago

@apetrut Is this resolved for you? If not please provide us with the flow Anders asked for or can we close?

apetrut commented 7 months ago

@AndersAbel I have added a few screenshots starting with the Login page. However I couldn't catch the logout redirects because the AutomaticRedirectAfterSignOut flag was set to TRUE and the pages were refreshed so quickly. I will disable that flag and try again.

The problem still stays as the PostLogout Redirect URL is always empty so I need to manually hardcode it to a specific url.

Login step 1: login_step_1

Login step 2: login_step_2_callback

apetrut commented 7 months ago

@RolandGuijt @AndersAbel The problem is not yet solved for me. You can find below the logout steps that the app is taking.

logout_part_1

logout_part_2

logout_part_3

Note: The flow appears to be working because I hardcoded the PostLogOut redirect uri field. I can't understand why that variable is empty all the time and needs hardcoded.

RolandGuijt commented 7 months ago

It's hard for us to see the entire flow with all these fragments. In the header of the Network tab is a checkbox "Preserve log". With this enabled the log will survive between requests. Can you please enable that and post the entire flow?

Also, you were mentioning that you're using the login page as the post logout Url. But the purpose of it is to have a means to go back to the client application so it should be a URL that points to that. Please change that while you go through the flow again.

RolandGuijt commented 6 months ago

@apetrut Would you like to pursue this further or can we close?

apetrut commented 6 months ago

Hi, @RolandGuijt. I will send the flows today.

apetrut commented 6 months ago

Hi @RolandGuijt the flows can be found below:

image

I have disabled the Automatic redirect flag and also the Post_Logout_Redirect uri appears to be set, but it's really hardcoded because it comes as null from Duende (see the logs below):

image

RolandGuijt commented 6 months ago

This doesn't seem to have anything to do with back-channel logout which is basically a way to notify the other clients (different from the one that initiated the logout) of the fact that the session has ended.

Are you using your OAuth library to initiate the logout? I'm asking because logout might not be as straightforward as you think. As your flow shows there are multiple endpoints that are involved that used different URLs. I can't say how it is implemented exactly in the OAuth library your using. That is outside the scope for this issue tracker. Maybe it's a good idea to post an issue there as well if needed.

Also: it also seems like you're still using the login page as the PostLogoutRedirect URL. It is not designed for that. Instead, it should point to a URL on the client to redirect to when logout is finished. This could be part of the problem too.

RolandGuijt commented 6 months ago

@apetrut Has this been resolved for you? If so I'd like to close.

RolandGuijt commented 5 months ago

Closing for now but feel free to reopen if something comes up.