We have implemented a logout method on the SP (an Asp.Net MVC Core 2.0 app) to send the SLO request to the IdP (Okta):
var ssoState = await samlServiceProvider.GetStatusAsync();
logger.LogInformation(4, string.Format(“User {0} - CanSloAsync = {1}.”, email, await ssoState.CanSloAsync()));
logger.LogInformation(4, string.Format(“User {0} - IsSloCompletionPending = {1}.”, email, ssoState.IsSloCompletionPending()));
logger.LogInformation(4, string.Format(“User {0} - IsSso = {1}.”, email, ssoState.IsSso()));
logger.LogInformation(4, string.Format(“User {0} - IsSsoCompletionPending = {1}.”, email, ssoState.IsSsoCompletionPending()));
var ssoState = await samlServiceProvider.GetStatusAsync();
logger.LogInformation(4, string.Format(“User {0} - CanSloAsync = {1}.”, email, await ssoState.CanSloAsync()));
logger.LogInformation(4, string.Format(“User {0} - IsSloCompletionPending = {1}.”, email, ssoState.IsSloCompletionPending()));
logger.LogInformation(4, string.Format(“User {0} - IsSso = {1}.”, email, ssoState.IsSso()));
logger.LogInformation(4, string.Format(“User {0} - IsSsoCompletionPending = {1}.”, email, ssoState.IsSsoCompletionPending()));
var partnerName = configuration.GetValue(“PartnerName”);
if (await ssoState.CanSloAsync())
{
await samlServiceProvider.InitiateSloAsync(partnerName, null, relayState);
}
else
{
logger.LogInformation(4, string.Format(“CanSloAsync is false… User {0} initiating SLO request anyway.”, email));
try
{
await samlServiceProvider.InitiateSloAsync(partnerName, null, relayState);
}
catch (Exception ex)
{
logger.LogError(ex, “SLO initiation didn’t work for User {0}… sending back to login screen…”, email);
return controller.RedirectToAction(nameof(AccountController.Login), “Account”, new { p = relayStateObject.CustomLoginCode, l = relayStateObject.Language, to = relayStateObject.IsTimedOut ? 1 : 0 });
}
}
return new EmptyResult();
Generally, this works as expected, in that it initiates the SLO request to OKTA, and, in turn our SingleLogoutService is called, and we are appropriately redirected to the login page by our SinglLogoutService.
Sometimes, however, ssoState.CanSloAsync() resolves to false, even though the Okta session is definitely still active.
When CanSloAsync resolves to false, we try to initiate the SLO request anyway, which of course fails with an exception. Given that, you would think this is an indication that the Session is in fact inactive, we then route the user back to the login page.
The login page uses the Okta login widget which will check to see if the user is Active, and if the user is Active, route them back into the app… otherwise expose a login screen.
Under normal circumstances, when the SingleLogoutService is routing the user back to the login screen, the session is deemed inactive and the login screen renders.
However, when this logout method routes the user back to the login screen, the session is proven to be active, and sends the user back into the app.
The question is, How can CanSloAsync resolve to false, when the SSO session is in fact still Active.
This tends to happen more often on our load balanced servers than it does on our single web servers.
The only time it happens on the non-load balanced servers is when the browser is idle for 20 or more minutes, which you wouldnt think would affect the behavior.
The following log trail from one of our load balanced environments shows 3 consecutive attempts to log out then log back in. The first 2 worked as expected, and the 3rd one unexepectedly failed.
User mike logged out manually.
User mike - IsSloCompletionPending = False.
User mike - CanSloAsync = True.
User mike - IsSsoCompletionPending = False.
User mike - IsSso = True.
User mike initiating SLO request.
User logged in.
User logged in.
User mike logged out manually.
User mike - IsSloCompletionPending = False.
User mike - CanSloAsync = True.
User mike - IsSso = True.
User mike initiating SLO request.
User mike - IsSsoCompletionPending = False.
User mike recieving SLO response.
User logged in.
User mike logged out manually.
User mike - IsSso = False.
User mike - IsSloCompletionPending = False.
User mike - CanSloAsync = False.
Initiation of SLO to the partner identity provider http://www.okta.com/(partner name) has failed. There is no SSO session to partner http://www.okta.com/(partner name) to logout.
CanSloAsync is false… User mike initiating SLO request anyway.
User mike - IsSsoCompletionPending = False.
SLO initiation didn’t work for User mike… sending back to login screen… There is no SSO session to partner http://www.okta.com/(partner name) to logout