I'm having probles with passing claims from external identity to local one. The application is basically the QuickBooksASPNetCore2 Demo with added openid identity provider. Everything seams to work, except the
[Authorize(Role = "some_role")]
on test Razer page.
This is the code that is doing the login logic, and
_signInManager.GetExternalLoginInfoAsync();
return principal with all the claims from identity provider (including roles under the "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" type).
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login");
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToPage("./Login");
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: true, bypassTwoFactor : true);
if (result.Succeeded)
{
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
if (result.IsLockedOut)
{
return RedirectToPage("./Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ReturnUrl = returnUrl;
LoginProvider = info.LoginProvider;
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
Input = new InputModel
{
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
}
return Page();
}
}
But when this method exit, the User.Claims on Razor page doesn't have the role claims. It even gets the new "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" claim (with different ID). Did I miss something here?
So if anyone will have this issue, you need to map claims from external identity to local application identity.
That means to replace the line
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: true, bypassTwoFactor : true);
with
var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
var claimsPrincipal = await this._signInManager.CreateUserPrincipalAsync(user);
((ClaimsIdentity)claimsPrincipal.Identity).AddClaims(info.Principal.Claims.Where(c => c.Type == ClaimTypes.Role));
await HttpContext.SignInAsync("Identity.Application", claimsPrincipal);
All the claims that you need in applications, you need to map manually with AddClaim(s) method from external identity.