43

I want to save user without email, like this:

var user = new ApplicationUser { UserName = model.Name };
var result = await UserManager.CreateAsync(user);

But I got error "Email cannot be null or empty". Is there any solution for this? Or it is impossible?

1
  • I have the exact same problem, though Email is provided result always returns null but successful. Is there a solution to this? Commented Jan 19, 2020 at 11:14

8 Answers 8

91

I know this is old, but I disagree with the accepted answer, since the question is tagged as asp.net-identity-2. For the benefit of future readers, ASP.NET Identity 2.0 has a very simple solution to this problem:

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    ...snip...
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            // This disables the validation check on email addresses
            RequireUniqueEmail = false
        };
        ...snip...
    }
}

In UserValidator<TUser>, the Task<IdentityResult> ValidateAsync(T item) implementation checks this flag and determines if it should run email validation:

if (this.RequireUniqueEmail)
{
    await this.ValidateEmail(item, list);
}

Since you want to save users without an email address, this is how you should do it.

CAUTION: This should only be used when email addresses are not collected. If you want to collect and validate email addresses, but make them optional during registration, you should use a custom IIdentityValidator.

Sign up to request clarification or add additional context in comments.

1 Comment

Plus one for your avatar. ...and the correct answer to the question, of course! : )
34

ASP Identity 2.2 can be set in App_Start\IdentityConfig.cs

    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
    {
        AllowOnlyAlphanumericUserNames = false,
        RequireUniqueEmail = false
    };

Comments

11

In startup file you can disable RequireUniqueEmail by access IdentityOptions configuration Also here you can change complexity of password , Lockout,...

  services.Configure<IdentityOptions>(x =>
        {
            x.User.RequireUniqueEmail = false;

            //x.Password.RequireUppercase = false; => other condition for identity

        });

you should use Microsoft.AspnetCore.Identity, address of doc here

Thank you @PBO and you can also change identity options like below

 services.AddDefaultIdentity<IdentityUser>(
            options=> { 
                options.SignIn.RequireConfirmedAccount = false;
                options.User.RequireUniqueEmail = false;
                options.Password.RequireUppercase = false;
            })
            .AddEntityFrameworkStores<BookShopDbContext>();

1 Comment

you can access the same functionality in services.AddDefaultIdentity
9

Identity depends on email as a way to reset user password.

However, ignoring email is not simple, but possible. You'll need to implement Microsoft.AspNet.Identity.IIdentityValidator interface that ignores the lack of email:

namespace Microsoft.AspNet.Identity
{
  /// <summary>
  /// Used to validate an item
  /// 
  /// </summary>
  /// <typeparam name="T"/>
  public interface IIdentityValidator<in T>
  {
    /// <summary>
    /// Validate the item
    /// 
    /// </summary>
    /// <param name="item"/>
    /// <returns/>
    Task<IdentityResult> ValidateAsync(T item);
  }
}

And then in ApplicationUserManager assign your own implementation to property UserValidator.

If you really need this, you can get the original source code for UserValidator by decompiling Microsoft.AspNet.Identity.UserValidator class and peeking into the existing source code and removing cheecking for email.

However, I'm not sure how the rest of the framework will react on lack of email on user. Probably you'll get exceptions in other operations.

Comments

7

If anyone is still struggling with this...

Set options.User.RequireUniqueEmail=true, create a custom user validator and register it before registering Identity. This will make the email optional but still require it to be unique if entered.

in ConfigureServices(IServiceCollection services):

services.AddTransient<IUserValidator<ApplicationUser>, OptionalEmailUserValidator<MonotypeIdentityUser>>();
services.AddIdentity(...);

The custom user validator class:

public class OptionalEmailUserValidator<TUser> : UserValidator<TUser> where TUser : class
{
    public OptionalEmailUserValidator(IdentityErrorDescriber errors = null) : base(errors)
    {
    }

    public override async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
    {
        var result = await base.ValidateAsync(manager, user);
        
        if(!result.Succeeded && String.IsNullOrWhiteSpace(await manager.GetEmailAsync(user)))
        {
            var errors = result.Errors.Where(e => e.Code != "InvalidEmail");

            result = errors.Count() > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
        }           

        return result;
    }
}

1 Comment

Great answer, you can also just do a services.AddIdentity().AddUserValidator<OptionalEmailUserValidator<ApplicationUser>>() to inject in a one liner
3

You can create a user account without Email by editing the identity file in the App_Start folder and changing the RequireUniqueEmail value to False to avoid the Email validator`

  manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = false,
        };

`

Comments

0

I create custom EmailValidator with same code exist on Microsoft.AspNetCore.Identity.UserValidator::ValidateEmail with minor change on empty email check:

public class EmailValidator<TUser> : IUserValidator<TUser>
    where TUser : IdentityUser
{
    //! make sure email is valid, and unique
    private static async Task<List<IdentityError>?> ValidateEmail(UserManager<TUser> manager, TUser user, List<IdentityError>? errors)
    {
        var Describer = manager.ErrorDescriber;
        var email = await manager.GetEmailAsync(user).ConfigureAwait(false);
        if (string.IsNullOrEmpty(email)/*string.IsNullOrWhiteSpace(email)*/)
        {
            //errors ??= new List<IdentityError>();
            //errors.Add(Describer.InvalidEmail(email));
            return errors;
        }
        if (!new EmailAddressAttribute().IsValid(email))
        {
            errors ??= new List<IdentityError>();
            errors.Add(Describer.InvalidEmail(email));
            return errors;
        }
        var owner = await manager.FindByEmailAsync(email).ConfigureAwait(false);
        if (owner != null &&
            !string.Equals(await manager.GetUserIdAsync(owner).ConfigureAwait(false), await manager.GetUserIdAsync(user).ConfigureAwait(false)))
        {
            errors ??= new List<IdentityError>();
            errors.Add(Describer.DuplicateEmail(email));
        }
        return errors;
    }

    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
    {
        if (ValidateEmail(manager, user, []).GetAwaiter().GetResult() is List<IdentityError> emailErrors)
        {
            return Task.FromResult(IdentityResult.Failed([.. emailErrors]));
        }

        return Task.FromResult(IdentityResult.Success);
    }
}

then add validator to identity on program.cs:

services.AddIdentity<AppUser, AppRole>(
    options =>
    {
        //! must be false, this is going to done by 'EmailValidator<AppUser>' on below
        options.User.RequireUniqueEmail = false;
    })
    .AddUserValidator<EmailValidator<AppUser>>()
    ;

Comments

-1

Actually there is a better way. Alter the validation for just when you need to ignore it and then restore it back when done. (Don't use async in this case because another thread might be need to update(?))

// first create/update the identity!!! 
user.PasswordHash = UserManager.PasswordHasher.HashPassword(model.Password); //hash and update directly

// alter
UserManager.UserValidator = new UserValidator<StoreUser>(UserManager)
{
    AllowOnlyAlphanumericUserNames = false,
    RequireUniqueEmail = false
};

var result = UserManager.Update(user);

// restore... 
UserManager.UserValidator = new UserValidator<StoreUser>(UserManager)
{
    AllowOnlyAlphanumericUserNames = false,
    RequireUniqueEmail = true
};

3 Comments

this is a really, really bad idea. What happens if two people update their user at the same time? Race condition that's what. Why wouldn't you just set this once on startup like everyone else suggests?
What if I want to store e-mail and its associated fields now found in AspNetUsers in a separate table? Same with phone fields?
@muybn, I managed to seperate phone number, email address into a AccountContact table. Its a lot of work and unless you have specific reason for doing it, its a waste of time. I got a lot of errors that required a lot of time looking through the source code to understand what is going on. Initially I removed username too but restored it just to get my project going (I fill it with either phone number or email depending on what the customer provided).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.