Web API Individual Accounts: how add Roles - c#

in ASP Web API i use the individual accounts to register the user. I have implemented this functionality following this official tutorial:
Web API Individual Accounts
Now i want be able to assign a Role to the user: when is executed the Register method i want pass also the role in the POST request:
{
"UserName": "Alice",
"Password": "password123",
"ConfirmPassword": "password123",
"Role": "admin" -> i want add this
}
and the Register method must value the database's table properly.
How can i handle the roles in the web api individual accounts? There's any tutorial?
Thanks

Just add the following line in your code after you have created the user and the appropriate role
var roleresult = UserManager.AddToRole(currentUser.Id, "RoleName");
You should also add the Role property to your view model (it is normally called RegisterBindingModel) which is passed to the Register method.
Update
Here's a complete example how a Register method could look like (RoleEntity and UserEntity are my implementations of the appropriate classes in Identity, but this will also work with your custom implementation)
public async Task<IdentityResult> RegisterAsync(RegisterViewModel model)
{
var user = new UserEntity { UserName = model.UserName };
var role = model.Role.ToString();
if (await _roleManager.FindByNameAsync(role) == null)
{
var roleResult = await _roleManager.CreateAsync(new RoleEntity(role));
if (roleResult != IdentityResult.Success)
{
return roleResult;
}
}
var result = await _userManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return result;
}
var addToRoleResult = await _userManager.AddToRoleAsync(user.Id, role);
return !addToRoleResult.Succeeded ? addToRoleResult : IdentityResult.Success;
}
My RegisterViewModel looks something like this
public class RegisterViewModel
{
[Required(ErrorMessage="This field is required")]
[Display(Name = "User name", Prompt="Please enter user name...")]
public string UserName { get; set; }
[Required(ErrorMessage = "This field is required")]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password", Prompt="Please enter password...")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password", Prompt = "Please enter confirm password...")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required(ErrorMessage = "This field is required")]
[Display(Name = "Role")]
public Roles Role { get; set; }
}

Related

Why is RedirectToAction() method not working in Authentication?

I am doing authentication for a project. When I redirect it to another controller after authentication, it's not working. After I remove [Authorize] in the redirected controller, it's getting redirected. So, my understanding is that the authentication is not working. I followed a tutorial and in that it's getting redirected to the controller with [Authorize]. I rewatched the whole tutorial again and again and I sill can't figure out what went wrong. Please help me with this.
These are my Model classes
User.cs
[Display(Name = "First Name")]
[Required(AllowEmptyStrings = false, ErrorMessage = "First name is required.")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required(AllowEmptyStrings = false, ErrorMessage = "Last name is required.")]
public string LastName { get; set; }
[Display(Name = "Email Id")]
[Required(AllowEmptyStrings = false, ErrorMessage = "Email Id is required.")]
public string EmailId { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
[Required(AllowEmptyStrings = false, ErrorMessage = "Password is required.")]
[MinLength(6, ErrorMessage ="Minimum 6 characters are required.")]
public string Password { get; set; }
[Display(Name = "Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords don't match")]
public string ConfirmPassword { get; set; }
UserLogin.cs
[Display(Name ="Email Id")]
[Required(AllowEmptyStrings = false, ErrorMessage ="Email id required")]
public string EmailId { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
[Required(AllowEmptyStrings = false, ErrorMessage = "Password required")]
public string Password { get; set; }
[Display(Name ="Remember Me")]
public bool RememberMe { get; set; }
Controller Clases
AccountController
[HttpGet]
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
//POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(UserLogin details, string ReturnUrl = "")
{
string message = "";
using (var context = new PerformaxLiteEntities1())
{
var users = context.Users.Where(x => x.EmailId.ToLower().Equals(details.EmailId.ToLower())).FirstOrDefault();
if(users != null)
{
if( details.Password != null && string.Compare(Crypto.Hash(details.Password), users.Password) == 0)
{
int timeOut = details.RememberMe ? 525600 : 30;
var ticket = new FormsAuthenticationTicket(details.EmailId, details.RememberMe, timeOut);
string encrpted = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrpted);
cookie.Expires = DateTime.Now.AddMinutes(timeOut);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else
{
//Debugger hits here. But redirection is not happening.
//It only happens when I remove [Authorize] in Home Controller.
return RedirectToAction("Index", "Home" );
}
}
else
{
message = "Invalid credentials";
}
}
else
{
message = "Invalid credentials";
}
}
ViewBag.Message = message;
return View();
}
Home Controller
[Authorize]
public class HomeController : Controller {
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index()
{
return View();
}
}
I have also added this in my Webconfig file.
<authentication mode="Forms">
<forms cookieless ="UseCookies" loginUrl="~/Account/Login"
slidingExpiration="true"></forms>
</authentication>
I am new to Authentication and I have been sitting with this problem for some days. Kindly help me out.
you never actually make user login in owin
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
so in you account controller use usermanager and if user is logged in by user manager then whole authorization will work
To read the FormsAuthentication cookie, normally you would hook the AuthenticateRequest event in a HttpModule or the Global.asax and set up the user IPrinciple context.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if(authCookie != null)
{
//Extract the forms authentication cookie
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
// If caching roles in userData field then extract
string[] roles = authTicket.UserData.Split(new char[]{'|'});
// Create the IIdentity instance
IIdentity id = new FormsIdentity( authTicket );
// Create the IPrinciple instance
IPrincipal principal = new GenericPrincipal(id, roles);
// Set the context user
Context.User = principal;
}
}
source How can I manually create a authentication cookie instead of the default method?
-->IIdentity id = new FormsIdentity( authTicket );
Context.User = principal;<-------
if (HttpContext.Current.User.Identity.IsAuthenticated)
[]-_-[]
nothing else will work probably otherwise you want to try there is hint override authorize attribute
I have found out the issue with this. In my Web.config Forms Authentication was disabled by default. When I started the project, I gave Identity based authentication. I am assuming it's because of that Forms Authentication was disabled.
<system.webServer>
<modules>
<remove name="FormsAuthentication"/>
</modules>
</system.webServer>
Comment out or delete the above lines in the Web.config and you are good to go.
If you are still having problems then, make sure Form Authentication is enabled in IIS Manager. In order to check that, Go to start, type IIS, then select IIS Manager, go to Authentication and check Forms authentication is enabled.

Adding a dropdown of Roles in the built-in Register.cshtml view and retrieving its selected value in the Reginster action method

I've an ASP.NET Core app with Individual User Accounts authentication. The app by default creates AccountController, RegisterViewModel, Register.cshtml etc as shown below. I've added two properties for a Role dropdown in the RegisterViewModel and I'm displaying that dropdown in the Register.cshtml view so an admin can select a role for a newly created user.
Question: In the following Post method for Register, how do I retrieve the role admin selected from the Register.cshtml view? In other words, in this Post action method how do I use that selected role so I can add the newly created user to that role?
RegisterViewModel:
public class RegisterViewModel
{
[Required]
[Display(Name = "Login")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Display(Name = "Select Role")]
public ApplicationRole SelectedRole { get; set; }
public IEnumerable<SelectListItem> Roles { get; set; }
}
AccountController Action Method:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.UserName, StateID=model.StateID, Region=model.Region };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
to your view model add a selected parameter
[Display(Name = "Selected Role")]
public int SelectedRole{ get; set; }
then on your view setup the drop down like this
#Html.DropDownListFor(x => x.SelectedRole, model.Roles)
this will tie the result of the drop down to your model. then on your controller model.SelectedRole should have your selected id

How can I send a Confirmation Email in asp.net mvc

Today i'm trying to follow this article of Shai Raiten's Blog and when I finish it the createStatus return invalidAnswer
here is my Register action
[HttpPost]
[AllowAnonymous]
[CaptchaValidation("CaptchaCode", "registerCaptcha", "Wrong captcha!")]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, false, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
MailHelper.SendConfirmationEmail(model.UserName);
return RedirectToAction("Confirmation", "User");
}
else
{
ModelState.AddModelError("", "Failed!");
}
}
return View(model);
}
and here is my RegisterModel.cs
public class RegisterModel
{
[Key]
public long ID { set; get; }
[Required(ErrorMessage = "Do not Skip this")]
public string UserName { set; get; }
[StringLength(500, MinimumLength = 6, ErrorMessage = "Atleast 6 characters in passwords")]
[Required(ErrorMessage = "Do not Skip this")]
public string Password { set; get; }
[Compare("Password", ErrorMessage = "Wrong confirm passwords")]
[Required(ErrorMessage = "Do not skip this")]
public string ConfirmPassword { set; get; }
public string Name { set; get; }
public string Address { set; get; }
[RegularExpression(#"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", ErrorMessage = "This is not an email")]
public string Email { set; get; }
public string Phone { set; get; }
public bool EmailConfirm { set; get; }
}
any suggestion for me , really appreciated all the help you guys make.
The simplest thing you can do is:
First, you should define a property in your user model which will hold email confirmation token. Also, you should define property bool IsEmailConfirmed which defaults to false.
The token should be something like auto-generated random string. E.g. Guid.NewGuid().ToString()
Then, you should define another action, say [HttpGet, AllowAnonymous] ConfirmEmail(string email, string token), which will validate that token against saved in the database and update IsEmailConfirmed accordingly.
And the link you are asking about, should then point to an url which will look like something like that: http://YOUR.SERVER/YourController/ConfirmEmail?email={0}&token={1}, where {0} is user email and {1} is your user email confirmation token. It should return a view that tells whether confirmation was successfull.
However, i do recommend not to reinvent the wheel and to simply use Asp.Net Identity 2.0 framework, which will do all that authn & authz stuff for you.
Please follow the below example from ASP.Net site where its beautifully explained how to send email during registration prcoess.
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset
Additionally I would not recommend MD5 password hashinh since its pretty old, try using SHA 256 hashing for password encryption.
http://forums.asp.net/t/1211478.aspx?How+do+I+use+Sha256+to+Encrypt+a+String+

Get password from a user with UserManager

I'm making a website with MVC5 ASP.NET.
I'm using Identity framework 2.0 implement class with properties such as passwordhash, username, email, emailconfirmed and so on. I'm using userManager.ChangePassword(user.Id, Oldpassword, Newpassword);, but i can't figure out how i should get a password from a user as plain text (string)
[HttpPost]
public ActionResult ChangePassword(AspNetUsersViewModel userView)
{
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, "123456789", userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
As now I'm i have hardcoded users current password "123456789" to test if it works, and it does.
I hope you guys can help.
Add password input to the View inside the form tag
<input type="password" id= "userNewPassword" name="userNewPassword">
Pass the userNewPasswor as string to the controller after the userView and the pass it to the UserManager
[HttpPost]
public ActionResult ChangePassword(
AspNetUsersViewModel userView,
string userNewPassword){
UserManager<IdentityUser> userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var result = userManager.ChangePassword(_User.Id, userNewPassword , userView.Password);
return RedirectToAction("Index", "ConfigUser");
}
Note: the Best way is to Modify the userView and add the userNewPassword to the model
Update:
in the visual studio 2013 the if you used the asp.net default template you will find the flowing class
public class ChangePasswordBindingModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

No validation other than Required working for my test method

I am creating a test method in Asp.Net MVC 5. I am using following code to test my registration method
[TestMethod]
public async Task Registration_Test_For_Password_And_Confirm_Password_Mismatch()
{
// Arrange
var registerationModel = new RegisterViewModel() { UserName = "abc", Password = null, ConfirmPassword = "jjk", Email = "nitin.daiya#sunarctechnologies.com" };
// Validate model state start
var validationContext = new ValidationContext(registerationModel, null, null);
Validator.TryValidateObject(registerationModel, validationContext, validationResults);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
}
// Validate model state end
// Act
var result = await controller.Register(registerationModel) as ViewResult;
// Assert
Assert.AreEqual("", result.ViewName);
Assert.IsFalse(controller.ModelState.IsValid);
}
Above code is working fine if I pass empty values for the Required fields.
If I try to check any other validation it is not showing the error message and test fails each time.
Following is my ViewModel
public class RegisterViewModel
{
public string UserId { get; set; }
[Required]
[System.Web.Mvc.Remote("IsUserNameAvailable", "Account", ErrorMessage = "That UserName is already taken.")]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[EmailAddress]
[System.Web.Mvc.Remote("IsEmailAvailable", "Account", ErrorMessage = "That email is already taken.")]
[Display(Name = "Email")]
public string Email { get; set; }
}
Any help will be appreciated
EDIT: Solved the issue by changing Validator.TryValidateObject(registerationModel, validationContext, validationResults); to Validator.TryValidateObject(registerationModel, validationContext, validationResults,true); this. True is set for validateAllProperties property to check all validation.
It still not testing the remote validation. Any clue??
Validator.TryValidateObject(registerationModel, validationContext, validationResults)
This line will by default only validate required properties. To validate all properties, you need to set the validateAllProperties parameter to true:
Validator.TryValidateObject(registerationModel, validationContext, validationResults, true);
(Adding this as it wasn't immediately clear that this question had an answer)

Categories