Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

userManager.CreateAsync System.ObjectDisposedException was unhandled

We are trying to use Microsoft Account Authentication in an ASP.Net 5 project. We don't require local authentication and don't require user names.

In the ASP.Net 5 Template for a web application, after signing in with an external provider, control returns to ExternalLoginCallback in the AccountController.

If the user is not registered locally ExternalLoginCallback returns the user to a registration screen. I have attempted to modify ExternalLoginCallback to automatically register the new user as below.

        public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
    {
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null)
        {
            return RedirectToAction(nameof(Login));
        }

        // Sign in the user with this external login provider if the user already has a login.
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
        if (result.Succeeded)
        {
            _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
            return RedirectToLocal(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
        }
        if (result.IsLockedOut)
        {
            return View("Lockout");
        }
        else
        {
            // If the user does not have an account, then ask the user to create an account.
            //ViewData["ReturnUrl"] = returnUrl;
            //ViewData["LoginProvider"] = info.LoginProvider;
            //var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);
            //return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });

            // The user has not previously logged in with an external provider. Create a new user.
            CreateUser(info);
            return RedirectToLocal(returnUrl);
        }
    }

CreateUser implements code copied from ExternalLoginConfirmation as it appears in the ASP.Net 5 Template for a web application.

        private async void CreateUser(ExternalLoginInfo info)
    {
        var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);
        var user = new ApplicationUser { UserName = email, Email = email };
        var result = await _userManager.CreateAsync(user);
        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider);
            }
        }
        AddErrors(result);
    }

An error is thrown on the line

var result = await _userManager.CreateAsync(user);

The error is

System.ObjectDisposedException was unhandled Message: An unhandled exception of type 'System.ObjectDisposedException' occurred in mscorlib.dll. Additional information: Cannot access a disposed object.

I have rebooted my machine just in case it was 'just one of those things', but the error recurs.

like image 306
Vague Avatar asked Dec 19 '22 21:12

Vague


1 Answers

Using an async void method is rarely a good idea and is the best way to introduce weird race conditions like the one you're experiencing: since your CreateUser method doesn't return a task, it can't be awaited by ExternalLoginCallback and the request completes before CreateUser has the time to execute the database operations (when the request completes, the DI system call Dispose on scoped dependencies like your EF context).

Update your CreateUser method to return a Task and await it from ExternalLoginCallback and it should work:

await CreateUser(info);
like image 190
Kévin Chalet Avatar answered Dec 29 '22 07:12

Kévin Chalet