Login and session management in Asp.net Mvc 4 - asp.net-mvc-4

I want to create a application in which different users have login functionality such as admin, coach and student. Each has there own tasks. So here I want session handling and I am new in asp.net mvc.

Here is a Example. Say we want to manage session after checking user validation, so for this demo only I am hard coding checking valid user. On account Login
public ActionResult Login(LoginModel model)
{
if(model.UserName=="xyz" && model.Password=="xyz")
{
Session["uname"] = model.UserName;
Session.Timeout = 10;
return RedirectToAction("Index");
}
}
On Index Page
public ActionResult Index()
{
if(Session["uname"]==null)
{
return Redirect("~/Account/Login");
}
else
{
return Content("Welcome " + Session["uname"]);
}
}
On SignOut Button
Session.Remove("uname");
return Redirect("~/Account/Login");

Related

Authenticate using Azure active directory but Authorize user using aspmembership roles and claims

I am new to the development field. I am working on intranet application which is internal to our organization. I have implemented authentication where I am using windows credentials for login and verifying the username from Active directory. The login part with Azure AD authentication is completed. The next step is to authorize user from AspNetmembership roles and claims. How can I implement that? I have read many articles but didn't find any suitable solution that I can use in. Please help if you have already implemented or any idea on this. Thank you
This this the account controller I used for login. Not sure what to do next to authorize user with AspNetmembership roles and claims.
public class AccountController : Controller
{
public static UserModel userModel = new UserModel();
private bool ValidateUserActiveDirectory(string username, string pwd)
{
bool isValid = false;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "MAPLE"))
{
isValid = pc.ValidateCredentials(username, pwd);
if (isValid)
userModel.errMessage = "User Authenticated";
else
userModel.errMessage = "Username or Pasword not Authenticated, please contact administrator.";
}
return isValid;
}
public ActionResult LoginTest()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LoginTest(string uId, string uPwd)
{
userModel.uId = uId;
userModel.ISAppsConn = ConfigurationManager.ConnectionStrings["ISAppsConn"].ToString();
if (ModelState.IsValid)
{
if (ValidateUserActiveDirectory(uId, uPwd))
{
Session["userInformation"] = userModel;
var user = "Maple\\" + uId;
FormsAuthentication.RedirectFromLoginPage(user, false);
return RedirectToAction("Index", "Home");
}
}
return View(userModel);
}

In ASP.NET Core razor Login page, how to redisplay the Login page in the POST handler?

I've modified the logic in the standard ASP.NET Core Login page POST routine (Areas/Identity/Pages/Account/Login.cshtml.cs). After the user has logged in successfully, I have additional logic that may deny the login attempt. If that additional logic denies the login attempt, I want to redisplay the login page with an appropriate message displayed on the page.
My problem is that, unlike an MVC controller, where calling return View() in a POST action redisplays the view, calling return Page() in a Razor page apparently redirects to "/" (the default page in the website).
I have two questions:
In a Razor page POST routine, how do I redisplay the Razor page?
What does return Page() actually do in a POST routine?
Here is code to reproduce the behavior I see happening:
In VS 2022, create a new ASP.NET Core Web App (Model-View-Controller) project.
Framework: .NET 6.0
Authentication Type: Individual Accounts
In the Package Manager window, type: update-database
Run the application. Create a new account and verify that you can log in.
Right-click the project in Solution Explorer and select Add>New Scaffolded Item
Select Identity and click Add
Select Account\Login and click the drop-down arrow to select the ApplicationDbContext Data context class.
In Controllers/HomeController.cs, add an [Authorize] attribute to the Index method:
using Microsoft.AspNetCore.Authorization; // Added this line
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WebApplication3.Models;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
[Authorize] // Added this line
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Run the project. The [Authorize] attribute forces you to log in before it redirects to the Index page.
Now, open Areas/Identity/Pages/Account/Login.cshtml.cs and add some code to the OnPostAsync() routine that emulates the case where an error is detected after the user has successfully authenticated.
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid) {
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
// User is logged in, but we want to deny the login for some reason
ModelState.AddModelError("", "Some login error"); // <--------------
return Page(); // <--------------
// <snip>
}
// If we got this far, something failed, redisplay form
return Page();
}
Run the app. The new return Page() statement, which occurs after the user is authenticated, causes the app to happily display the Index page. It does not, as I would have expected, redisplay the login page with the model error displayed.
Just add a SignOutAsync when you want to return to the login page after a successful call to SignInAsync
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
// User is logged in, but we want to deny the login for some reason
await _signInManager.SignOutAsync();
ModelState.AddModelError("", "Some login error");
return Page();
}
TL,DR I suppose that we should look at the expected flow when the application starts.
The startup logic tries to reach the Index page, but this page has the [Authorize] attribute, so it cannot be displayed without a logged on user whatever roles we have in place.
Logically, the code flow needs to take a detour to the Login page. Here, when it receives the POST message, it looks for the credentials given and start the Identity method that leads to a logged in or not logged in user.
Exiting from the login page with a logged in user means that we have resolved the requirements for the [Authorize] attribute, else...
Now the code above logs in the user successfully and then starts some internal logic that should result in a return to the login page. But the code exits with the user still logged in, thus the Identity engine thinks that everything is ok and goes back the Index page that has caused the start of the identification process.
So we need to add the missing SignOutAsync before calling return Page();
You said,
My problem is that, unlike an MVC controller, where calling return
View() in a POST action redisplays the view, calling return Page() in
a Razor page apparently redirects to "/" (the default page in the
website).
I would suggest you add and check your additional logic before code executes if (ModelState.IsValid).
Example:
Login.cshtml.cs
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
string str = "invalid";
ViewData["Message"] = "";
if (str=="invalid")
{
ViewData["Message"] = "Something is invalid";
}
else
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
}
// If we got this far, something failed, redisplay form
return Page();
}
Then you could display the appropriate message(which we set in the 'if' condition in the example above) on the same login page like below.
Login.cshml
<p style="color:red; font-size:larger">
#ViewData["Message"]
</p>
Output:
If everything is fine then it will log in to the site.
Further, you could modify the logic as per your own requirements.

How to use Windows Active Directory Authentication and Identity Based Claims?

Problem
We want to use Windows Active Directory to authenticate a user into the application. However, we do not want to use Active Directory groups to manage authorization of controllers/views.
As far as I know, there is not an easy way to marry AD and identity based claims.
Goals
Authenticate users with local Active Directory
Use Identity framework to manage claims
Attempts (Fails)
Windows.Owin.Security.ActiveDirectory - Doh. This is for Azure AD. No LDAP support. Could they have called it AzureActiveDirectory instead?
Windows Authentication - This is okay with NTLM or Keberos authentication. The problems start with: i) tokens and claims are all managed by AD and I can't figure out how to use identity claims with it.
LDAP - But these seems to be forcing me to manually do forms authentication in order to use identity claims? Surely there must be an easier way?
Any help would be more than appreciated. I have been stuck on this problem quite a long time and would appreciate outside input on the matter.
Just hit AD with the username and password instead of authenticating against your DB
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.UserName);
if (user != null && AuthenticateAD(model.UserName, model.Password))
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View(model);
}
public bool AuthenticateAD(string username, string password)
{
using(var context = new PrincipalContext(ContextType.Domain, "MYDOMAIN"))
{
return context.ValidateCredentials(username, password);
}
}
On ASPNET5 (beta6), the idea is to use CookieAuthentication and Identity : you'll need to add in your Startup class :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization();
services.AddIdentity<MyUser, MyRole>()
.AddUserStore<MyUserStore<MyUser>>()
.AddRoleStore<MyRoleStore<MyRole>>()
.AddUserManager<MyUserManager>()
.AddDefaultTokenProviders();
}
In the configure section, add:
private void ConfigureAuth(IApplicationBuilder app)
{
// Use Microsoft.AspNet.Identity & Cookie authentication
app.UseIdentity();
app.UseCookieAuthentication(options =>
{
options.AutomaticAuthentication = true;
options.LoginPath = new PathString("/App/Login");
});
}
Then, you will need to implement:
Microsoft.AspNet.Identity.IUserStore
Microsoft.AspNet.Identity.IRoleStore
Microsoft.AspNet.Identity.IUserClaimsPrincipalFactory
and extend/override:
Microsoft.AspNet.Identity.UserManager
Microsoft.AspNet.Identity.SignInManager
I actually have setup a sample project to show how this can be done.
GitHub Link.
I tested on the beta8 and and with some small adaptatons (like Context => HttpContext) it worked too.
Shoe your solution above pushed me toward a direction that worked for me on MVC6-Beta3 Identityframework7-Beta3 EntityFramework7-Beta3:
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}
//
// Check for user existance in Identity Framework
//
ApplicationUser applicationUser = await _userManager.FindByNameAsync(model.eID);
if (applicationUser == null)
{
ModelState.AddModelError("", "Invalid username");
return View(model);
}
//
// Authenticate user credentials against Active Directory
//
bool isAuthenticated = await Authentication.ValidateCredentialsAsync(
_applicationSettings.Options.DomainController,
_applicationSettings.Options.DomainControllerSslPort,
model.eID, model.Password);
if (isAuthenticated == false)
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
//
// Signing the user step 1.
//
IdentityResult identityResult
= await _userManager.CreateAsync(
applicationUser,
cancellationToken: Context.RequestAborted);
if(identityResult != IdentityResult.Success)
{
foreach (IdentityError error in identityResult.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View(model);
}
//
// Signing the user step 2.
//
await _signInManager.SignInAsync(applicationUser,
isPersistent: false,
authenticationMethod:null,
cancellationToken: Context.RequestAborted);
return RedirectToLocal(returnUrl);
}
You could use ClaimTransformation, I just got it working this afternoon using the article and code below. I am accessing an application with Window Authentication and then adding claims based on permissions stored in a SQL Database. This is a good article that should help you.
https://github.com/aspnet/Security/issues/863
In summary ...
services.AddScoped<IClaimsTransformer, ClaimsTransformer>();
app.UseClaimsTransformation(async (context) =>
{
IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
return await transformer.TransformAsync(context);
});
public class ClaimsTransformer : IClaimsTransformer
{
private readonly DbContext _context;
public ClaimsTransformer(DbContext dbContext)
{
_context = dbContext;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
System.Security.Principal.WindowsIdentity windowsIdentity = null;
foreach (var i in context.Principal.Identities)
{
//windows token
if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
{
windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
}
}
if (windowsIdentity != null)
{
//find user in database by username
var username = windowsIdentity.Name.Remove(0, 6);
var appUser = _context.User.FirstOrDefault(m => m.Username == username);
if (appUser != null)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id)));
/*//add all claims from security profile
foreach (var p in appUser.Id)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
}*/
}
}
return await System.Threading.Tasks.Task.FromResult(context.Principal);
}
}
Do you know how to implement a custom System.Web.Security.MembershipProvider? You should be able to use this (override ValidateUser) in conjunction with System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials() to authenticate against active directory.
try:
var pc = new PrincipalContext(ContextType.Domain, "example.com", "DC=example,DC=com");
pc.ValidateCredentials(username, password);
I had to design a solution to this problem this way:
1. Any AD authenticated user will be able to access the application.
2. The roles and claims of the users are stored in the Identity database of the application.
3. An admin user will be able to assign roles to users (I have added this functionality to the app as well).
Read on if you want to see my complete solution. The link to the full source code is towards the end of this answer.
Basic design
1. User enters Active Directory credentials (Windows login credentials in this case).
2. The app checks if it's a valid login against AD.
2.1. If it's not valid, app returns the page with 'Invalid login attempt' error message.
2.2. If it's valid, go to next step.
3. Check if the user exists in the Identity database.
3.1. If Not, create this user in our Identity database.
3.2 If Yes, go to next step.
4. SignIn the user (using AD credentials). This is where we override UserManager.
Note: The user created in step 3.1 has no roles assigned.
An admin user (with valid AD username) is created during Db initialization. Adjust the Admin2UserName with your AD username if you want to be the admin user who will assign roles to newly added users. Don't even worry about the password, it can be anything because the actual authentication will happen through AD not through what's in Identity database.
Solution
Step 1:
Ensure that you've got Identity setup in your application. As an example, I'm taking a Blazor Server app here. If you don't have Identity setup, follow this guide from Microsoft learn.
Use my project to follow along the guide.
Step 2:
Add ADHelper static class to help with Active Directory login. In my example, it's at Areas/Identity/ADHelper.cs and has contents that look like this:
using System.DirectoryServices.AccountManagement;
namespace HMT.Web.Server.Areas.Identity
{
public static class ADHelper
{
public static bool ADLogin(string userName, string password)
{
using PrincipalContext principalContext = new(ContextType.Domain);
bool isValidLogin = principalContext.ValidateCredentials(userName.ToUpper(), password);
return isValidLogin;
}
}
}
Step 3:
Override CheckPasswordAsync method in UserManager so you can authenticate users against Active Directory. I have done this in Areas/Identity/ADUserManager.cs, the contents of which look like this:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
namespace HMT.Web.Server.Areas.Identity
{
public class ADUserManager<TUser> : UserManager<TUser> where TUser : IdentityUser
{
public ADUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators,
IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer,
errors, services, logger)
{
}
public override Task<bool> CheckPasswordAsync(TUser user, string password)
{
var adLoginResult = ADHelper.ADLogin(user.UserName, password);
return Task.FromResult(adLoginResult);
}
}
}
Step 4:
Register it in your Program.cs
builder.Services
.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})
.AddRoles<ApplicationRole>()
.AddUserManager<CustomUserManager<ApplicationUser>>() <----- THIS GUY
.AddEntityFrameworkStores<ApplicationDbContext>();
ApplicationUser, ApplicationRole and ApplicationDbContext look like this:
public class ApplicationUser : IdentityUser
{
}
public class ApplicationRole : IdentityRole
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Step 5:
Update OnPostAsync method in Areas/Identity/Pages/Account/Login.cshtml.cs to implement the authentication flow. The method looks like this:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
if (ModelState.IsValid)
{
// Step 1: Authenticate an user against AD
// If YES: Go to next step
// If NO: Terminate the process
var adLoginResult = ADHelper.ADLogin(Input.UserName, Input.Password);
if (!adLoginResult)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
// Step 2: Check if the user exists in our Identity Db
// If YES: Proceed to SignIn the user
// If NO: Either terminate the process OR create this user in our Identity Db and THEN proceed to SignIn the user
// I'm going with OR scenario this time
var user = await _userManager.FindByNameAsync(Input.UserName);
if (user == null)
{
var identityResult = await _userManager.CreateAsync(new ApplicationUser
{
UserName = Input.UserName,
}, Input.Password);
if (identityResult != IdentityResult.Success)
{
ModelState.AddModelError(string.Empty, "The user was authenticated against AD successfully, but failed to be inserted into Application's Identity database.");
foreach (IdentityError error in identityResult.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
}
// Step 3: SignIn the user using AD credentials
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
// If we got this far, something failed, redisplay form
return Page();
}
Basically, we're done here. 🙌
Step 6:
Now if an admin user wants to assign roles to newly added users, simply go to Manage Users page and assign appropriate roles.
Pretty easy, right? 😃
Step 7:
If you want to manage roles (add, edit, delete), simply go to manage/roles page.
Conclusion
This setup ensures that users are authenticated using Active Directory and are authorized using roles in the Identity database.
Complete source code
https://github.com/affableashish/blazor-server-auth/tree/feature/AddADAuthentication

MVC5 EF6 How to add confirmation screen with additional authentication before submitting data

Developing a new MVC5 project. I have my scaffolding in place for CRUD functionality but there is a requirement that when data is inserted or updated, an e-signature is required. Before data can be submitted to the database the user must be presented with a page asking them to enter their username and password again to confirm the data. If the username and password entered is valid and the username matches the currently signed in user, then the original data entered can be saved to its table (for example Member) and the e-signature information is saved to a separate table (ESignature). I'd appreciate any help on the best way to go about this - a view model combining Member and ESignature, or a reuse of the LoginViewModel from the Account controller to check the authentication, or an alternative approach? I need something that I can use across half a dozen controllers where e-signatures are required.
Alright maybe my approach is not the best but I will attempt.
My solution would be to create a CustomAttribute: AuthorizeAttribute and decorate all the actions which require Esignature. In your CustomAttribute implementation you will redirect to a controller action exactly similar to Login but with slight modification.
public class CustomAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var url = filterContext.HttpContext.Request.Url;
var query = url.Query;
if (query.Contains("g="))
{
var code = query.Split(new String[] { "g=" }, StringSplitOptions.None);
//You can create time sensistive token and validate it.
}
else
{
//Redirect User to a particular page
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Account" },
{ "action", "elogin" },
{ "redirectUrl", url.AbsolutePath}
}
);
}
}
}
Then decorate for example Index() method with it.
[CustomAuthorize]
public ActionResult Index()
{
return View();
}
At first when you hit the Index() method then inside OnAuthorization method of CustomAuthorizeAttribute the else loop gets executed and re-directs you to a elogin method inside AccountController. This method is similar to the Login HttpGet method. While specifying the RedirectToResult I am specifying the redirectUrl path of the current page so when you successfully validate a user inside the elogin method then with the help of redirectUrl we can come back.
[AllowAnonymous]
public ActionResult ELogin(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View("Login");
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ELogin(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
var url = String.Format("{0}/?g={1}", returnUrl, "HashCode");
return RedirectToLocal(url);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
The only difference in the HttpPost ELogin method is that before doing RedirectToLocal I append /g=HasCode. Note: Here you can append your own logic to create a time sensitive hash. When we get redirected to our home page then we can inspect inside our OnAuthorization Method if the url contains g=HashCode then don't redirect to Login Page.
This would be very basic idea on how you can approach to force users to re-sign in whenever they hit specific controllers. You will have to do additional security checks and be careful in what you are exposing via url.

How to automatically add users to roles

I am building a small MVC4 web site and am using SimpleMembershipProvider along with the SimpleRoleProvider.
I have seeded my database so that the [webpages_Roles] contains "Administrator" and "User"
(similar to this: link).
Everything with the Accounts and User profile model is as it was when the MVC4 project was created.
Now how do I make it so that when ever someone registers they are automatically placed in the "User" role?
Typically all your user registration logic will reside in your accounts controller (this is certainly the case if you use one of the provided templates for an Internet Application). Therefore you will need to add code to the registration method to add the user to the User role once their account is successfully created.
This is simpler and tidier than trying to watch for an event, even if one existed. You should find that in a well designed app you won't have your registration code in any more than a single place anyway. This also lets you handle OAuth scenarios, if you are using social network logins such as Facebook.
e.g. This is how you could use it with the out-of-the-box AccountsController in the Internet Application template (for your local site registration)
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
... various actions ...
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register() {
return View();
}
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model) {
if (ModelState.IsValid) {
// Attempt to register the user
try {
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
// ----------------- NEW LINES HERE
Roles.AddUserToRoles(model.UserName, new[] { "Users" });
// ----------------- END NEW LINES
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e) {
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
... more actions ...
}