Asp.Net Core Identity Create User - entity-framework

I have trouble creating a user. The process runs correctly, reads data to user, redirects to Home / Index, but there is no new user in the database.
I have the same code in DbSeeder and the users are created correctly there.
[HttpPost]
public async Task<IActionResult> Create(WorkerVM model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (user == null)
{
user = new User()
{
FirstName = model.FirstName,
LastName = model.LastName,
Email = model.Username,
UserName = model.Username
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Worker");
}
return RedirectToAction("Index", "Home");
}
}
ModelState.AddModelError("", "Registration Failed");
return View();
}

The most possible reason that the user is not created in db is the new password doesn't fit the password criteria.
Check the password criterias and fix the password. Also fixed your code by moving "}" behind the first return operator:
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Worker");
return RedirectToAction("Index", "Home");
}
you can manage your password criteria adding this code to startup
services.Configure<IdentityOptions>(x => {
x.Password.RequireDigit = false;
x.Password.RequiredLength = 2;
x.Password.RequireUppercase = false;
x.Password.RequireLowercase = false;
x.Password.RequireNonAlphanumeric = false;
x.Password.RequiredUniqueChars = 0;
x.Lockout.AllowedForNewUsers = true;
x.Lockout.MaxFailedAccessAttempts = 5;
x.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(30);
});

Related

Flutter add multiple value into map

So I'm trying to create a flutter app. In my app user A can invite user B,C and so on to join the group, but the user invited can reject the ticket. The user ID that reject the ticket will be stored in the firestore to detect which id reject it and there's a reason why.
The database data looks like this:
- Rejected_detail_map
----Rejected_role_admin
-------- USER_ID_A : "REASON_TO_REJECT"
-------- USER_ID_C : "REASON_TO_REJECT"
----Rejected_role_member
-------- User_ID_B :"REASON_TO_REJECT_2"
I wanted to fetch the data from each rejected_detail_map in one of my page, and use that ID to get the information such as nickname, prof_pic, etc.
Here's how I do it:
if (data['reject_detail_map'] != null) {
if (data['reject_detail_map'].isNotEmpty) {
List rejectDetailKeyList = data['reject_detail_map'].keys.toList();
if (rejectDetailKeyList.contains('rejected_by_pic')) {
List picIdRejectList = data['reject_detail_map']['rejected_by_pic'].keys.toList();
Map picRejectData = {};
await Future.forEach(picIdRejectList, (picId) async {
try {
if (Provider.of<OtherUserDataProvider>(context, listen: false)
.getUserData(picId.toString()) ==
null) {
picRejectData[picId] = await UserDatabase.getUserShortData(userId: picId.toString(), context: context);
Provider.of<OtherUserDataProvider>(context, listen: false).setUserData(userInfo: picRejectData[picId], userId: picId.toString());
picRejectData[picId]['role'] = 'pic';
picRejectData[picId]['reject_text'] = data['reject_detail_map']['rejected_by_pic'][picId];
ticketData['user_reject_data'] = Map.of(picRejectData);
} else {
picRejectData[picId] = Map.of(Provider.of<OtherUserDataProvider>(context, listen: false).getUserData(picId.toString())!);
picRejectData[picId]['role'] = 'pic';
picRejectData[picId]['reject_text'] = data['reject_detail_map']['rejected_by_pic'][picId];
ticketData['user_reject_data'] = Map.of(picRejectData);
}
} catch (e) {
DevMode.log("Error: $e");
}
});
}
if (rejectDetailKeyList.contains('rejected_by_client')) {
List clientIdRejectList = data['reject_detail_map']['rejected_by_client'].keys.toList();
Map clientRejectData = {};
await Future.forEach(clientIdRejectList, (clientId) async {
try {
if (Provider.of<OtherUserDataProvider>(context, listen: false)
.getUserData(clientId.toString()) ==
null) {
clientRejectData[clientId] = await UserDatabase.getUserShortData(userId: clientId.toString(), context: context);
Provider.of<OtherUserDataProvider>(context, listen: false).setUserData(userInfo: clientRejectData[clientId], userId: clientId.toString());
clientRejectData[clientId]['role'] = 'client';
clientRejectData[clientId]['reject_text'] = data['reject_detail_map']['rejected_by_client'][clientId];
ticketData['user_reject_data'] = Map.of(clientRejectData);
} else {
clientRejectData[clientId] = Map.of(
Provider.of<OtherUserDataProvider>(context, listen: false)
.getUserData(clientId.toString())!);
clientRejectData[clientId]['role'] = 'client';
clientRejectData[clientId]['reject_text'] = data['reject_detail_map']['rejected_by_client'][clientId];
ticketData['user_reject_data'] = Map.of(clientRejectData);
}
} catch (e) {
DevMode.log("Error: $e");
}
});
}
}
}
The problem is the ticketData['user_reject_data'] is always limited to 1 person, like the data from before next id is overwritten. So if there are 2 person rejecting the invitation:
User A reject as admin
User B reject as member
the data that i expect to be shown when printing the 'user_reject_data' are:
user_reject_data{
"user_A" : {his info}
"user_B" : {his info}
}
but i will always get only the user B data, like the user A is being over written. I guess there's something wrong with the way I add the data to the map. How to fix this ?

PayPal authentication problem on azure "live store"

I am getting the following error when trying to use paypal API
HttpStatusCode: Unauthorized; AUTHENTICATION_FAILURE; Authentication failed due to invalid authentication credentials or a missing Authorization header.
But problem is only when i publish my code to Azure Api. Live store works if I run it on visual studio.
public async Task<bool> InvoicingCreate(Models.ShopTransaction t)
{
sentJson = null;
if (_accessToken == null) await GetAccessTokenAsync();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "v2/invoicing/invoices");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken.access_token);
bool addNotes;
if (t.Product.TotalPrice == 0.0) addNotes = false;
else if (t.PaymentMethod == null) addNotes = true;
else if (t.PaymentMethod == "paypal" || t.PaymentMethod == "credit_card") addNotes = false;
else addNotes = true;
string billingEmail;
if (t.Product.IndividualCouponId.HasValue)
{
billingEmail = _configuration["Shop:CouponInvoiceEmail"];
}
else
{
billingEmail = t.BillingAddress.Email;
}
var inv = new Root
{
detail = new Detail
{
.......details the items. ...
},
.......Fill the items. ...
sentJson = JsonConvert.SerializeObject(inv, Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
request.Content = new StringContent(JsonConvert.SerializeObject(inv), Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.SendAsync(request);
string content = await response.Content.ReadAsStringAsync();
if (response.StatusCode != System.Net.HttpStatusCode.Created)
{
Error error = JsonConvert.DeserializeObject<Error>(content);
throw new Exception(CompactError("Invoicing-create", response.StatusCode, error));
}
CreateResponse invoiceCreated = JsonConvert.DeserializeObject<CreateResponse>(content);
t.InvoiceId = invoiceCreated.href.Split('/').Last();
return true;
}
Auth methode
appsettings.json paypalmodel
Invoicing methode
I found the problem.
It's an domain issue. It has send to many request on paypal server. So classic denial of service.

_Context.SaveChangesAsync() is not executing in command line

I have used _Context.SaveChangesAsync() in a lot of places in my project and it work fine, except here
//hash the password change the bool to false set the salt save
await TryUpdateModelAsync<User>(user);
Hash hash = new Hash();
string salt = hash.CreateSalt();
user.Salt = salt;
user.Password = hash.HashPassword(NewPassword, salt);
user.ChangePassword = false;
await _Context.SaveChangesAsync();
The Model state is true, and it should be working fine but even in the command line (Microsoft.EntityFrameworkCore.Database.Command) it doesn't show at all and the database is not updating, no error or exception is being thrown
any idea ?
this is the whole class
public class ChangePasswordModel : PageModel
{
public string CurrentPassword { set; get; }
public string NewPassword { set; get; }
public User user { set; get; }
private readonly PROJECTDATABASE_MDFContext _Context;
public ChangePasswordModel(PROJECTDATABASE_MDFContext databasecontext)
{
_Context = databasecontext;
}
public void OnGet()
{
}
public async Task<IActionResult> OnPostAsync()
{
int TempId = 0;
try
{
TempId = Convert.ToInt32(User.FindFirst(claim => claim.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value);
}
catch { }
if (TempId > 0)
{
user = new User();
user = await _Context.User.AsNoTracking().Include(m => m.Role).FirstOrDefaultAsync(m => m.UserId == TempId);
if (user != null)
{
CurrentPassword = Request.Form["CurrentPassword"];
NewPassword = Request.Form["NewPassword"];
//if the password is hashed
if (user.ChangePassword == false)
{
Hash hash = new Hash();
CurrentPassword = hash.HashPassword(CurrentPassword, user.Salt);
}
if (user.Password == CurrentPassword)
{
if(NewPassword.Length >= 8)
{
//hash the password change the bool to false set the salt save
await TryUpdateModelAsync<User>(user);
Hash hash = new Hash();
string salt = hash.CreateSalt();
user.Salt = salt;
user.Password = hash.HashPassword(NewPassword, salt);
user.ChangePassword = false;
await _Context.SaveChangesAsync();
if (user.Role.RoleName == "Student")
{
return RedirectToPage("Users/StudentProfile", new { id = user.UserId });
}
else
{
return RedirectToPage("Users/StaffMemberProfile", new { id = user.UserId });
}
}
else
{
ModelState.AddModelError("NewPassword", "Password Must be at least 8 characters!");
return Page();
}
}
else
{
ModelState.AddModelError("CurrentPassword", "Password is not Correct!");
return Page();
}
}
}
return Page();
}
}
before the save the user is populated correctly with the updated values so the problem is not in the hash method
The problem is in the line where you load User entity:
user = await _Context.User.AsNoTracking().Include(m => m.Role).FirstOrDefaultAsync(m => m.UserId == TempId);
You use AsNoTracking() extension call which tells Entity Framework not to track any changes for returned entity. So to fix the problem just remove unnecessary AsNoTracking() call:
user = await _Context.User.Include(m => m.Role).FirstOrDefaultAsync(m => m.UserId == TempId);

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();
}
}