I'm trying to write a custom UserStore/RoleStore and am struggling. This might as well be my error, but am having a hard time to figure out what in that case.
Background
There are several questions at StackOverflow about getting the user id when using ASP.NET Identity. Most of them resolve around parsing the NameIdentifier claim.
However, most of them seem to miss an import point. When using OAuth the NameIdentifier seem to point at the identity that the OAuth provider returned.
This can be observed by setting a break point in the AccountController just after the authentication finished.
My problem
If you look at the UserStore you can see that GetUserIdAsync will return the Id of the IdentityUser object which is your internal id from the database. All good so far.
However, if you look at the UserManager it got the following code:
/// <summary>
/// Returns the User ID claim value if present otherwise returns null.
/// </summary>
/// <param name="principal">The <see cref="T:System.Security.Claims.ClaimsPrincipal" /> instance.</param>
/// <returns>The User ID claim value, or null if the claim is not present.</returns>
/// <remarks>The User ID claim is identified by <see cref="F:System.Security.Claims.ClaimTypes.NameIdentifier" />.</remarks>
public virtual string GetUserId(ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException("principal");
return principal.FindFirstValue(this.Options.ClaimsIdentity.UserIdClaimType);
}
.. which looks just fine on the first peak. But if you look at the constant value for UserIdClaimType it's the same as ClaimTypes.NameIdentifier . Thus it will fetch the oauth user id.
Code that wont work due to the above problem:
//this returns the oauth id, can't be used to work with the application account
var id = await _userManager.GetUserIdAsync(currentClaimPrincipal);
//this returns null as it internally uses the above line
await user = _userManager.GetUserAsync(currentClaimPrincipal);
The code above will invoke UserStorage.FindByIdAsync with the oath user id as user id. the user store will therefore try to find the application account by using the incorrect key. It will of course return null .
A hack would be to use the oAuth userId as the PK in the local user table, but that can break if two oauth providers return the same id (not unlikely). And it won't work if multiple oauth accounts are associated to the same application user account.
This is confusing. userId should mean one thing and only one thing in all the identity classes like SigninManager, UserManager and UserStore. You should also state more explicitly which claims are required to be able to map oauth accounts to the application account.
|