Search code examples
c#asp.net-core-mvcasp.net-core-identityreturnurl

Logout & ReturnUrl | ASP.Net Core Identity MVC | Authentication


I will try to explain my problem as simply as possible. I can't get back to the original URL, which can be recognized thanks to its ReturnUrl.

Here's what I have when I launch my application :

https://localhost:XXXX/Identity/Account/Login?ReturnUrl=%2F%3FGB%3DMjI2fC0x

So far, so good.

Then once I log in with my account, I get to the index page.

https://localhost:XXXX/?GB=MjI2fC0x

Once again, everything is fine.

But WHEN I log out, I'm coming to this one:

https://localhost:XXXX/Identity/Account/Logout

How can I make the URL to be this ?

https://localhost:XXXX/Identity/Account/Logout?ReturnUrl=%2F%3FGB%3DMjI2fC0x

or this :

https://localhost:XXXX/Identity/Account/Login?ReturnUrl=%2F%3FGB%3DMjI2fC0x

Logout.cshtml.cs

[AllowAnonymous]
public class LogoutModel : PageModel
{
    private readonly SignInManager < ApplicationUser > _signInManager;
    private readonly ILogger < LogoutModel > _logger;

    public LogoutModel(SignInManager < ApplicationUser > signInManager, ILogger < LogoutModel > logger)
    {
        _signInManager = signInManager;
        _logger = logger;
    }
    public string ReturnUrl { get; set; }

    public void OnGet()
    {
    }

    public async Task < IActionResult > OnPost(string returnUrl)
    {
        ReturnUrl = returnUrl;
        await _signInManager.SignOutAsync();
        _logger.LogInformation("User logged out.");
        if (returnUrl!= null) {
            return LocalRedirect(returnUrl);
        }
        else {
            return RedirectToPage();
        }
    }
}

Login.cshtml.cs

[AllowAnonymous]
public class LoginModel : PageModel
{
    private readonly UserManager < ApplicationUser > _userManager;
    private readonly SignInManager < ApplicationUser > _signInManager;
    private readonly ILogger < LoginModel > _logger;

    public LoginModel(SignInManager < ApplicationUser > signInManager,
    ILogger < LoginModel > logger,
    UserManager < ApplicationUser > userManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public IList < AuthenticationScheme > ExternalLogins { get; set; }

    public string ReturnUrl { get; set; }

    [TempData]
    public string ErrorMessage { get; set; }

    public class InputModel {
        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }

    public async Task OnGetAsync(string returnUrl)
    {
        if (!string.IsNullOrEmpty(ErrorMessage)) {
            ModelState.AddModelError(string.Empty, ErrorMessage);
        }

        returnUrl = returnUrl ?? Url.Content("~/");

        // Clear the existing external cookie to ensure a clean login process
        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

        ReturnUrl = returnUrl;
    }

    public async Task < IActionResult > OnPostAsync(string returnUrl)
    {
        returnUrl = returnUrl ?? Url.Content("~/");

        if (ModelState.IsValid) {
            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, set lockoutOnFailure: true
            var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
            if (result.Succeeded) {
                _logger.LogInformation("User logged in.");
                return LocalRedirect(returnUrl);
            }
            if (result.RequiresTwoFactor) {
                return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
            }
            if (result.IsLockedOut) {
                _logger.LogWarning("User account locked out.");
                return RedirectToPage("./Lockout");
            }
            else {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return Page();
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@inject SignInManager < ApplicationUser > SignInManager
@inject UserManager < ApplicationUser > UserManager

< ul class="navbar-nav" >
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Model.ReturnUrl">
            <button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li >
}
else 
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register" asp-route-returnUrl="@Model.ReturnUrl">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login" asp-route-returnUrl="@Model.ReturnUrl">Login</a>
    </li>
}
</ul >

HomeController.cs

[...]
public IActionResult Index(string GB)
{
    if (string.IsNullOrEmpty(GB)) {
        return RedirectPermanent("Error");
    }
    return View();
}
[...]

UPDATE Now I've to go back juste after login........ enter image description here


Solution

  • Ok I got it & it works.

    Updates :

    HomeController.cs

    [...]
    public IActionResult Index(string GB)
    {
        ViewBag.GB = GB;
        if (string.IsNullOrEmpty(GB)) {
            return RedirectPermanent("Error");
        }
        return View();
    }
    [...]
    

    _LoginPartial.cshtml

    @using Microsoft.AspNetCore.Identity
    @inject SignInManager < ApplicationUser > SignInManager
    @inject UserManager < ApplicationUser > UserManager
    
    < ul class="navbar-nav" >
    @if (SignInManager.IsSignedIn(User))
    {
        <li class="nav-item">
            <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
        </li>
        <li class="nav-item">
            @if (Model != null && !string.IsNullOrEmpty(Model.FirstName))
            {
            <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Model.ReturnUrl">
                <button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
            </form>
            }
            else
            {
            <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@ViewBag.GB">
                <button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
            </form>
            }
        </li >
    }
    else 
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register" asp-route-returnUrl="@Model.ReturnUrl">Register</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login" asp-route-returnUrl="@Model.ReturnUrl">Login</a>
        </li>
    }
    </ul>