简体   繁体   中英

ASP.NET Core 2.2 Authentication not working

I'm trying to create my own login authentication, it wasn't working so I create this quick test...

[HttpGet]
public IActionResult Index()
{
    try
    {
        var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
        identity.AddClaim(new Claim(ClaimTypes.Name, "Test Name"));
        var principal = new ClaimsPrincipal(identity);

        SignIn(principal, CookieAuthenticationDefaults.AuthenticationScheme);

        var isAuthenticated = User.Identity.IsAuthenticated;    //-- THIS IS ALWAY FALSE... BUI JUST LOGGED IN?!?!?!?

        return View();
    }
    catch(Exception ex)
    {
        _logger.LogError(ex, "Error:");
        return StatusCode(500);
    }
}

So you can see I call SignIn then on the next line I check if the user is now authenticated but its always returns false, also the User.Identity looks empty. Does anyone know what i'm doing wrong here?

In my Startup / ConfigureServices i have:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(
    CookieAuthenticationDefaults.AuthenticationScheme,
    options =>
    {
        options.LoginPath = "/Admin/Index";
    });

And in Startup / Configure

app.UseAuthentication();
app.UseCookiePolicy(new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Strict,
});
 SignIn(principal, CookieAuthenticationDefaults.AuthenticationScheme); 

This is a no-op. SignIn is a convenience method for creating an instance of a SignInResult and would usually be returned from an action. This follows the command pattern, but your example just creates the command and never executes it.

What you really want is the following:

await HttpContext.SignInAsync(principal);

You can provide the the scheme if you want to, but you've set CookieAuthenticationDefaults.AuthenticationScheme as the default in ConfigureServices so this will be assumed.

However, even with this change, User.Identity.IsAuthenticated will still be false until another request is made. Typically, you'd do something like this:

await HttpContext.SignInAsync(principal);

return RedirectToAction(...);

With your setup, calling SignInAsync ends up creating a cookie that stores the authentication information. It doesn't update the current User . On the next request, caused by the redirect, the cookie is read by your ASP.NET Core app and the User is populated correctly.

I see your Index method missing Authorize attribute. Please add Authorize attribute as follows:

[Authorize]
[HttpGet]
public IActionResult Index()
{
    ....
}

Then in the Configure method of the Startup class the middlewares order should be as follows:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
        ......

        app.UseStaticFiles();
        app.UseCookiePolicy(new CookiePolicyOptions
        {
            MinimumSameSitePolicy = SameSiteMode.Strict,
        });

        app.UseAuthentication();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
}

Now it should work for you.

Here is sourcecode fo SignIn

public virtual SignInResult SignIn(
  ClaimsPrincipal principal,
  string authenticationScheme)
=> new SignInResult(authenticationScheme, principal);

Above code simply returns an object SignInResult , which does nothing action at that moment.

Here is sourcecode of SignInResult : https://github.com/aspnet/Mvc/blob/master/src/Microsoft.AspNetCore.Mvc.Core/SignInResult.cs

Here is core code:

public class SignInResult : ActionResult
{

    /// <inheritdoc />
    public override async Task ExecuteResultAsync(ActionContext context)
    {
        ...
        await context.HttpContext.SignInAsync(AuthenticationScheme, Principal, Properties);
    }
}

It is a return type ActionResult and it's simply calling context.HttpContext.SignInAsync , so ControllerBase.SignIn is the sort of shortcut for HttpContext.SignInAsync() .

BUT! Take a look the following ActionResult :

public abstract class ActionResult : IActionResult
{
  public virtual Task ExecuteResultAsync(ActionContext context)
  {
   this.ExecuteResult(context);
   return Task.CompletedTask;
  }

  public virtual void ExecuteResult(ActionContext context)
  {
  }
}

So SignInResult does nothing on the Sync method, but only Async .

What I can suggest is:

  1. In your test method, you can only return SignIn(...) , then the action will be proformed automatically, then you can check if its authorized in next request.
  2. if you want to SignIn and do something else in one action, directly use HttpContext.SignInAsync() , which is for your current code.
  3. [this options is just for fun]Regarding the SignInResult implemented the async function only, but does nothing in sync method, so you can try to change your test action to async Task<IActionResult>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM