Inconsistency with ISamlProvider.GetStatusAsync for .Net Core

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())
{

logger.LogInformation(4, string.Format(“User {0} initiating SLO request.”, email));
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

The status is not affected by the authentication session being active or not at the IdP.
There’s no communication between the SP and IdP to determine whether the IdP authentication is still active.
This isn’t supported by the SAML specification.
CanSloAsync is true if there’s been a previously successful SSO and the IdP is configured to support SLO.
We maintain SAML SSO state through a cookie. That’s how we know if there’s been a previously successful SSO.
If the cookie has expired then we won’t have any SSO state and therefore we won’t be able to support SLO.
This is independent of whether or not the authentication session at the IdP is still active.
If CanSloAsync returns false you should simply logout the user locally.
If you’re concerned about the user being logged in still at the IdP, you should prompt the user to close the browser.