AspNet Identity 2.0 Email and UserName duplication - asp.net-mvc

My current Asp.Net MVC 5 project mandates Email address for UserName. Now I want to upgrade ASPNet Identity v1.0 to v2.0 to leverage all its new features (see here).
However, ASPNet Identity v2.0 adds Email as a separate column to the Users table and adds a corresponding property to the IdentityUser class.
I don't want to duplicate UserName into this new Email column. How can I map this Email Property of IdentityUser to use existing UserName column & property? Is it possible to ignore this Email property and skip adding the column in the Users table? Has anybody tried this?
Please share.
Update
This is the identity 2.0 limitation. We cannot ignore Email property or leave it Null. Some of the Identity functionality will not work. :(

You can try one of these:
Try to ignore it by either overriding Email property in your User class and unmapping it or using fluent API.
public class ApplicationUser : IdentityUser
{
// ....
[NotMapped]
public override string Email { get; set; }
}
or
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>().Ignore(u => u.Email);
}
When you register your user just make sure that you populate Email with your UserName
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
// ...
}
}
Of course, you can always ignore Email column if you're not going to use it, since it allows NULLs, it'll just be sitting in your AspNetUsers table with bunch of NULLs, not the best approach but remember that by ignoring it you might lose new features that ASP.NET Identity 2 might offer that you might want to use.
NOTE However I'm not sure if option number 1 will work on Email property since it's probably used all over the place in new Identity code. Worth a try though. I know that's how you can get rid of other columns if you don't need them. I personally happen to use new Email property/column so I haven't tried it.
Not sure if it helps you, but thought I'd share it just in case.

I have the same problem, and the way that I resolved it was that the email address was the same as the username when creating a user:
var newUser = new ApplicationUser()
{
UserName = email,
Email = email,
};
However, if you try to create an account with a duplicate username, you will get 2 validation errors, one for the username field, and one for the email address.
To get around that, allow email addresses to not be unique (they will still be unique though as your usernames are unique) by editing the identityconfig.cs file:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};

Related

Ensure unique Username / Emails

I am currently using Asp MVC / Identity 2.0 to build a membership site.
I have made some changes that now allow a user todo the following :
Create separate username / email values on account creation
Login using either username or email through a single text field
What I am concerned about is should user Alice register an account as follows :
Username - Alice
Email - alice#example.com
User Bob could create an account as follows :
Username - alice#example.com
Email - evilbob#l33th4x0rs.com
I'd like to ensure, that should a user create an account, their username is unique in both the UserName and EmailAddress columns.
I have currently achieved within my RegisterViewModel as follows
[RegularExpression(#"^([a-zA-Z0-9 \.\&\'\-]+)$", ErrorMessage = "{0} must be alpha numeric")]
[Display(Name = "Username")]
public string UserName { get; set; }
As such it is not possible to enter an email into the username field - is this the best way to do this though?
I'm concerned that later down the line, I may want to allow some special characters in the username field.
Forgive me if this is a n00b question, I'm still very much new to this.
You cannot ensure uniqueness of your records using the Data annotations you have in your view model properties. Those are for helping validations & display purposes primarily.
What you should be doing is checking the userName value againist both UserName and Email field of your User table and allow/deny user to continue his action.
To check the userName against both email and userName fields , you might try something like this.
var userNameToCheck="alice#ss.com";
var exists=db.Users.Any(x=>x.UserName==userNameToCheck||x.Email==userNameToCheck);
if(!exists)
{
// New record. Let's continue saving it.
}
But a better solution is to use email as your username for the app(Only one column in db table) and keep a unique constraint on that column(Email) to prevent accidental duplicate entry even if your code failed to stop the duplicate entry insertion.
You should always have validation in your code and db (constraints) to be on the safer side.

ASP.NET MVC - How does WebSecurity work?

I have an ASP.NET MVC 4 project which I have successfully connected to a MySQL database. I have done this by adding a ADO.NET/EntityFramework class which created a Model.edmx object.
Within the database, I have created a table called user which holds what you should expect in a User table such as Email, UserName, Password, FirstName. etc etc.
I have created some dummy records and added the following code to the Login method within the AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var database = new Database();
user user = database.SelectByUserName(model.UserName).FirstOrDefault<user>();
var hash = Utilities.HashPassword(model.Password, user.Salt);
if (hash == user.Password && WebSecurity.Login(user.UserName, user.Password))
{
//Correct Login Details!
RedirectToAction("About", "Home");
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
For some reason, the WebSecurity.Login method returns false and the user isn't redirected to the Home page.
Why is it returning false? What am I missing and how would the WebSecurity.Login even know what credentials are required i.e. How does it even know that it should look inside the user table which I created?
WebSecurity doesn't default to looking at your database, it will actually make it's own tables using the DefaultConnection that is defined in Web.Config. To work around this you need to add a new connection string Web.Config and then during app initialization force WebSecurity to look at that connection.
The easiest way to accomplish this, assuming you have a MySQL specific connection string in your Web.Config named "AccountConnection" is by adding the following to your Application_Start()
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
And then you'll need the following fields and function:
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
// Overload is: Web.Config Connection string by name, user table name, user id column name, user name column name, auto create missing tables
WebSecurity.InitializeDatabaseConnection("AccountConnection", "UserProfile", "UserId", "Email", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The Membership database could not be initialized.", ex);
}
}
}
Whether you can make WebSecurity work with MySQL I have no idea, though I believe I've read some place that it is supported.
Note: the UserContext should have been auto generated when you installed WebSecurity into your solution. If not it's a CodeFirst model that you can easily add.
There are one of two reasons your code will not work. Understand that WebSecurity and SimpleMembershipProvider (assuming you are using it) uses PBKDF2 algorithm to populate the password field when you call WebSecurity.CreateUserAndAccount or WebSecurity.CreateAccount.
So Either:
You did not use one of these two methods to create the user, in which case WebSecurity.Login will almost always fail (99.99%).
or
You did use one of the methods above and the code in Utilities.HashPassword() (which seems redundant since the Create Account methods listed above hash passwords anyway...) does not hash the password Exactly the same way WebSecurity does so hash == user.Password will always fail.

ASP.NET (OWIN) Identity: How to get UserID from a Web API controller?

(Using VS2013 RTW, ASP.NET MVC5)
I've seen lots of documentation on how to add properties to the ApplicationUser class (and table) when using ASP.NET identity. But I haven't seen any documentation on how to have a separate table with content that maps to the ApplicationUser table via a foreign key.
I've successfully derived my own Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext, and now my own "UserPreferences" table coexists in harmony with the various "AspNet*" tables in my SQL Server database. But I'm not clear on the best way to get the current user's ID so as to write a new row to the "UserPreferences" table. Note that the project is ASP.NET MVC, but I've added (and am working inside of) a Web API controller.
I have a working solution, which is:
var uman = new Microsoft.AspNet.Identity.UserManager<Microsoft.AspNet.Identity.EntityFramework.IdentityUser>(new Microsoft.AspNet.Identity.EntityFramework.UserStore<Microsoft.AspNet.Identity.EntityFramework.IdentityUser>(new App1.Data.App1DbContext()));
var uident = User.Identity;
var userobject = uman.FindByNameAsync(uident.Name);
var userid = userobject.Result.Id;
// (Perform new row creation including the userid we just looked up
Consider that the AspNetUsers table (as defined by the Identity framework) consists of these fields:
-id (PK, nvarchar(128) - seems to contain a GUID, not sure why not an autoincrement integer, but I assume there are reasons for this)
-Username (nvarchar(max))
-PasswordHash (nvarchar(max))
-SecurityStamp (nvarchar(max))
I think that the id field (not the username) is the correct field to reference in this table. (I was thinking that a user may change his/her username, and someone else could then assume the other user's username, which is why both fields are there likely.) So then, I need to get the current user's ID for storage in the "UserPreferences" table, which is what the above code does. But it seems inefficient to have to do this lookup.
An important point is that in the context of a System.Web.Mvc.Controller, I can do:
User.Identity.GetUserId()
(Runtime type of User.Identity: System.Security.Principal.GenericIdentity)
But in the context of a System.Web.Http.ApiController (Web API), I cannot because that method does not exist (runtime type of User.Identity: System.Security.Claims.ClaimsIdentity) which is why I must rely instead on:
User.Identity.Name
and do the extra lookup to convert Name to ID. Does anyone have any suggestions for how this can be improved? Am I approaching the task of writing user data to separate tables in the correct way?
You should be able to get user id on both MVC controller and web api controller by same extension method in identity 1.0 RTW package.
Here is the extensions from identity package:
namespace Microsoft.AspNet.Identity
{
public static class IdentityExtensions
{
public static string FindFirstValue(this ClaimsIdentity identity, string claimType);
public static string GetUserId(this IIdentity identity);
public static string GetUserName(this IIdentity identity);
}
}
The IIdentity is the base interface for all identity types. You may need to add "using Microsoft.AspNet.Identity" in order to see the extension method.
BTW: regarding adding a foreign table for user, why not using ApplicationUser and add navigation property to UserPreference to let EF to handle their relationship? That will be easier.
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, UserID));
ClaimTypes.NameIdentifier is the claim for the function User.Identity.GetUserId()
I'm using claim base approach:
private ApplicationUser GetCurrentUser(ApplicationDbContext context)
{
var identity = User.Identity as ClaimsIdentity;
Claim identityClaim = identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
return context.Users.FirstOrDefault(u => u.Id == identityClaim.Value);
}
Short description for the best answer:
Install-Package Microsoft.AspNet.Identity.Core -Version 2.2.1
var userId = User.Identity.GetUserId();
Tried #Mastenka answer and it gave me nothing. I checked ClaimsIdentity and there were
claims type "UserName", so as a result, I get username by using "UserName" as ClaimsType.
Hope someone will give more info about it. It looks strange that "ClaimTypes.NameIdentifier" had no effect. (ApiController, ASP MVC API)
var userName = ((ClaimsIdentity)RequestContext.Principal.Identity).Claims.FirstOrDefault(cl => cl.Type == "UserName")?.Value;

Remote attribute in asp.net mvc – in some situations restricts our model

I’ve got an unexpected situation when using Remote Attribute in ASP.NET MVC3.
The model type I used:
using System;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace dTweets.Models
{
// at first time, user should create his account with unique username
// as in twitter.com, user do
public class UserMetadata
{
[HiddenInput]
internal int Identity { get; set; }
[Remote("IsUserExist", "Account")] // at any HttpPost, username should
// be unique – not appropriate if
// updating/editing this model later
[Required(ErrorMessage = "username should be unique")]
public string UserName { get; set; } // user cannot change it, later
[DataType(DataType.Password)]
public string Password { get; set; } // user can also change password, later
[DataType(DataType.MultilineText)]
public string About { get; set; } // Optional field – user can edit it later
}
[MetadataType(typeof(UserMetadata))]
[Bind(Include="UserName, Password, About")]
public partial class User
{
}
}
Remote attribute validates user unique name at account creation time. But when later user wants to update/change his account, Remote attribute did not allow to update model if keeping user unique name the same one.
This is not appropriate result because rarely user changes their unique user name. They just change other fields like About field or password etc.
[Note: at account creation time, I want to check user unique name so I used Remote attribute here, but at later time when updating user account I no longer need Remote attribute]
I must remove Remote attribute for updating this model later.
I want to update/change this model without changing user unique name (remote attribute is applied to this unique name).
one way to do this is to send ID value of this record in AdditionalFields named parameter like
[Remote("IsUserExist", "Account",AdditionalFields = "Identity")]
and then you can check for uniqueness across all rows except the ones that belong to current user. and don't forget to change signature of IsUserEsists action result to receive Identity like
public ActionResutl IsUserExists(string UserName, int Identity)
{
}
Can't you just change server side validation method to something like:
public ActionResult IsUserExists(string userName)
{
if (!UserService.UserNameExists(userName) || (CurrentUser.UserName == userName))
{
return "Yeah. Is it valid.";
}
}
You have current user, because he is logged in. As long as user can only edit his data, this will work.
This is one place where buddy metadata falls short.
Edit/Add scenarios require their own view models. One size fits all scenario validation attributes only work in very trivial business CRUD apps. Add and Edit actions happen in totally different contexts and are only transiently related. This concept is very similar to the DDD bounded context idea.

ASP.NET MVC validation of uniqueness

Rails has a very convenient uniqueness validation.
ASP.NET MVC doesn't.
I need to make sure that the e-mail address a user has entered hasn't been registered by anyone yet.
I can see only one way of doing this kind of validation: create a new data context object in the UniqueAttribute class.
But I'm afraid that wasting memory on a new data context object just for one validation is dangerous.
Am I wrong? Is there a better way to do that?
Update
This is what I got so far
public class UniqueEmailAttribute : ValidationAttribute {
public override bool IsValid(object value) {
DataContext db = new DataContext();
var userWithTheSameEmail = db.Users.SingleOrDefault(
u => u.Email == (string)value);
return userWithTheSameEmail == null;
}
}
// Usage
[UniqueEmail(ErrorMessage="This e-mail is already registered")]
public string Email { get; set; }
There are two problems.
It would be good to have just one UniqueAttribute class, not separate classes for e-mails, usernames etc. How can I do that?
Creating a new data context every time you need to validate a single attribute.
SOLUTION
So in the end I created a unique constraint on the table and now I just have to intercept SqlException in Users repository. Works great and is probably more efficient than searching for the same node in the whole table. Thanks!
Mvc 3 Relaease candidate has new New Validation Attributes as a remotevalidation -where you can register a method for validation on clientside(jquery).
see below example-
RemoteAttribute
The new RemoteAttribute validation attribute takes advantage of the jQuery Validation plug-in's remote validator, which enables client-side validation to call a method on the server that performs the actual validation logic.
In the following example, the UserName property has the RemoteAttribute applied. When editing this property in an Edit view, client validation will call an action named UserNameAvailable on the UsersController class in order to validate this field.
public class User {
[Remote("UserNameAvailable", "Users")]
public string UserName { get; set; }
}
The following example shows the corresponding controller.
public class UsersController {
public bool UserNameAvailable(string username) {
return !MyRepository.UserNameExists(username);
}
}
Mvc 3
UPDATE
public bool UserNameAvailable(string Propertyname)
{
if (Request.QueryString[0]= "UserName")
{
//validate username
}
elseif (Request.QueryString[0]= "Email")
{
//Validate Email
}
}
ASP.Net does have a feature that can automatically check the uniqueness of a user's email address when a user registers. It is the ASP.Net Membership service and you can use it to do what you want even if you don't use all of the features of it.
If you are not using the full Membership feature in your MVC application, then all you need to do is use
Membership.FindUsersByEmail(emailYouAreLookingFor);
If any values come back, you know that the address is not unique. If you ARE using the Membership service to create users, then the Membership service will check AUTOMATICALLY and return a code to you if the user's email address is not unique.
The Membership service sits in the System.Web.Security area so you would need a
using System.Web.Security;
reference in your controller.
Here is an example
MembershipCreateStatus createStatus = MembershipService.CreateUser(UserName, Password, Email);
if (createStatus == MembershipCreateStatus.DuplicateEmail)
{
//do something here
}
else
{
//do something here
}
I hope this helps!
The right way to make a generic remote unique validator in MVC can be found in this MVC forum. by counsellorben. It's based on my MVC unique remote validator article http://msdn.microsoft.com/en-us/library/gg508808(VS.98).aspx
A foolproof way of doing this is to create a validation attribute that would query the database for the email address. It would certainly add latency.
An alternative would be to create a unique constraint on the table and intercept SqlException.

Resources