How to create roles and add users to roles in ASP.NET MVC Web API - asp.net-web-api

I have a .NET Web API project that users the individual accounts. I can register users fine using the standard template AccountController. However, I now want to set up roles and add users to roles depending on the type of user.
There are no roles automatically set up in the DB. How do I set up the roles and how do I add users to the roles?
The only information I can find on this is based on the old ASP.NET Membership, so it fails on the fact that the stored procedures are not set up for it.
Have scoured forums and tutorials on MSDN and can't seem to find an example for Web API.

You can add roles using the RoleManager...
using (var context = new ApplicationDbContext())
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
await roleManager.CreateAsync(new IdentityRole { Name = "Administrator" });
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "admin" };
await userManager.CreateAsync(user);
await userManager.AddToRoleAsync(user.Id, "Administrator");
}
You're right that documentation is a bit light right now. But I find that once you've worked with the RoleManager and the UserManager a bit, the API's are pretty discoverable (but perhaps not always intuitive and sometimes you have to run queries directly against the store or even the db context).

It took me awhile to figure out but I finally got it. Anthony please excuse me but going to repost a lot of your code so that dumb developers like me can understand.
In the latest WebAPI2 (Visual Studio 2013 Update 2) the registration method will look like so:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
What you want to do is replace it with this:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result;
using (var context = new ApplicationDbContext())
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
await roleManager.CreateAsync(new IdentityRole() { Name = "Admin" });
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
result = await UserManager.CreateAsync(user, model.Password);
await userManager.AddToRoleAsync(user.Id, "Admin");
}
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Now when you post it should correctly work, but you may run into a further problem. After I did this my response complained about the DB.
The model backing the <Database> context has changed since the database was created
To fix this error I had to go into the Package Manager Console and enable Migrations.
Enable-Migrations –EnableAutomaticMigrations
Then:
Add Migration
Finally:
Update-Database
A good post on enabling migrations here:
http://msdn.microsoft.com/en-us/data/jj554735.aspx

Related

Creating a profile in the database once an user is registered, working with IdentityUser

I'm currently making a forum using ASP.NET and I'm trying out to generate a profile entry in the database once the user makes an account (I have a 'Profile' model with a foreign key to 'ApplicationUserId' deriving IdentityUser).
Though, I have used scaffolding on ApplicationUser and most of the generated code that manages user registration uses async functions located in Register.cshtml.cs and I don't thoroughly comprehend how they interact with the database. My method of creating a profile would've been to get the context of my database through
IServiceProvider.serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()
then created a Profile object with New Profile() and lastly added it to the database that way. Though that beats me because I don't know how to obtain the serviceProvider used to fetch the database context, inside the Register.cshtml.cs file.
Sorry for my poor understanding of .net, the documentation is overwhelming for me since I'm a beginner.
Phrased in another way, my question boils down to, how can I create an instance of a profile and add it to my database, when most of the user registration (to my understanding) uses pre-defined functions by the framework?
(Part of) register.cshtml.cs, in case I should include it
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await setDataAndCountsAsync(user);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
await _userManager.AddToRoleAsync(user, "User");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}

MVC NEt Core 5.0 - Identity help - Adding Users to database (models)

I'm new to Net Core and studying it at the moment. I am creating a web based app for users to create events and register for these events and and need help.
I have an EventsCoordinator and Member Model which inherit from my User model which inherits from ApplicationUser : IdenityUser. In my Account Register.cshtml, I can create new users and they are added to the database in my User table, however I'd like when they select the AccountType (enum value (EventCoordinator, Member) that they will then be added to the applicable table e.g. EventsCoordinator or Member, however I am having trouble doing this. I added an IF statement but not sure what to add then...
I've yet to add roles as I want to create different levels of authorization based on these account types but that will have to try work on that later.
Any help for this newbie, would be great!
Thanks!
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new User { UserName = Input.Email, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName, DOB = Input.DOB, Address = Input.Address, AccountType = (Models.AccountType)Input.AccountType };
if (AccountType == AccountType.EventCoordinator)
{
}
else
{
}
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
In your if statement add the the user to the table or assign the user its role (EventCoordinator ir Member).
If EventCoordinator & Member are Roles you add the user with the UserManager.
Like
await userManager.AddToRoleAsync(user,"EventCoordinator ");
If those 2 are not roles and are columns in a table in your DB
You add the users id to the specific column in your DB.
If possible specify where or how do you save the AccountType or want to save it

How to add default role to Asp.net identity user manager

I am new and exploring the Asp.net Identity, I wanted to add a default Role to my users. However, I failed to run this code during runtime and it shows me this error "Role admin does not exist."
var userManager = new UserManager<IdentityUser>(userStore);
var user = userManager.Find(UserName.Text, Password.Text);
userManager.AddToRole(user.Id,"admin");
I am aware that i didn't created the Role of "admin", Does there any Simplest way to implement this Role based authorization?
Here is how it should be done, first check if the role exists, and if it doesn't add the role.
public async Task<ActionResult> Register(RegisterViewModel model, ApplicationDbContext context)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
if (!roleManager.RoleExists("Administrator"))
{
await roleManager.CreateAsync(new IdentityRole("Administrator"));
}
await UserManager.AddToRoleAsync(user.Id, "Administrator");
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Notice how the ApplicationDbContext is passed in the method signature.

MVC 5 Roles Out-of-the-box

Grettings. I see a thousand of questions and i lost on every one of them... So... Basically i start a new project on VS (WebAPI) with Authentication. I put the token on the header and the methods with
[Authorize]
works fine. Later i add a two roles into the table dbo.AspNetRoles (admin and users) and to one user i add the relationship in the table dbo.AspNetUserRoles like this:
USER ID | Roleid
-----------------------
1d156e98-fc8b-4dcb-8dba-f7c66131f488 | 1001
So, when i try to put this:
[Authorize(role="admin")]
Dont work... The request is denied.
What i need to do exactly?
Thanks
It's not "Authentication" but "Authorize". Try this:
[Authorize(Roles = "admin")]
But first you've to create your roles:
context.Roles.Add(new IdentityRole { Name = "admin" });
context.SaveChanges();
And assign role to the user:
var role = context.Roles.SingleOrDefault(m => m.Name == "admin");
user.Roles.Add(new IdentityUserRole { RoleId = role.Id });
Database initialization code can be put anywhere you want, it depends on you:
when the application starts - check if roles are there, if no then create them
generate migration and update the migration script by custom role inserts
put them manually in the database - BUT YOU HAVE TO DO IN PROPER WAY - add roles from the code and check what has been changed in the database
So at the end i use this following code to resolve this:
public class DAO
{
public static void addRoleToUser(ApplicationUser user, string role)
{
// EL SIGUIENTE CODIGO AGREGA AL USUARIO UN ROL
ApplicationDbContext context = new ApplicationDbContext();
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
userManager.AddToRole(user.Id,role);
}
}
This sync the role to the user and the context db.
In the controller after register a new user automatically adds the rol "User" with the code:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
// Codigo de Ali para agregar el rol "User" al usuario inmediatamente es creado
DAO.addRoleToUser(user, "User");
return Ok();
}
Thanks to dawidr to help me to go deep on this.

How to get user context during Web Api calls?

I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()

Resources