0

The majority of functions in ASP.Net Identity are asynchronous. There are helper extension methods which allow you to run these synchronously (which the ASP.Net Identity reference project uses), however:

  • If I use the synchronous extension methods, HttpContext.Current is null.
  • If I call the async methods directly, HttpContext.Current is non-null.

The problem as described is pretty much identical to this question, but seeks to understand why it behaves this way, not just to solve the problem.

The reason I'm running into this issue is that I want to extend the login process to check the request IP address against a user's whitelist IP addresses - but it doesn't work if I can't access the HttpContext.

In trying to work out why this issue occurs, I've learned that ASP.Net does it's own thing with synchronisation contexts to make sure that HttpContext.Current is available for async calls, which is why it works when I call the async method directly.

Why would you use the extension methods that allow async calls to run synchronously, if it breaks access to HttpContext.Current. Is this a bug?

public class LoginPage : Page
{
    public void Login(string user, string password)
    {
        var signInManager = HttpContext.Current.GetOwinContext().Get<MySignInManager>();
        signInManager.PasswordSignIn(user, password, false, false); // this is a synchronous extension method
    }
}

public class MySignInManager<TUser, TKey> : Microsoft.Aspnet.Identity.Owin.SignInManager<TUser, TKey>
{
    public override async Task<SignInStatus> PasswordSignInAsync(string username, string password, bool isPersistent, bool shouldLockout)
    {
        ...
        string ipAddress = GetIpAddress();
        ...
    }

    private string GetIpAddress()
    {
        // HttpContext.Current is null if using the PasswordSignIn extension method
        var request = HttpContext.Current.GetOwinContext().Request;
        return request.RemoteIpAddress;
    }
}

// namespace Microsoft.AspNet.Identity.Owin
public static class SignInManagerExtensions
{
    public static SignInStatus PasswordSignIn(this SignInManager<TUser, TKey> manager, string username, string password, bool isPersistent, bool shouldLockout)
    {
        return AsyncHelper.RunSync(() => manager.PasswordSignInAsync(userName, password, isPersistent, shouldLockout));
    }
}

// namespace Microsoft.AspNet.Identity
internal static class AsyncHelper
{
    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }
}
Zac Faragher
  • 963
  • 13
  • 26
  • Does it really matter "why" in this particular case if one should not ever "use the extension methods that allow async calls to run synchronously" anyway? – Alexei Levenkov Jan 10 '20 at 00:48
  • @AlexeiLevenkov it matters because the reference implementation uses the extension methods, which implies that it's the right way to do it even though it causes this problem – Zac Faragher Jan 10 '20 at 00:50
  • If the whole thing is based on asynchronous operations, I would not trust `HttpContext`. There used to be some articles on how to synchronise the `HttpContext` or `Identity` between the workers and the main server process. But it is risky to me. What I did instead is when a request comes in, I put it into my own caching framework (a static singleton class in a nutshell). And from within the workers, I can access it from the cache. – Antediluvian Jan 10 '20 at 00:58
  • @Antediluvian this is all part of the authentication process when logging in, not some background worker process. the majority of the authentication methods are async methods, from the DB access to the actual login. – Zac Faragher Jan 10 '20 at 00:59
  • The actual cause is more than obvious, https://stackoverflow.com/questions/19509672/why-is-httpcontext-current-null – Lex Li Jan 11 '20 at 00:38

0 Answers0