We have implemented a Web API controller that inherits the DNNAPIController.
What does the API do?
This API allows Single Sign on via HTTP post. Take the saml response and authenticates the user logs them in using UserController.UserLogin(portalId, userInfo, portalName, "", false);. So issues here.
The API is used by a client who has two different portals xyz.com/portal1 and xyz.com/portal2 where they SSO into the portal via different API URL's:
xyz.com/portal1/api/receivesaml?federation1
xyz.com/portal2/api/receivesaml?federation2
The problem:
When the user clicks on the first SSO URL (different from above), it opens in a browser tab and initiates the SSO to the API url above, authenticates the user, creates the session, redirects them to the portal home page xyz.com/portal1.
When the same user clicks on the second SSO URL (different from above), it opens in a browser tab and initiates the SSO to the API url above, authenticates the user, but after that the redirect to the happens to the API URL again instead of to the second portal home page.
This happens no matter what order the links are clicked. First then second. Or second then first. It's always the second SSO that fails.
If I close the browser tab that the first SSO was succesful in, then the second SSO works.
After the second SSO failed, if the user clicks the second link again, the second SSO works.
I was able to trace the error to this:
Message = "Server cannot modify cookies after HTTP headers have been sent."
at System.Web.HttpResponse.BeforeCookieCollectionChange()
at System.Web.HttpCookieCollection.Set(HttpCookie cookie)
at DotNetNuke.Security.PortalSecurity.SignIn(UserInfo user, Boolean createPersistentCookie)
at DotNetNuke.Entities.Users.UserController.UserLogin(Int32 portalId, UserInfo user, String portalName, String ip, Boolean createPersistentCookie)
at Galileo.Utilities.Sso.SsoAuthentication.Authenticate(String userName, Int32 portalId, String portalName) in C:\Projects\Galileo\Release\Solution\Galileo.Application\Utilities\SSO\SAML\Authentication.cs:line 18
at Galileo.Controllers.SamlApiController.<>c__DisplayClass1_0.<ReceiveSaml>b__0() in C:\Projects\Galileo\Release\Solution\Galileo.Controllers\SamlApiController.cs:line 111
When looking at what is doing this. I noticed that the System.Web.HttpContext.Current.Response object had already sent the response headers and hence the API Controller is not able to set the cookie after authenticating the second SSO user.
The currResponse object below was always having currResponse.IsRequestBeingRedirected = True for the second SSO, when the first session is open in another tab or in the same tab.
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Receive([FromBody]string SAMLResponse, string target)
{
System.Web.HttpResponse currResponse = System.Web.HttpContext.Current.Response;
}
DNN is somehow setting the headers and the 302 redirect way too early stopping the API from doing anything. Most likely happening in the DNNAPIController.
What is causing this and how to avoid this?