Search code examples
asp.net-identityasp.net-core-3.1asp.net-core-identity

ASP.NET Core Identity not working on localhost


I'm following a tutorial on Pluralsight that uses ASP.NET Core Identity. When I'm trying to login on a debug session through Visual Studio, the login form works, however when it redirects me after a successfull login, I am not logged in anymore.

I can see through debug that the SignInManager.PasswordSignInAsync() returns Succeeded. However it redirects to the frontpage and appears to not have been logged in.

A bit of searching the web has me thinking that it has something to do with cookies not being set correctly, but I have been unable to figure out how/why.

Identity also logs that the user is logged in:

BethanysPieShop.Areas.Identity.Pages.Account.LoginModel: Information: User logged in.

The Identity pages used for login has been created through scaffolding.

Auto-generated Login.cshtml OnPostAsync:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
   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();
   }
}

Setup of Identity in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
   services.AddDbContext<AppDbContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

   services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<AppDbContext>();
   ...
   services.AddHttpContextAccessor();
   services.AddSession();
   
   services.AddControllersWithViews();
   services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }

   app.UseHttpsRedirection();
   app.UseStaticFiles();
   app.UseSession();

   app.UseRouting();
   app.UseAuthorization();

   app.UseEndpoints(endpoints =>
   {
      endpoints.MapControllerRoute(
         name: "default",
         pattern: "{controller=Home}/{action=Index}/{id?}");
         endpoints.MapRazorPages();
      });
   }
}

I have been able to replicate this problem across 2 projects with different .NET Identity versions used. The versions in this project is:

Microsoft.AspNetCore.Identity.EntityFrameworkCore: 3.1.25
Microsoft.AspNetCore.Identity.UI: 3.1.25

The Identity pages I have scaffolded are

Login.cshtml
Logout.cshtml
Register.cshtml
Login.cshtml:
@page
@model LoginModel

@{
    ViewData["Title"] = "Log in";
}

<h2>@ViewData["Title"]</h2>
<div class="row">
    <div class="col-md-4">
        <section>
            <form id="account" method="post">
                <h4>Use a local account to log in.</h4>
                <hr />
                <div asp-validation-summary="All" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="Input.Email"></label>
                    <input asp-for="Input.Email" class="form-control" />
                    <span asp-validation-for="Input.Email" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Input.Password"></label>
                    <input asp-for="Input.Password" class="form-control" />
                    <span asp-validation-for="Input.Password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <div class="checkbox">
                        <label asp-for="Input.RememberMe">
                            <input asp-for="Input.RememberMe" />
                            @Html.DisplayNameFor(m => m.Input.RememberMe)
                        </label>
                    </div>
                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-default">Log in</button>
                </div>
                <div class="form-group">
                    <p>
                        <a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a>
                    </p>
                    <p>
                        <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
                    </p>
                </div>
            </form>
        </section>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>Use another service to log in.</h4>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                            for details on setting up this ASP.NET application to support logging in via external services.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                                {
                                    <button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

_Layout.cshtml that returnUrl redirects, uses the _LoginPartial:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro">
    <link href="~/lib/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <script src="~/lib/jquery/jquery.js"></script>
    <script src="~/lib/bootstrap/js/bootstrap.js"></script>
    <link href="~/Content/site.css" rel="stylesheet" />
</head>
<body>
    <div class="container">
        <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                </div>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li><a asp-controller="Home" asp-action="Index">Home</a></li>
                        @await Component.InvokeAsync("CategoryMenu")
                        <li><a asp-controller="Contact" asp-action="Index">Contact</a></li>
                        @await Component.InvokeAsync("ShoppingCartSummary")
                        <partial name="_LoginPartial" />
                    </ul>
                </div>
            </div>
        </nav>
        <div class="row">
            <div class="col-md-3">
                <p class="lead">
                    <img class="img-responsive" alt="Bethany's Pie Shop'" src="~/images/bethanylogo.png" />
                </p>
            </div>
            <div class="col-md-9">
                @RenderBody()
            </div>
        </div>
    </div>
    @RenderSection("Scripts", required: false)
</body>
</html>

_LoginPartial.cshtml which has the @if(SignInManager.IsSignedIn(User)), which returns false after successfull login:

@using Microsoft.AspNetCore.Identity

@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
            </li>
            <li>
                <button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
            </li>
        </ul>
    </form>
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li><a asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}

Solution

  • I figured it out myself - I had forgot to add app.UseAuthentication() in Configure under Startup..