Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User.Identity.Name always null and claims count 0 after PasswordSignInAsync MVC .Net Core 3.0

I have an issue that I cannot solve with ASP.NET MVC Core 3.0. After login, the result is succeed and returns successfully to the page I want if signed in, and when I check cookies or session I can see that the API added them successfully. But when I try to get User.Identity.Name it always null, and isAuthenticated always equals false. It is like app.UseAuthentication() don't read cookies or sessions.

my startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContextPool<AnisubsDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("AnisubsDBConnection")));

        services.AddIdentity<IdentityUser, IdentityRole>()
            .AddEntityFrameworkStores<AnisubsDbContext>()
            .AddDefaultTokenProviders();
        services.AddMvc();
        services.AddControllersWithViews();


        services.AddAuthentication(options =>
        {
            options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        })
            .AddFacebook(facebookOptions =>
            {
                facebookOptions.AppId = "353222242210621";
                facebookOptions.AppSecret = "XXXX";
                facebookOptions.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/Login/Callback");
            })
            .AddGoogle(googleOptions =>
            {
                googleOptions.ClientId = "1093176997632-ug4j2h7m9f1nl9rg8nucecpf9np0isro.apps.googleusercontent.com";
                googleOptions.ClientSecret = "XXXX";
                googleOptions.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/Login/Callback");
            })
            .AddTwitter(twitterOptions =>
            {
                twitterOptions.ConsumerKey = "lZ2ugpLuKpDOlmdSuyw1hVJLU";
                twitterOptions.ConsumerSecret = "XXXX";
                twitterOptions.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/Login/Callback");
            })
            .AddMicrosoftAccount(microsoftOptions =>
            {
                microsoftOptions.ClientId = "22f501ab-70c9-4054-8f33-2b35af3a64ba";
                microsoftOptions.ClientSecret = "XXXX";
                microsoftOptions.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/Login/Callback");
            })
            .AddCookie();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.UseStaticFiles();

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseHttpsRedirection();

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

LoginController.cs

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel loginViewModel)
    {
        if (ModelState.IsValid)
        {
            var user = await userManager.FindByEmailAsync(loginViewModel.Email);
            if (user != null)
            {
                var result = await signInManager.PasswordSignInAsync(user.UserName, loginViewModel.Password, loginViewModel.RememberMe, false);
                if (result.Succeeded)
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            ModelState.AddModelError(string.Empty, "Invalid Login Attempt");
        }
        return View(loginViewModel);
    }

Sessions after redirect to home page enter image description here

Razor page Shared _navbarlayout.cshtml (partial view inside _layout.cshtml)

@using Microsoft.AspNetCore.Identity

@inject SignInManager<IdentityUser> signInManager;


<nav class="navbar fixed-top">


    <a class="navbar-logo" href="Dashboard.Default.html">
        <span class="logo d-none d-xs-block"></span>
        <span class="logo-mobile d-block d-xs-none"></span>
    </a>

    <div class="navbar-right">
        <div class="header-icons d-inline-block align-middle">
        <div class="user d-inline-block">
            <button class="btn btn-empty p-0" type="button" data-toggle="dropdown" aria-haspopup="true"
                    aria-expanded="false">
                @if (signInManager.IsSignedIn(User))
                {
                    <span class="name">@User.Identity.Name</span>
                }
                else
                {
                    <span class="name">Not Registered</span>
                }

                <span>
                    <img alt="Profile Picture" src="img/profile-pic-l.jpg" />
                </span>
            </button>

            <div class="dropdown-menu dropdown-menu-right mt-3">
                @if (signInManager.IsSignedIn(User))
                {
                    <a class="dropdown-item" href="#">Account</a>
                    <a class="dropdown-item" href="#">Features</a>
                    <a class="dropdown-item" href="#">History</a>
                    <a class="dropdown-item" href="#">Support</a>
                    <a class="dropdown-item" asp-action="logout" asp-controller="account">Sign out</a>
                }
                else
                {
                    <a class="dropdown-item" asp-action="login" asp-controller="account">Login</a>
                    <a class="dropdown-item" asp-action="register" asp-controller="account">Register</a>
                }
            </div>
        </div>
    </div>
</nav>

From the razor code above, signInManager.IsSignedIn(User) always false, User.identity claims always count zero.

Changed the order of startup middlewares like below and the issue still the same

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

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

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

Below is a GIF picture of how it looks when quickwatch User enter image description here

like image 467
Afterlife Avatar asked Dec 14 '19 12:12

Afterlife


People also ask

What is Aspnet core identity?

ASP.NET Core Identity: Is an API that supports user interface (UI) login functionality. Manages users, passwords, profile data, roles, claims, tokens, email confirmation, and more.


2 Answers

ASP.NET Core Identity creates a cookie (shown in the screenshot as .AspNetCore.Identity.Application), which gets set after a successful call to PasswordSignInAsync. The call to AddIdentity in Startup.ConfigureServices sets this up: it registers an authentication scheme named Identity.Application and sets it as the default authentication scheme for the application.

Now, with that in mind, take the following code from the question:

services.AddAuthentication(options =>
 {
     options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
     options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
     options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
 })

As is clear above, this call to AddAuthentication overrides the default authentication scheme to be CookieAuthenticationDefaults.AuthenticationScheme. This ends up with PasswordSignInAsync correctly signing in the user with the Identity.Application scheme, but the application is using the Cookies scheme when trying to load in the current user. Naturally, this means the user is never loaded.

In terms of a solution, just remove the callback from AddAuthentication:

services.AddAuthentication()
    .AddFacebook(facebookOptions =>
    {
        // ...
    })
    .AddGoogle(googleOptions =>
    {
        // ...
    })
    .AddTwitter(twitterOptions =>
    {
        // ...
    })
    .AddMicrosoftAccount(microsoftOptions =>
    {
        // ...
    });

I've also removed the call to AddCookie, which is redundant. This adds the Cookies authentication scheme, but your application is using Identity.Application, as already described.

like image 138
Kirk Larkin Avatar answered Oct 26 '22 08:10

Kirk Larkin


Try to changing the order of middlewares in the "startup.cs". So you should use app.UseHttpsRedirection() before app.UseAuthentication() like this:

    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
like image 23
Pegah Khabaziyan Avatar answered Oct 26 '22 08:10

Pegah Khabaziyan