How can i get and use AspnetUsers UserId in migration configuration file? - entity-framework

This is the code of configuration file(seed method) of entity framework migration.
Through this code, i create a user and role for AspnetUsers table and i put the user in role named Admin.
AppUserManager userMgr = new AppUserManager(new UserStore<AppUser>(context));
AppRoleManager roleMgr = new AppRoleManager(new RoleStore<AppRole>(context));
string roleName = "Admin";
string userName = "admin#educationboard.com";
string password = "Sifre";
string email = "admin#educationboard.com";
if (!roleMgr.RoleExists(roleName))
{
roleMgr.Create(new AppRole(roleName));
}
AppUser user = userMgr.FindByName(userName);
if (user == null)
{
userMgr.Create(new AppUser { UserName = userName, Email = email },
password);
user = userMgr.FindByName(userName);
}
if (!userMgr.IsInRole(user.Id, roleName))
{
userMgr.AddToRole(user.Id, roleName);
}
foreach (AppUser dbUser in userMgr.Users)
{
dbUser.Cinsiyet = eCinsiyetler.Erkek;
}
context.SaveChanges();
Then i also create an entity named Articles, each article has an author id. I named AuthorId as UserId in Article entity. How can i get and use UserId I've just created above in following code?
var articles= new List<Article>
{
new Article{Title="Title 1", AddedDate=DateTime.Now, Content="content here.", UserId=.(What code should be here? };
articles.ForEach(p => context.Articles.AddOrUpdate(s => s.Title, p));
context.SaveChanges();

Why can't you query for the user like you did above?
AppUser user = userMgr.FindByName(userName);
if (user == null)
{
userMgr.Create(new AppUser { UserName = userName, Email = email },
password);
user = userMgr.FindByName(userName);
}
and then have user.Id?
If you have a handle in ApplicationDbContext you could easily do something like
ApplicationDbContext db = new ApplicationDbContext();
var user = db.AspNetUser.Single(x => x.UserName == username);
var id = user.Id;

Related

Multiple Roles causing issues in Blazor

I add roles via the following code in the server project
foreach (var userRole in userInfo.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
}
var token = new JwtSecurityToken(
_config["JWTSettings:validIssuer"],
_config["JWTSettings:validAudience"],
claims,
null,
expires: DateTime.Now.AddMinutes(20),
signingCredentials: credentials);
Now, If I only have one role then this works fine and the following code in the OnInitializedAsync method in a razor component
var t = await AuthState;
var role1= t.User.IsInRole("admin");
leads to role1 being true.
However if I have multiple roles then role1 is then false (as it is for all the roles I add to the user) despite clearly being there!
Now, if I do the following in OnInitializedAsync
var t= await AuthState;
var claimsList= t.User.Claims;
foreach(var item in claimsList)
{
var s1 = item.Type;
var s2 = item.Value;
string asasas = string.Empty;
}
I get a single claim that has a type of role and it has the following as its value
["admin","myrole2"]
on the server side I get a number of claims of type role, each with a single role as the value.
What on earth is going on?
You need to transform your claims:
public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
ClaimsIdentity claimsIdentity = (ClaimsIdentity)user.Identity;
if (account is not null) {
MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
}
return user;
}
private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
{
foreach (var keyValuePair in account.AdditionalProperties) {
var key = keyValuePair.Key;
var value = keyValuePair.Value;
if (value is not null &&
value is JsonElement element && element.ValueKind == JsonValueKind.Array) {
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(keyValuePair.Key));
var claims = element.EnumerateArray()
.Select(x => new Claim(keyValuePair.Key, x.ToString()));
claimsIdentity.AddClaims(claims);
}
}
}
}
Program.cs in your client.
services.AddApiAuthorization().AddAccountClaimsPrincipalFactory<CustomUserFactory>();

userManager.GetRolesAsync() returns list of roles but userManager.GetUsersInRoleAsync() returns empty list

Problem
The below code fetches the roles of users but not the users in that particular role. Is there something missing?
Code
public void SeedRolesData(PartnerDbContext context)
{
if (!context.Roles.Any())
{
context.Roles.AddRange(this.Roles);
context.SaveChanges();
}
}
public void SeedUserRoles(PartnerDbContext context)
{
if (!context.UserRoles.Any())
{
context.UserRoles.AddRange(this.UserRolesMapping);
context.SaveChanges();
}
}
public List<IdentityRole> Roles => new List<IdentityRole>
{
new IdentityRole { Id="1", Name = ApplicationDefaultRoles.admin }
};
public List<IdentityUserRole<string>> UserRolesMapping => new List<IdentityUserRole<string>>
{
new IdentityUserRole<string> { UserId = "1", RoleId = "1" }
};
Getting Roles by user and Users By Roles
var roles = userManager.GetRolesAsync(new User() {
Id = "1",
UserTypeId = (int)UserTypeEnum.admin,
FirstName = "Sam",
LastName = "Ram"
}).Result; --> Returns result
var user= userManager.GetUsersInRoleAsync(ApplicationDefaultRoles.admin).Result; --> Returns empty list.
The sample data for the Roles should also include the NormalizedName to fetch the users in role. Below is the updated sample roles data.
public List<IdentityRole> Roles => new List<IdentityRole>
{
new IdentityRole {
Id="1",
Name = ApplicationDefaultRoles.admin,
NormalizedName = ApplicationDefaultRoles.admin.ToUpperInvariant() }
};

Facebook login in asp.net core not working and return 500

I have been using facebook login for one of my asp.net core projects. However, it stopped working for Facebook login suddenly. I am getting HTTP 500 error.
The issue is, even in the debug more, asp.net core is not mentioning any error. It is just same 500 error code. Nothing else.
When I tried to set breakpoint in the first line of public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null) function, I realized that it is not even hitting that and failing even before that.
I am not sure how to debug this further. Am I missing anything here? Or is there any change from FB in the login side?
The return URL being hit is by Facebook is:
https://localhost:44300/signin-facebook?code=AQBxGGw7ZCoa9xtXc3CCsVGRD9TJLL428bZ_eJpUu4CtVu3K4UrfOZuYYdwFBXzGZ6GOGXpOi2Nme_jfbewB84otVZhKZfs4i7Dhi9Y3E_rloU9ouLeIvuOsm29jr7IDCtTj_HM7rKuKjj3zmc4yz5i_fniZ9ZhMfXtSus5KyKa4EFkZTsmKrz2ngMlGQalUAob_52GJNhvSIXDlmiNSrZLJV3m7Zbkf9eXETQkqhu2L1kgXPvWkMzVP8EN00GwRCYB3xT1kQMOimDANRKhziZjoVS5QZFUJTP0Faj47tE1xNfmAzb30iuwcaRORCOTMipUrnRvOO4nGRo8JuUNdPJaO&state=CfDJ8EHIO3qHMHFClr5BAt4EC1Wj7LyAs5Pg1XOqKo4uFiJM2Jr1rNyooxLIu2fbXr6Z3X5_kqbF_7WwFfvF3L3H4xgyooo-3Y9BV8Zh1S5wXlLJDAyCT5_LwkPJ1j8Zrwx4umQJp6NOl76GwRXpi1_BHlWGRxnh_naTL35iqeGovOa8oEDC0jOQ4trRe7YG3fV_ptjWk4yOnvJnsI81O-6wfyhdc3jm-LTP7ZO7-duf_lPZXZ8mL42XyLXDTIyOJ__S2yLYdvwItdDVntsM8Hwq94goXdU-RaH7ZkDA8iAzeCl3Ke0tWAdYBKy9vooJIXmE9Q#_=_
Based on this article, it should have state_token too in the URL. But that seems to be missing here. How can I figure out here what is the actual error?
I am using asp.net core RC2 release.
My callback function is:
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(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: false);
if (result.Succeeded)
{
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);
if (email == null)
{
return View("Error");
}
/* Determine user from external login info */
var name = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Name);
string firstName;
string lastName = "";
if (!string.IsNullOrWhiteSpace(name))
{
firstName = name.Split(' ').Length > 1? name.Split(new[] { ' ' }, 2)[0] : name;
lastName = name.Split(' ').Length > 1 ? name.Split(new[] { ' ' }, 2)[1] : "";
}
else
firstName = email.Split('#')[0];
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
/* No user with same email ID. So, create a new user.*/
var newUser = new ApplicationUser
{
UserName = email,
Email = email,
FirstName = firstName,
LastName = lastName,
PasswordLastModifiedTime = DateTime.UtcNow,
UserSignUpDate = DateTime.UtcNow
};
var userCreationResult = await _userManager.CreateAsync(newUser);
if (userCreationResult.Succeeded)
{
userCreationResult = await _userManager.AddLoginAsync(newUser, info);
if (userCreationResult.Succeeded)
{
// Add user claims TODO:// Test if the claims are added successfully.
await _userManager.AddClaimAsync(newUser, new Claim("FirstName", newUser.FirstName));
await _userManager.AddClaimAsync(newUser, new Claim("LastName", newUser.LastName));
// Set user email to confirmed. This is more of work around
var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
userCreationResult = await _userManager.ConfirmEmailAsync(newUser, code);
if (userCreationResult.Succeeded)
{
//Create Subscription for user
var planService = new PlanServices();
var plan = planService.Find((int)SubscriptionType.Basic);
await _subscriptionService.CreateSubscription(newUser, plan, null);
await _signInManager.SignInAsync(newUser, isPersistent: false);
_logger.LogInformation(6, "User created an account using {Name} provider.",
info.LoginProvider);
await _emailSender.SendWelcomeEmailAsync(newUser.Email, newUser.FirstName);
return RedirectToLocal(returnUrl);
}
}
}
}
else
{
/* A user with email ID exists. Associate the account with that.*/
var loginAddResult = await _userManager.AddLoginAsync(user, info);
if (loginAddResult.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
}
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email, FirstName = firstName, LastName = lastName});
}
}
And ConfigureServices method is:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddScoped<ApplicationDbContext>();
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonLetterOrDigit = false;
o.Password.RequiredLength = 8;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddCaching();
services.AddSession();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ISubscriptionService, SubscriptionService>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.Configure<RecaptchaOptions>(Configuration);
__serviceProvider = services.BuildServiceProvider();
}
The thing is, it worked well for a long time and has stopped working now. Also, it is not even hitting ExternalLoginCallback, so I am not sure where to head for debugging it further.

ConfirmEmailAsync() method is not working

I am having issue in confirming new user email. the Confirm email link works for first 20 minutes , but after 50 minutes the link expires. I have set the token expiration time to 24 hours. Please help me in resolving this issue. I am stuck on it for last 2 days:(.My code is as follows:
I am setting the token lifetime in Create() method in ApplicationUserManager as following:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = _settings.ConfirmationAndResetTokenExpirationTimeSpan
};
}
And then In AccountsController, the Create method for new user is geiven below. The SendEmailAsync method consist of email subject, email body, generated password and the callback uri.
[Authorize(Roles = Roles.Bam.Name.Admin)]
[HttpPost]
[Route(Routes.Accounts.Template.Create, Name = Routes.Accounts.Name.Create)]
public async Task<IHttpActionResult> Create(CreateUserBindingModel createUserBindingModel)
{
IHttpActionResult result;
var memberNameExists = UserManager.Users.Any(x => x.MemberName.ToLower() == createUserBindingModel.MemberName.ToLower());
if (!memberNameExists)
{
var applicationUser = new ApplicationUser
{
UserName = createUserBindingModel.Email,
Email = createUserBindingModel.Email,
FirstName = createUserBindingModel.FirstName,
LastName = createUserBindingModel.LastName,
Company = createUserBindingModel.Company,
Location = createUserBindingModel.Location,
PhoneNumber = createUserBindingModel.PhoneNumber,
MemberName = createUserBindingModel.MemberName,
LastLoginDate = SqlDateTime.MinValue.Value,
CreateDate = DateTime.Now,
CreatedBy = User.Identity.GetUserId(),
UpdateDate = DateTime.Now,
UpdatedBy = User.Identity.GetUserId(),
TwoFactorEnabled = createUserBindingModel.TwoFactorEnabled,
SecurityResetRequired = true,
PasswordExpirationDate = DateTime.Now.AddDays(Convert.ToDouble(ConfigurationManager.AppSettings["PasswordExpirationDays"]))
};
if (!string.IsNullOrEmpty(createUserBindingModel.AvatarBase64))
{
var avatarBytes = Convert.FromBase64String(createUserBindingModel.AvatarBase64);
var resizedAvatarBytes = ImageResizer.ResizeImage(avatarBytes, _avatarWidth, _avatarHeight);
applicationUser.UserAvatar = new ApplicationUserAvatar
{
Avatar = resizedAvatarBytes
};
}
var generatedPassword = PasswordGenerator.GenerateStrongPassword(10, 10);
var identityResult = await UserManager.CreateAsync(applicationUser, generatedPassword);
if (identityResult.Succeeded)
{
await UserManager.AddToRolesAsync(applicationUser.Id, createUserBindingModel.Roles.ToArray());
var token = await UserManager.GenerateEmailConfirmationTokenAsync(applicationUser.Id);
var callbackUri = string.Format("{0}?userId={1}&token={2}", createUserBindingModel.EmailConfirmationCallbackUri, applicationUser.Id, HttpUtility.UrlEncode(token));
await UserManager.SendEmailAsync(applicationUser.Id, Email.Confirmation.Subject, string.Format(Email.Confirmation.Body, string.Format("{0} {1}", applicationUser.FirstName, applicationUser.LastName), callbackUri, generatedPassword, _settings.AccessTokenExpirationTimeSpan.TotalHours));
var userUrl = new Uri(Url.Link(Routes.Accounts.Name.Get, new { id = applicationUser.Id }));
var roles = await UserManager.GetRolesAsync(applicationUser.Id);
var contract = _accountsMapper.ToContract(applicationUser, roles);
result = Created(userUrl, contract);
}
else
{
result = GetErrorResult(identityResult);
}
}
else
{
ModelState.AddModelError(string.Empty, "Member Name already exists!");
result = BadRequest(ModelState);
}
return result;
}
Once the email is generated the UI has following JS angular code which gets executed and the provide the userid and token to service.
Angular JS code:
angular.module('confirmEmailModule').factory('confirmEmailFactory', function ($http) {
var factory = {};
factory.confirmEmail = function(userId, token) {
var encodedToken = encodeURIComponent(token);
var uri = '/identity/api/accounts/confirmemail?userId=' + userId + '&token=' + token;
return $http.post(uri);
}
return factory;
});
and the Service is :
[AllowAnonymous]
[HttpPost]
[Route(Routes.Accounts.Template.ConfirmEmail, Name = Routes.Accounts.Name.ConfirmEmail)]
public async Task<IHttpActionResult> ConfirmEmail([FromUri] string userId, [FromUri] string token)
{
//var decodedToken = HttpUtility.UrlDecode(token);
var identityResult = await UserManager.ConfirmEmailAsync(userId, token);
var result = identityResult.Succeeded ? StatusCode(HttpStatusCode.NoContent) : GetErrorResult(identityResult);
return result;
}
Please advice.
I found the solution to this issue. I am posting it if somebody faced the same issue. In my case the services and web API were on different servers. Different machine keys caused this issue. So I generated the machine key for my Web application and posted the same machine key in web.config file of Identity service. After that it worked. For more information on generating machine key, following link is helpful.
http://gunaatita.com/Blog/How-to-Generate-Machine-Key-using-IIS/1058
This is what worked for me. Hope it helps out;
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return RedirectToAction("employees", "home");
}
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"The User ID {userId} is invalid";
return View("NotFound");
}
var result = await userManager.ConfirmEmailAsync(user, Uri.EscapeDataString(token));
if (result != null)
{
user.EmailConfirmed = true;
await userManager.UpdateAsync(user);
return View();
}
}

asp.net MVC5: How to get all users and user's role from AspnetUser table by linq

I am trying to get all Users and User's Roles by Linq.
The entities is from ASP.NET MVC identity Framework.
My code:
var userList = db.Users
.Include("Roles")
.ToList();
My entities:
The result i have for the Roles table is "RoleId, UserId" only
. I want to get the Roles which added for each users.
How i can do it?
Thank you.
UPDATE ANSWERS:
var userList = new List<ManagerUserViewModel>();
foreach (var user in db.Users.ToList())
{
var userRolesId = user.Roles.Select(m => m.RoleId).ToList();
var model = new ManagerUserViewModel()
{
UserId = user.Id,
UserName = user.UserName,
Email = user.Email,
UserInfo = user.UserInfo,
Roles = db.Roles.Where(r => userRolesId.Contains(r.Id))
.ToList()
};
userList.Add(model);
}
I hope you think this is sufficient:
// Dummy role name
var name = "Admin";
var rm = new RoleManager<Role>(new RoleStore<Role>(new ApplicationDbContext()));
// Get the role you need (Admin role in this case)
var r = rm.FindByName(name);
// All the user ids that have this role
var userIds = r.Users.Select(u => u.UserId);
// Get all user objects
var users = Db.Users.Where(u => userIds.Contains(u.Id)).ToList();