Handling signout flow
This isn't really a bad workaround. In fact, I wouldn't even consider it a workaround. It is now part of the business logic of the application.

The only reason it feels like a workaround is because of your // 1 case and // 2 case comments. Remove those and this is no longer a workaround.

I see the following method:


protected override async Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync()
{
    var ticket = await EnsureCookieTicket();
    if (ticket == null)
        return AuthenticateResult.Fail("No ticket.");

    //...

    return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
}



And can't help but wonder what the //... bit is because I see no reason for ticket to be a strong type. This entire method:


private Task&lt;AuthenticationTicket&gt; EnsureCookieTicket()
{
    var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
    if (String.IsNullOrEmpty(cookie))
        return null;

    var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding());
    if (ticket == null)
        return null;

    var currentUtc = Options.SystemClock.UtcNow;
    var issuedUtc = ticket.Properties.IssuedUtc;
    var expiresUtc = ticket.Properties.ExpiresUtc;

    if (expiresUtc != null &amp;&amp; expiresUtc.Value &lt; currentUtc)
    {
        //...

        Context.Items.Add("IsExpired", true);
        return null;
    }
}



Never returns a non-null value either. So I'm guessing the bits you left out are substantially important to understanding why you chose the form you did.

Provided you don't need AuthenticationTicket in the HandleAuthenticateAsync() method, I would change the return result from Task&lt;AuthenticationTicket&gt; to Task&lt;bool&gt;. Then rename it to IsTicketValid().


if (expiresUtc != null &amp;&amp; expiresUtc.Value &lt; currentUtc)



You don't use .HasValue to check there, but you use it on Request.Path.HasValue, we should be consistent and do if (expiresUtc.HasValue &amp;&amp; expiresUtc.Value &lt; currentUtc).

I don't like any of these method names, and as a result the method actions themselves because it looks like each one of them has too many responsibilities. You omitted certain parts (so this should have been VTC'd probably) but I'd be willing to bet those parts do more than they should. You should try to find a way to break the flow up into smaller methods that are more maintainable.