Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unique UserName & Email per tenant

I'm coding a multi-tenant app using ASP.NET Core 2.1.

I would like to override the default user-creation-related validation mechanism.

Currently I cannot create several users with the same UserName.

My ApplicationUser model has a field called TenantID.

What I'm trying to achieve: UserName & EmailAddress must be unique per tenant.

I've been googling a solution, but haven't found much info for asp.net core on this one.

Most of the results would only cover Entity Framework aspects, as if it's just a matter of overriding OnModelCreating(...) method. Some are related to NON-core edition of ASP.NET Identity.

I wonder if I should keep investigating OnModelCreating approach?

Or perhaps, there's something else that needs to be overridden around Identity?

like image 721
Alex Herman Avatar asked Jul 31 '18 23:07

Alex Herman


People also ask

How do I create a unique username?

What to Know. Incorporate your favorite things, such as food, celebrities, or career aspirations. Use an online screen name generator such as SpinXO. Build an unlikely username by substituting symbols and letters, like 3 for E and $ for 5.

What is a perfect username?

To pick a good social media username that's both unique and catchy, first identify your account's purpose. Full names are great for a personal profile, especially for curating a professional self-image. You could even add words such as “real”, “official”, or an extra initial (such as the writer @StephenRCovey).

What are aesthetic usernames?

Aesthetic usernames should be breezy, cool, soft, funny, and cute. Coming up with a unique aesthetic username for all platforms can be difficult. So we have done the work for you. In this article, we have included a guide to help you come up with the best aesthetic usernames.


1 Answers

First of all, you need to disable the Identity's built-in validation mechanism:

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    // disable the built-in validation
    options.User.RequireUniqueEmail = false;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

Then, assuming you are registering users with the ASP.NET Core with Identity template, you could just do:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;

    if (ModelState.IsValid)
    {
        return View(model); 
    }

    // check for duplicates
    bool combinationExists = await _context.Users
        .AnyAsync(x => x.UserName == model.UserName 
                 && x.Email == model.Email
                 && x.TenantId == model.TenantId);

    if (combinationExists)
    {
        return View(model);
    }

    // create the user otherwise
}

If you don't want to do that kind of checking in the controller and would rather keep the Identity flow, you can create your own IUserValidator<ApplicationUser> pretty simply:

public class MultiTenantValidator : IUserValidator<ApplicationUser>
{
    public async Task<IdentityResult> ValidateAsync(UserManager<ApplicationUser> manager, ApplicationUser user)
    {
        bool combinationExists = await manager.Users
            .AnyAsync(x => x.UserName == user.UserName 
                        && x.Email == user.Email
                        && x.TenantId == user.TenantId);

        if (combinationExists)
        {
            return IdentityResult.Failed(new IdentityResult { Description = "The specified username and email are already registered in the given tentant" });
        }

        // here the default validator validates the username for valid characters,
        // let's just say all is good for now
        return IdentityResult.Success;
    }
}

And you would then tell Identity to use your validator:

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddUserValidator<MultiTenantValidator>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

With this, when you call UserManager.CreateAsync, the validation will take place before creating the user.

like image 171
Camilo Terevinto Avatar answered Nov 05 '22 05:11

Camilo Terevinto