23

using asp net core identity - when user provides password and username to get a jwt token they post credentials to /api/token

should my token controller method be using usermanager to check the password using the CheckPasswordAsync and if this passes return the token or should i use the signinmanager and call PasswordSignInAsync and then return token based on that result?

I have seen examples of both and wondered what is benefit of each, is one way better than the other?

Currently someone in my team has written the following:

[AllowAnonymous]
[HttpPost]
public async Task<ActionResult<User>> Post([FromBody]User model)
{
    try
    {                                              
        var user = await _userManager.FindByNameAsync(model.Username);
        if (user == null)
            return StatusCode(StatusCodes.Status401Unauthorized, "Incorrect username or password");

        var passwordOK = await _userManager.CheckPasswordAsync(user, model.Password);
        if (!passwordOK)
            return StatusCode(StatusCodes.Status401Unauthorized, "Incorrect username or password");

        model.Id = user.Id;
        model.Name = user.DisplayName;
        model.Password = "";               

        int expiresIn;
        long expiresOn;
        model.Token = _authorisationService.GetJWTToken(model.Username, user.Id, out expiresIn, out expiresOn);
        model.ExpiresIn = expiresIn;
        model.ExpiresOn = expiresOn;

        return model;
    }
    catch (Exception)
    {
        // log the exception
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

but i think there are things in that that are not necessary.

Kirk Larkin
  • 60,745
  • 11
  • 150
  • 162
JimmyShoe
  • 1,379
  • 10
  • 35

1 Answers1

40

The two methods you've mentioned serve different purposes:

1. UserManager.CheckPasswordAsync

This method hashes the provided password and compares it against the existing password hash (stored in the database, for example).

2. SignInManager.PasswordSignInAsync

This method does a lot more. Here's a rough breakdown:

  • Checks whether sign-in is allowed. For example, if the user must have a confirmed email before being allowed to sign-in, the method returns SignInResult.Failed.
  • Calls UserManager.CheckPasswordAsync to check that the password is correct (as detailed above).
    • If the password is not correct and lockout is supported, the method tracks the failed sign-in attempt. If the configured amount of failed sign-in attempts is exceeded, the method locks the user out.
  • If two-factor authentication is enabled for the user, the method sets up the relevant cookie and returns SignInResult.TwoFactorRequired.
  • Finally, performs the sign-in process, which ends up creating a ClaimsPrincipal and persisting it via a cookie.

If you are not interested in requiring confirmed emails, lockout, etc, then using UserManager.CheckPasswordAsync as in your question will suffice.

Kirk Larkin
  • 60,745
  • 11
  • 150
  • 162
  • 3
    ok thanks after researching a bit more the signinmanger is tied in to the cookie auth so in my case if i dont want to use cookies then according to the github identity source i should avoid signinmanager. In that case to have features such as lockout etc in my cookie-less api i would need to implement these features myself. – JimmyShoe Dec 19 '18 at 15:55
  • 4
    @JimmyShoe Do you have sample code to share, or a link for an example implementation? I'm also using JWT (instead of cookies) and am surprised how hard it is to find such code. – lonix May 06 '19 at 09:44
  • `UserManager.CheckPasswordAsync` does one other thing: It logs a warning if the password is incorrect. Annoying because it clutters up the log with warnings for a normal event (we expect users to occasionally mistype passwords). – Jovie Nov 15 '20 at 15:54