I have an mvc4 forms application that uses the simple membership OOTB account controller. I have a view model for the application where I was successfully able to retrieve the username after completing registration as follows:
this.UserName = HttpContext.Current.User.Identity.Name;
At the point this was working my registration method was as follows:
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, propertyValues: new
{
//Form defined values
Forename = model.Forename,
Surname = model.Surname,
Email = model.Email,
Password = model.Password,
Answer = model.SecretAnswer,
DOB = model.DOB,
//Auto defined values
JoinDate = DateTime.Today,
LastLogin = DateTime.Now,
CompanyID = 5,
ParticipationPoints = 0,
Privacy = 0,
IsDeleted = 0,
ImageURL = "/Images/user-holder.jpg"
});
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
After consultation with my customer it was decided that to prevent anyone from just registering on the internet they should already be contained with the usertable with the username value found as a pre existing user. So after this the registration was changed to:
Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
avm.Username = model.UserName;
avm.Forename = model.Forename;
avm.Surname = model.Surname;
avm.Email = model.Email;
avm.Password = model.Password;
avm.Answer = model.SecretAnswer;
avm.DOB = model.DOB;
avm.RegisterUser();
if (avm.StatusCode == "Success")
{
return RedirectToAction("Index", "Home");
}
else
{
//ModelState.AddModelError("", ErrorCodeToString(avm.StatusCode));
return View();
}
}
}
ViewModel
try
{
this.dbcontext = new MyContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString);
userRepository = new Repository<MyUser>(dbcontext);
//Step 1 - Check User is in user table.
MyUser userCheck = userRepository.Get(u => u.Username == this.Username).ToList().FirstOrDefault();
if (userCheck == null)
{
StatusCode = "NoUserError";
return;
}
else
{
//Step 2 - Check user has not already registered
if (userCheck.Password != null || userCheck.Answer != null)
{
StatusCode = "AlreadyRegistered";
return;
}
}
//Step 3 - Check the email is valid and the password confirms to password length.
Regex expEmail = new Regex(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
if (!expEmail.IsMatch(this.Email))
{
StatusCode = "InvalidEmail";
return;
}
if (this.Password.Length < 8)
{
StatusCode = "InvalidPassword";
return;
}
//Encrypt the password to store in SQL Azure. It does not at this point have any encryption.
EncryptionUtils encryptor = new EncryptionUtils();
string encrytpedPassword = encryptor.Encrypt(this.Password);
//Form defined fields
userCheck.Username = this.Username;
userCheck.Password = encrytpedPassword;
userCheck.Forename = this.Forename;
userCheck.Surname = this.Surname;
userCheck.Email = this.Email;
userCheck.Answer = this.Answer;
userCheck.DOB = this.DOB;
//Automatically defined values
userCheck.JoinDate = DateTime.Today;
userCheck.LastLogin = DateTime.Now;
userCheck.CompanyID = 5;
userCheck.RoleID = 3;
userCheck.ParticipationPoints = 0;
userCheck.Privacy = 0;
userCheck.IsDeleted = false;
userCheck.ImageURL = "/Images/user-holder.jpg";
userRepository.Update(userCheck);
userRepository.SaveChanges();
StatusCode = "Success";
}
catch (Exception ex)
{
StatusCode = "Error";
return;
}
}
Now when I hit the home controller I am not able to access the HttpContext.Current.User.Identity.Name value. Has the authenticated username been stored elsewhere due to the changes?
Authentication cookie must be issued after registration success. Try,
if (avm.StatusCode == "Success")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToAction("Index", "Home");
}
hope this helps.
Related
When I sign in with SignInAsync in ASP.NET Core Identity, RedirectToLocal is not authenticated.
If I log in without returning Url or go to allow anonymous action, it works fine, but when I redirect authenticate action return to the login page like the user never signs in. Still, I go to allow anonymous action and see the user sign in everything is okay.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWithSms(string userId, string code, string returnUrl)
{
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
if (code == null)
{
throw new ArgumentNullException(nameof(code));
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
throw new ApplicationException(string.Format(ErrorConstant.UnableToLoadUser, userId));
}
var result = await _userManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
return View("AccountError", this.GetErrorVm(AccountConstant.Persian.ConfirmSms, ErrorConstant.Persian.CodeWrong));
}
if (!user.PhoneNumberConfirmed)
{
user.PhoneNumberConfirmed = true;
_context.Users.Update(user);
_context.SaveChanges();
}
await _signInManager.SignInAsync(user, true);
await _setUserActivityLog.SetLogAsync(user.Id, AccountConstant.Persian.LoginToProfile);
return RedirectToLocal(string.IsNullOrEmpty(returnUrl) ? AccountConstant.Routes.ReturnUrlManageIndex : returnUrl);
}
redirect action:
[HttpGet]
[ActionDetail(menuCode: MenuConstant.ManageService.Code, name: "پاسخ دادن به تیکت")]
public async Task<IActionResult> TicketAnswer(long id)
{
var baseTicket = await _context.Tickets.Include(t => t.TicketType).Include(t => t.TicketRecords)
.ThenInclude(tr => tr.Person)
.SingleOrDefaultAsync(t => t.Id == id);
if (baseTicket == null)
{
return NotFound();
}
var vm = new ManageVm<TicketAnwserVm>
{
Entity = new TicketAnwserVm
{
QuickResponses = _context.QuickResponses.OrderBy(qr => qr.Title).Select(qr => new QuickResponseVm
{
Id = qr.Id,
Title = qr.Title
}),
Ticket = new TicketDisplayVm(baseTicket.StartDate)
{
Id = baseTicket.Id,
PersonId = baseTicket.PersonId,
State = baseTicket.State,
Subject = baseTicket.Subject,
TicketTypeName = baseTicket.TicketType.Name,
TicketRecords = baseTicket.TicketRecords.Join(_context.Users, tr => tr.PersonId,
u => u.PersonId,
(tr, u) => new TicketRecordVm(tr.Date)
{
Id = tr.Id,
PersonName = tr.Person.Name,
UserId = u.Id,
Content = tr.Content,
IsOwner = tr.IsOwner,
TicketId = tr.TicketId,
Status = tr.IsOwner ? TicketStatus.Out : TicketStatus.In
})
}
}
};
return View(vm);
}
I am building an application using ASP.NET CORE 3.1 and built identity using built in funcations. When I delete or update I want to delete users and related entities from aspnetuserroles and aspnetusers.
I made an update users but seems verbose and I cannot make it work yet with a postman error.
Error
405 Method not allowed
I can sort this error out but I think the code is too verbose. Do you have any examples or a better way to do this?
Postman request
{
"email":"user#gmail.com",
"password":"Passw0rd",
"username":"user4",
"roles":["user"],
"firstname":"user",
"lastname":"Lee",
"phonenumber":"12345"
}
postman url
https://localhost:4200/api/accounts/05ce20c6-e2c4-4d40-b364-85c03206d562
Update Users
[Route("accounts/{id}")]
[HttpPut]
public async Task<IActionResult> UpdateUser(string id, [FromBody] RegistrationViewModel model)
{
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
var user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return NotFound();
}
var userRoles = await _userManager.GetRolesAsync(user);
IdentityResult roleRemoveResult = await _userManager.RemoveFromRolesAsync(user, userRoles);
if (!roleRemoveResult.Succeeded)
{
ModelState.AddModelError("", "Failed to remove roles");
return BadRequest(ModelState);
}
IdentityResult roleAddResult = await _userManager.AddToRolesAsync(user, model.Roles);
if (!roleAddResult.Succeeded)
{
ModelState.AddModelError("", "Failed to add roles");
return BadRequest(ModelState);
}
//finished the role removing and assinging new roles
IdentityResult passwordResult = await _userManager.ChangePasswordAsync(user, user.PasswordHash, model.Password);
user.Email = model.Email;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.PhoneNumber = model.PhoneNumber;
user.UserName = model.UserName;
IdentityResult updateUser = await _userManager.UpdateAsync(user);
if (!updateUser.Succeeded)
{
ModelState.AddModelError("", "Failed to update a user");
return BadRequest(ModelState);
}
var newUser = new User_Role_ViewModel();
newUser.Id = id;
newUser.Email = model.Email;
newUser.FirstName = model.FirstName;
newUser.LastName = model.LastName;
newUser.UserName = model.UserName;
newUser.Roles = model.Roles;
newUser.PhoneNumber = model.PhoneNumber;
return Ok(newUser);
}
i am try to login with Microsoft Authentication in .net core application but it's giving me error Like
a database operation failed while processing the request
i am getting this error when i am try to execute below code
enter code here
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// GetAsync the information about the user from the external login provider
var fname = Request.Form["Input.FirstName"].ToList().First();
if (string.IsNullOrEmpty(fname))
{
ErrorMessage = "FirstName Name is Required.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
var Lname = Request.Form["Input.LastName"].ToList().First();
if (string.IsNullOrEmpty(Lname))
{
ErrorMessage = "LastName Name is Required.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
var pNumber = Request.Form["Input.PhoneNumber"].ToList().First();
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)
//{
//Todo: set all required fields
var user = new User { UserName = Input.Email, Email = Input.Email,FirstName= fname, LastName=Lname,PhoneNumber=pNumber,IsActive=true };
var searchKey = Input.Email.Split("#").Last();
var organization = _userRepository.SearchOrganization(searchKey);
if (organization != null)
user.OrganizationId = organization.Id;
var result = await _userManager.CreateAsync(user);
//Todo: Set Registered role
//Todo: Detect and assign organization
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_userManager.AddToRoleAsync(user, SmartRole.RegisteredUser).Wait();
_userManager.AddToRoleAsync(user, SmartRole.Anonymous).Wait();
user.IsAdmin =
_signInManager.UserManager.IsInRoleAsync(user, "Administrator").Result;
await _signInManager.SignInAsync(user, isPersistent: false);
_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();
}
i am try to compare some code with database that time i am getting this error
public Model.Organization SearchOrganization(string searchKey)
{
return DbContext.Organizations.FirstOrDefault(x =>x.URL.Equals(searchKey,StringComparison.OrdinalIgnoreCase));
}
So I am using LDAP to authenticate my users, and this works fine for most all of my users, but for some of them it does not. I know for a fact I have two users that it is not working for. The code pulls down all the information about the person, creates a formsAuthenticationticket, but when it gets to [authorize] it just bounces them back to the login page. The question is why?
Login controller:
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
string logon_user = model.UserName.ToString();
string logon_password = model.Password.ToString();
ConnHelper connhelper = new ConnHelper();
string encryptedTicket = null;
String adPath = "#####"; //Path to the 2003 LDAP directory server
ADAuthorize adAuth = new ADAuthorize(adPath);
FormsAuthenticationTicket authTicket = null;
try
{
if (true == adAuth.IsAuthenticated("#####", logon_user, logon_password))
{
string groups = adAuth.GetGroups();
Account acc = new Account();
acc.windows_id = logon_user;
acc.password = logon_password;
acc.gers_id = connhelper.GetGersID(acc.windows_id);
acc.region = connhelper.IsNull(connhelper.GetRegionManager(acc.gers_id));
acc.home_store_region = connhelper.IsNull(connhelper.GetHomeStoreRegion(acc.gers_id));
acc.store_group = connhelper.IsNull(connhelper.GetStoreGroup(acc.gers_id));
acc.home_store = connhelper.IsNull(connhelper.GetStore(acc.gers_id));
acc.arr = connhelper.GetStores(acc.gers_id);
//acc.home_store_phone = misc.IsNull(misc.GetHomeStorePhoneNumber("hzs"), "");
acc.home_store_phone = connhelper.IsNull(connhelper.GetHomeStorePhoneNumber(acc.gers_id), "");
acc.full_name = connhelper.IsNull(connhelper.GetFullName(acc.gers_id), "");
// Onlt use the following in the core
// acc.full_name = adAuth.getuserFname("#####", logon_user, logon_password);
misc.GetStore(acc.gers_id);
//Add information to the session
Session.Add("roles", groups);
Session.Add("Account", acc);
// Create the authentication ticket
authTicket =
new FormsAuthenticationTicket(1, // version
acc.windows_id,
DateTime.Now,
DateTime.Now.AddMinutes(500),
false, groups);
// Now encrypt the ticket.
encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Create a cookie and add the encrypted ticket to the cookie as data.
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
// Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
if (FormsAuthentication.GetRedirectUrl(acc.windows_id, false).EndsWith("Logout.aspx"))
{
return RedirectToAction("Login", "Account");
}
//
// Validate code this does the redirect to where you want the logged in person to go to.
//
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Authentication failed, check username and password.");
return View(model);
}
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error authenticating. " + ex.Message + ex.StackTrace);
return View(model);
}
// return View(model);
}
Adauth is authenticated ( this returns true even for the people who get bounced)
public bool IsAuthenticated(string domain, string username, string pwd)
{
cred(username, pwd);
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("SAMAccountName");
//search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
// Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (String)result.Properties["SAMAccountName"][0];
//_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return true;
}
Then the home index controller
[HttpGet]
[Authorize]
public ActionResult Index()
{
//grab all events and pass to view
//
int count = D.getEventRows();
if (count != 0)
{
Event[] events = new Event[count];
events = D.getEvents(count);
ViewBag.host = globals.hosts();
ViewBag.events = events;
DateTime curr = DateTime.Now;
ViewBag.curr = curr;
return View(events);
}
return View();
}
web config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="28800" />
</authentication>
<authorization>
<allow users="*" />
</authorization>
my colleague and myself are working on an application form with login functionality the user logs in from the mvc 4 app and there details are submitted to the web api to be checked against the values held in the database once verified the web api returns a loginResult class that contains the error message (if any) and a bool for stating whether it has been successful or not.
at the mvc 4 application level the code below is used to submit the login details to the web api:
Login Action
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(AccountViewModel model)
{
if (!ModelState.IsValid) return View("Login", model);
await _client.PostAsJsonAsync("api/Applicant/CheckApplicant", model)
.ContinueWith((postTask) => postTask.Result.EnsureSuccessStatusCode());
var service = DependencyResolver.Current.GetService<IApplyService>();
var loginResult = service.GetLoginResult();
var loginSuccess = loginResult.LoginSuccess;
if (loginSuccess != null && (bool) loginSuccess)
{
FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe);
return RedirectToRoute("Terms And Conditions");
}
return View("Login");
}
the login details are then received at the web api in this method:
Check Applicant Method
public String CheckApplicant(Applicant applicant)
{
Int32 passwordFailureTimeoutMins = Convert.ToInt32(System.Configuration.ConfigurationSettings.AppSettings["PasswordFailureTimeoutMins"]);
Int32 passwordFailureAttempts = Convert.ToInt32(System.Configuration.ConfigurationSettings.AppSettings["PasswordFailureAttempts"]);
ApplicantRepository applicantRepository = new ApplicantRepository();
Applicant applicantDB = applicantRepository.GetById(applicant.Email);
LoginResult loginResult = new LoginResult();
PasswordHelper passwordHelper = new PasswordHelper();
if (applicantDB == null)
{
loginResult.LoginSuccess = false;
loginResult.LoginError = "Your password or login may not be correct.";
}
else
{
bool loginFailureCheck;
if (applicantDB.LoginFailureCount > passwordFailureAttempts)
{
System.TimeSpan diffResult = DateTime.Now.Subtract(Convert.ToDateTime(applicantDB.LastLoginFailure));
if (diffResult.Minutes < passwordFailureTimeoutMins)
{
loginFailureCheck = false;
}
else
{
loginFailureCheck = true;
}
}
else
{
loginFailureCheck = true;
}
if (passwordHelper.CheckPassword(applicant.Password, applicantDB.Password))
{
if(loginFailureCheck)
{
if(applicantDB.AccountActive)
{
loginResult.LoginSuccess = true;
loginResult.LoginError = "Login Successful.";
applicantDB.LastLoginFailure = null;
applicantDB.LastLoginSuccess = DateTime.Now;
applicantDB.LoginFailureCount = 0;
applicantRepository.Update(applicantDB);
}
else
{
loginResult.LoginSuccess = false;
loginResult.LoginError = "This account has been permanently banned.";
}
}
else
{
loginResult.LoginSuccess = false;
loginResult.LoginError = "This account is now temporarily disabled please wait " + passwordFailureTimeoutMins + " minutes before trying again";
applicantDB.LastLoginFailure = DateTime.Now;
applicantDB.LoginFailureCount = applicantDB.LoginFailureCount + 1;
applicantRepository.Update(applicantDB);
}
}
else
{
loginResult.LoginSuccess = false;
loginResult.LoginError = "Your password or login may not be correct.";
applicantDB.LastLoginFailure = DateTime.Now;
applicantDB.LoginFailureCount = applicantDB.LoginFailureCount + 1;
applicantRepository.Update(applicantDB);
}
}
return JsonConvert.SerializeObject(loginResult);
}
as you can see it returns a JsonConvert.SerializeObject(loginResult).
when this is done the process returns to the Login ActionResult as above it then moves to the GetLoginResult() method as shown below:
GetLoginResult
public LoginResult GetLoginResult()
{
const string uri = "http://localhost:55830/api/Applicant/CheckApplicant";
using (var httpClient = new HttpClient())
{
var response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObject<LoginResult>(response.Result);
}
}
when it get to this point it returns an error 405 method not allowed.
How do I consume the loginResult at the mvc 4 app level and what is the best way of sending the loginResult from the web api?
Any advice would be greatly appreciated.
Not sure what exactly you are trying to do but are you making a GET to read the result of the previous POST? You can read the response message of POST to get the result, like this.
public async Task<ActionResult> Login(AccountViewModel model)
{
if (!ModelState.IsValid) return View("Login", model);
var message = await _client.PostAsJsonAsync
("api/Applicant/CheckApplicant", model);
message.EnsureSuccessStatusCode();
LoginResult result = await message.Content.ReadAsAsync<LoginResult>();
// do other stuff here
}
Change the web API action method to return LoginResult directly. The framework will serialize it for you.
public LoginResult CheckApplicant(Applicant applicant)
{
}