How can I programmatically log out a member?

There is a logout snippet provided:

	@using (Html.BeginUmbracoForm<UmbLoginStatusController>("HandleLogout", new { RedirectUrl = logoutModel.RedirectUrl }))
	{
		<button type="submit" class="btn btn-primary">Log out</button>
	}

which generates a visible button for a logged-in member to click.

I am wondering if there is a way to programmatically “log out” a member (automatically triggering the “HandleLogout” function) based on various conditions.

There’s a couple of ways to go about this, but the below hopefully should get you going:

public async Task LogOutMemberAsync(){var httpContext = _httpContextAccessor.HttpContext;
    if (httpContext?.User?.Identity?.IsAuthenticated ?? false)
    {
        await _signInManager.SignOutAsync();

        httpContext.Session.Clear();
        httpContext.Response.Cookies.Delete(".AspNetCore.Identity.Application");
    }
}

You may need to adjust the cookie name.

Thanks, @EssCee ,
So it sounds like I’d basically need to re-create the functionality of that “HandleLogout” Action - there isn’t a way to “call” that one programmatically from the View or Controller directly.

A follow-up question…

I have the general logout functionality working as part of the page controller:

public class LogoutPageController : PageControllerBase
{
	private readonly ILogger _Logger;
	private readonly IMemberManager _MemberManager;
	private readonly UserManager<MemberIdentityUser> _MemberIdentityManager;
	private readonly IMemberSignInManager _SignInManager;
	private readonly JwtService _JwtService;

	public LogoutPageController(
		 ILogger<LogoutPageController> logger
		 , ICompositeViewEngine compositeViewEngine
		 , IUmbracoContextAccessor umbracoContextAccessor
		 , IMemberManager MemberManager
		 , UserManager<MemberIdentityUser> MemberIdentityManager
		 , IMemberSignInManager signInManager
		 , JwtService jwtService)
		: base(logger, siteHelperService, themeHelperService, compositeViewEngine, umbracoContextAccessor)
	{
		_Logger = logger;
		_MemberManager = MemberManager;
		_MemberIdentityManager = MemberIdentityManager;
		_SignInManager = signInManager;
		_JwtService = jwtService;
	}

	public override IActionResult Index()
	{
		...

		//Membership
		var isLoggedIn = _MemberManager.IsLoggedIn();

		if (isLoggedIn)
		{
			return HandleLogout(new PostRedirectModel());
		}

		...
	}

	[AllowAnonymous]
	[ValidateAntiForgeryToken]
	public IActionResult HandleLogout([Bind(new string[] { }, Prefix = "logoutModel")] PostRedirectModel model)
	{

		IIdentity? identity = this.HttpContext.User.Identity;
		if ((identity != null ? (identity.IsAuthenticated ? 1 : 0) : 0) != 0)
		{
			try
			{
				this._SignInManager.SignOutAsync().RunSynchronously();
			}
			catch (Exception e)
			{
				_Logger.LogWarning(e, "HandleLogout issue");
				this._SignInManager.SignOutAsync().Wait();
			}

			this.HttpContext.Session.Clear();
			this.HttpContext.Response.Cookies.Delete(".AspNetCore.Identity.Application");
			this.HttpContext.Response.Cookies.Delete(JwtHelper.GetTokenHeaderKey());
		}

		this.TempData[JwtHelper.GetTokenHeaderKey()] = null;
		this.TempData["LogoutSuccess"] = (object)true;
		return model.RedirectUrl.IsNullOrWhiteSpace() ? (IActionResult)this.Index() : (IActionResult)this.Redirect(model.RedirectUrl);
	}

But it runs VERY slowly. I know I am calling an async method synchronously, but when the page loads visibly for the site visitor, I want the login status to be set correctly.

Has anyone else noticed this delay?

UPDATE: Even if I change it to an async call, it is S-L-O-W.

Could you try to change your HandleLogout and make it truly async by

public async Task<IActionResult> HandleLogout

and changing the existing code similar to

await _SignInManager.SignOutAsync();

and see what difference that makes. Might be worth opening a new thread if this doesnt work to keep the issues separate.

So, I tried that:

public override IActionResult Index()
{
	//Membership
	var isLoggedIn = _MemberManager.IsLoggedIn();

	if (isLoggedIn)
	{
		var logoutTask= HandleLogout(new PostRedirectModel());
	}
	...
}

public async Task<IActionResult> HandleLogout([Bind(new string[] { }, Prefix = "logoutModel")] PostRedirectModel model)
{
	IIdentity? identity = this.HttpContext.User.Identity;
	if ((identity != null ? (identity.IsAuthenticated ? 1 : 0) : 0) != 0)
	{
		try
		{
			await this._SignInManager.SignOutAsync(); 
		}
		catch (Exception e)
		{
			_Logger.LogWarning(e, "HandleLogout issue");
		}

		this.HttpContext.Session.Clear();
		this.HttpContext.Response.Cookies.Delete(".AspNetCore.Identity.Application");
		this.HttpContext.Response.Cookies.Delete(JwtHelper.GetTokenHeaderKey());
	}

	this.TempData[JwtHelper.GetTokenHeaderKey()] = null;
	this.TempData["LogoutSuccess"] = (object)true;
	return model.RedirectUrl.IsNullOrWhiteSpace() ? (IActionResult)this.Index() : (IActionResult)this.Redirect(model.RedirectUrl);
}

And stepping through the code, I noticed that since there was no “waiting” until the logout was completed, when the return redirects back to the same page, isLoggedIn is still TRUE, so it runs HandleLogout again.

I figured that the issue was the redirecting, so I changed it to:

	[AllowAnonymous]
	[ValidateAntiForgeryToken]
	public async Task<bool> HandleLogout([Bind(new string[] { }, Prefix = "logoutModel")] PostRedirectModel model)
	{

		IIdentity? identity = this.HttpContext.User.Identity;
		if ((identity != null ? (identity.IsAuthenticated ? 1 : 0) : 0) != 0)
		{
			try
			{
				await this._SignInManager.SignOutAsync(); 
			}
			catch (Exception e)
			{
				_Logger.LogWarning(e, "HandleLogout issue");
				return false;
			}

			this.HttpContext.Session.Clear();
			this.HttpContext.Response.Cookies.Delete(".AspNetCore.Identity.Application");
			this.HttpContext.Response.Cookies.Delete(JwtHelper.GetTokenHeaderKey());
		}

		this.TempData[JwtHelper.GetTokenHeaderKey()] = null;
		this.TempData["LogoutSuccess"] = (object)true;
		return true;
	}

Then the page loads faster, but still shows the member logged-in, which isn’t want I want.

If I change the calling code to this:

public override IActionResult Index()
{
	//Membership
	var isLoggedIn = _MemberManager.IsLoggedIn();

	if (isLoggedIn)
	{
		var logoutTask= HandleLogout(new PostRedirectModel());
		logoutTask.Wait();
	}
	...
}

it once again takes an age to load. I’m just not sure why await this._SignInManager.SignOutAsync(); takes so darn long to complete.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.