Creating a profile in the database once an user is registered, working with IdentityUser - asp.net-core-mvc

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

Related

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 are we meant to use IUserClaimsPrincipalFactory<T>?

I'm confused by the example in the documentation here that describes how to add claims using IUserClaimsPrincipalFactory.
The sample code shows how to extend the ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
...and then implement a UserClaimsPrincipalFactory that tests that property to determine which claims to add:
if (user.IsAdmin)
{
claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
}
else
{
claims.Add(new Claim(JwtClaimTypes.Role, "user"));
}
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin property for a user in the database. I think they could have made that clear. (Also, it's disappointing that the example uses roles when there's so much confusion around roles versus claims, but I digress...)
Anyway, we have added some "role" claims to the user based on the value of that new IsAdmin property. So far, so good. What I don't understand is the next bit:
The additional claim can then be used in the app. In a Razor Page, the IAuthorizationService instance can be used to access the claim value.
Sounds like the Razor page is going to access our claim then - but here's the code:
#if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
...
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
What a terrible piece of documentation this is - and I'm ignoring the fact that it's also in the wrong place.
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin property for a user in the database.
You can set the IsAdmin where you want,For example you can set it when register.Here is a demo:
Input Model in register:
public class InputModel
{
...
public bool IsAdmin { get; set; }
}
Post handler:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email ,IsAdmin=Input.IsAdmin};
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();
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
IsAdmin is a ploicy name in the code,you need to add a policy which name is IsAdmin,and check new role claim in it.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("IsAdmin", policy => policy.RequireClaim("role", "admin"));
});
}
result:

SignInStatus always returns Success on TwoFactorAuthentication is enabled in webapi using asp.net identity

I am implementing 2 factor authentication in WebApi, asp.net identity and OWIN. Every time I log in, I get SignInStatus = Success never reaches to SignInStatus = RequiresVerification though user TwoFactorAuthentication is enabled.
Below are some code snippets,
Startup.cs:
private void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseOAuthBearerTokens(OAuthOptions);
}
Action method for enabling two factor authentication,
[HttpPost]
public async Task<IHttpActionResult> EnableTwoFactorAuthentication()
{
var user = await this.AppUserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
IdentityResult result = await this.AppUserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
await this.AppSignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
}
return Ok();
}
Please suggest a solution.
If you get stuck here, one way to solve the problem is to copy the methods from SignInManager directly into your code and call those instead so you can step through the methods and see why you are getting the wrong status. For me the problem ended up being that I instantiated my UserManager with:
new MyUserManager()
instead of the right way:
HttpContext.GetOwinContext().Get<MyUserManager>()
I was using this as my template for setting it up:
https://github.com/adamtuliper/ASP.NET-Identity-Samples/tree/master/BasicTemplate%20-%20Two%20Factor/BasicTemplate
SignInManager return RequiresVerification if :
dbo.ASpnetUsers has for user set to true TwoFactorEnabled and EmailConfirmed and user email should be confirmed, email not be empty or null.
var result = SignInManager.PasswordSignIn(usernameIdentity, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", returnUrl);
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}

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.

How to create roles and add users to roles in ASP.NET MVC 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

Resources