public async Task <IActionResult> ExternalLoginCallback(string returnUrl)
        {
            // read external identity from the temporary cookie
            var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);

            if (result?.Succeeded != true)
            {
                throw new ExternalAuthenticationException("External authentication error");
            }

            // lookup our user and external provider info
            var userExternalProvider = await FindUserFromExternalProviderAsync(result);

            if (
                userExternalProvider.User == null &&
                m_featureFlagsManager.IsFeatureEnabled(FeatureFlagsEnum.AllowAutoPairWithExternalIdentityProviderThroughAdditionalLogin)
                )
            {
                if (User?.Identity.IsAuthenticated == true)
                {
                    // handle link user with 2FA in operation
                    var userId = int.Parse(User.Identity.GetSubjectId());

                    m_userManager.CreateExternalLogin(
                        userId, userExternalProvider.Provider, userExternalProvider.ProviderUserId
                        );

                    return(RedirectToAction(nameof(ExternalLoginCallback), new { returnUrl }));
                }

                var dynamicModuleContext = m_dynamicModuleProvider.ModuleContexts.FirstOrDefault(
                    x => x.ModuleConfiguration != null && x.ModuleConfiguration.Name.Equals(userExternalProvider.Provider)
                    );

                if (dynamicModuleContext?.LibModuleInfo is IExternalLoginProviderModule externalLoginProviderModule)
                {
                    var externalLoginProviderManager = externalLoginProviderModule.ExternalLoginProviderManager;

                    if (externalLoginProviderManager.CanProvideEmail)
                    {
                        var externalLoginEmail = externalLoginProviderManager.ExtractEmail(userExternalProvider.Claims);

                        if (string.IsNullOrEmpty(externalLoginEmail))
                        {
                            return(RedirectToSimplifiedLoginPage(returnUrl, null, ExternalProviderInfoLabelType.MissingEmailFromExternalProvider));
                        }

                        var localUserRequest = m_userManager.GetUserByConfirmedEmail(externalLoginEmail);

                        var localUser = localUserRequest.Result;

                        if (localUser != null)
                        {
                            //TODO pair user using confirmed email pair?

                            return(RedirectToSimplifiedLoginPage(returnUrl, externalLoginEmail, ExternalProviderInfoLabelType.UserWithEmailFound));
                        }

                        if (
                            externalLoginProviderManager.CanCreateUser &&
                            m_featureFlagsManager.IsFeatureEnabled(FeatureFlagsEnum.AllowRegistrationThroughExternalIdentityProvider)
                            )
                        {
                            userExternalProvider.User = await AutoProvisionUserAsync(
                                userExternalProvider.Provider,
                                userExternalProvider.ProviderUserId,
                                externalLoginProviderManager.MapClaims(userExternalProvider.Claims)
                                );
                        }

                        if (userExternalProvider.User == null)
                        {
                            return(RedirectToSimplifiedLoginPage(returnUrl, null, ExternalProviderInfoLabelType.UserCouldNotBeCreated));
                        }
                    }
                }
            }

            if (userExternalProvider.User == null)
            {
                return(Redirect(m_returnUrlConfiguration.DefaultFailedExternalLoginUrl));
            }

            // this allows us to collect any additional claims or properties
            // for the specific protocols used and store them in the local auth cookie.
            // this is typically used to store data needed for signout from those protocols.
            var additionalLocalClaims = new List <Claim>();
            var localSignInProps      = new AuthenticationProperties();

            ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);
            ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps);
            ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps);

            // issue authentication cookie for user
            // we must issue the cookie manually, and can't use the SignInManager because
            // it doesn't expose an API to issue additional claims from the login workflow
            var principal = await m_signInManager.CreateUserPrincipalAsync(userExternalProvider.User);

            additionalLocalClaims.AddRange(principal.Claims);

            var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? userExternalProvider.User.Id.ToString();

            await m_events.RaiseAsync(
                new UserLoginSuccessEvent(userExternalProvider.Provider,
                                          userExternalProvider.ProviderUserId,
                                          userExternalProvider.User.Id.ToString(), name)
                );

            //TODO use signInManager instead of HttpContext?
            await HttpContext.SignInAsync(
                userExternalProvider.User.Id.ToString(), name, userExternalProvider.Provider,
                localSignInProps,
                additionalLocalClaims.ToArray()
                );

            // delete temporary cookie used during external authentication
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

            // validate return URL and redirect back to authorization endpoint or a local page

            returnUrl = returnUrl ?? result.Properties.Items["returnUrl"];

            if (m_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
            {
                return(Redirect(returnUrl));
            }

            return(Redirect(m_returnUrlConfiguration.DefaultRedirectUrl));
        }