Sustainsys / Saml2

Saml2 Authentication services for ASP.NET
Other
960 stars 602 forks source link

Error: "Server cannot append header after HTTP headers have been sent." #550

Closed AndersAbel closed 8 years ago

AndersAbel commented 8 years ago

Copied from https://github.com/KentorIT/authservices/issues/455#issuecomment-240716125

I am on v19 and still see this issue. Just came back to this project and switched on SLO(e.g. I had DisableOutboundLogoutRequests = true from a few months ago) and started getting this error.

Update- looks like stack trace is different, coming from IDServer. Maybe a different issue.

OK, I think this is other sequencing issue with the removal of the IDSrv Signin cookie for whatever reason. Stack trace is below, I will keep looking but this only happens when I have the SLO enabled. Maybe you have an idea. thanks!

System.InvalidOperationException ---> System.Web.HttpException: Server cannot append header after HTTP headers have been sent.
   at System.Web.HttpHeaderCollection.SetHeader(String name, String value, Boolean replace)
   at Microsoft.Owin.Host.SystemWeb.CallHeaders.AspNetResponseHeaders.Set(String key, String[] values)
   at Microsoft.Owin.ResponseCookieCollection.Append(String key, String value, CookieOptions options) in c:\local\identity\server3\IdentityServer3\source\Core\Validation\ScopeValidator.cs:line 0
   at IdentityServer3.Core.Configuration.Hosting.MessageCookie`1.ClearByCookieName(String name) in c:\local\identity\server3\IdentityServer3\source\Core\Configuration\Hosting\MessageCookie.cs:line 196
   at IdentityServer3.Core.Extensions.InternalOwinExtensions.ProcessRemovalOfSignOutMessageCookie(IOwinContext context, MessageCookie`1 signOutMessageCookie) in c:\local\identity\server3\IdentityServer3\source\Core\Extensions\InternalOwinExtensions.cs:line 436
   at Microsoft.Owin.Host.SystemWeb.CallHeaders.SendingHeadersEvent.Fire()
   at Microsoft.Owin.Host.SystemWeb.OwinCallContext.StartOnce()
   --- End of inner exception stack trace ---
   at Microsoft.Owin.Host.SystemWeb.OwinCallContext.OnStart()
   at Microsoft.Owin.Host.SystemWeb.CallStreams.OutputStream.Start(Boolean force)
   at Microsoft.Owin.Host.SystemWeb.CallStreams.OutputStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
   at System.Net.Http.StreamToStreamCopy.TryStartWriteSync(Int32 bytesRead)
   at System.Net.Http.StreamToStreamCopy.StartRead()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Owin.HttpMessageHandlerAdapter.<SendResponseContentAsync>d__20.MoveNext() in c:\local\identity\server3\IdentityServer3\source\Core\Validation\ScopeValidator.cs:line 0
AndersAbel commented 8 years ago

Are you using the HTTP POST binding for outgoing Logout messages?

dmeierotto commented 8 years ago

Yes, I tried redirect as well and get the same error.

AndersAbel commented 8 years ago

@dmeierotto The binding config can be a bit tricky - did you verify with the browser dev tools that it is indeed using redirect? The reason I'm asking is because the POST binding writes to the body, which causes headers to be flushed while the redirect binding only affects the headers.

dmeierotto commented 8 years ago

OK, I see what you mean. I am getting redirected to logout on the id server, which is rendering a form that expected to be submitted.

Here is my config:

authServicesOptions.IdentityProviders.Add(new IdentityProvider( new EntityId(idpUrl), authServicesOptions.SPOptions) { LoadMetadata = true, MetadataLocation = idpMetadataUrl, AllowUnsolicitedAuthnResponse = true, Binding = Kentor.AuthServices.WebSso.Saml2BindingType.HttpRedirect, DisableOutboundLogoutRequests = false, SingleLogoutServiceBinding = Kentor.AuthServices.WebSso.Saml2BindingType.HttpRedirect });

Any problems there?

dmeierotto commented 8 years ago

@AndersAbel, yesterday we had a bit more time to work on this and solved the problem by getting the redirect binding to work. The code above was not taking effect as we were loading metadata from the IdP and this was overriding our bindings. Even if we set the bindings after initialization(maybe they are refreshed? Perhaps I missed this). Regardless we downloaded the metadata and removed the support for postbinding and left only redirect and everything started working correctly.

AndersAbel commented 8 years ago

You're guess is right. The metadata is automatically refreshed and will override anything you set manually.

Downloading metadata and editing it is a good way to solve it. Even better would be to add a notification when metadata is refreshed, allowing you to remove the POST binding before the idp configuration is updated. Add a new issue if that's something you'd like to see added.

eddietisma commented 7 years ago

So what's the idea of explicitly setting REDIRECT binding if it's overwritten anyways once the idp configuration metadata refreshes?

AndersAbel commented 7 years ago

@eddietisma The possibility to set configurations manually is meant to be used when metadata is not used.

eddietisma commented 7 years ago

@AndersAbel Thanks for a quick reply. Any idea of a workaround now that I want configuration from the metadata, but avoid the "Server cannot append header after HTTP headers have been sent." error. Which occurs after the 1-hour refresh?

I can see that there is "Prefer redirect binding for logout (#582)" fix, yet I get this issue where a POST is made even though the metadata contains POST, Redirect and SOAP.

AndersAbel commented 7 years ago

@eddietisma I think that a better mechanism for overriding the metadata through config is needed. Please open a new ticket to discuss that.