Closed JayJamieson closed 5 years ago
Is this using app.UseMicrosoftAccount? How have you activated Microsoft Account? Can we see come code for your configuration? The way external logins work is we do the lookup and replace it with the actual identity user.
Is this using app.UseMicrosoftAccount? How have you activated Microsoft Account? Can we see come code for your configuration? The way external logins work is we do the lookup and replace it with the actual identity user.
No this is using services.AddMicrosoftAccount(...)
as the use.MicrosoftAccount() warns of obsolete?
how ive configured in startup
services.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, c => {
c.ClientId = Configuration["MicrosoftOAuth:ClientId"];
c.ClientSecret = Configuration["MicrosoftOAuth:Secret"];
c.SaveTokens = true;
})
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
});
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Identity/Account/Logout";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
})
scaffolded code from vs 2019
builder.ConfigureServices((context, services) => {
services.AddDbContext<PortalContext>(options =>
options.UseNpgsql(
context.Configuration.GetConnectionString("SqlConnection")));
services.AddDefaultIdentity<PortalUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<PortalContext>();
});
this is what gets called to add the user after ms OAuth flow completes
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid) {
var user = new PortalUser {UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
LoginProvider = info.LoginProvider;
ReturnUrl = returnUrl;
return Page();
}
This is the code that fails after login is confirm and user created, i get redirected to the manage user page and fails to find user
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var userName = await _userManager.GetUserNameAsync(user);
var email = await _userManager.GetEmailAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Email = email,
PhoneNumber = phoneNumber
};
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
return Page();
}
Any update on this or need more information?
You need to do a lookup to find the matching user in the identity database, this is typically done via FindByLogin where you pass in the provider (MicrosoftAccount), and the provider key which is usually the name identifiter claim from there, which will give you back the identity user.
This is what gets setup by the call to AddLogin in your PostConfirmationFlow
result = await _userManager.AddLoginAsync(user, info);
Right, so the scaffolded project is incorrectly using GetUserAsync
? I did some digging in the source code and it seems to me that in order for GetUserAsync
to work I would need to save the user to my db using the external user id found in User
?
Everything should work if you scaffold all files and use them as is, if you are modifying particular pages and what not, you will need to stick to the same conventions the scaffolded pages use, or modify things appropriately. Nothing in the user itself links itself to an external login, its the external login table that does that lookup by default.
UserManager.GetUser(HttpContext.User) fails to find user when using OAuth
Describe the bug
I have setup a project based on scaffold-identity and have successfully configured MS OAuth to authenticate with my microsoft account but visiting the configured
Identity/Account/Manage
page fails to load my details.I have determined the issue is to do with
UserManager.GetUserAsync(HttpContext.User)
trying to use the OAuthClaimType.NameIdentifier
which is not the same as the database Id for the user.To Reproduce
Steps to reproduce the behavior:
Identity/Account/Manage
and get greeted with error of user not being foundExpected behavior
I would expect that the
HttpContext.User
be able to locate the currently logged in user using theClaimType.NameIdentifier
Im open to what best practice for this sort of thing is as out of the box the Scaffold Identity UI with OAuth doesnt work.