Multi-tenant same username different tenants

I have 2 tenants, I would like to create an admin user for each tenant with Username of 'admin'

When I try to create an account with username 'admin' for the second tenant, it says Username already taken.

This should be able to be done because of the TenantId column in the 'AspNetUsers' table, correct?

Hi @Chad,

This is not supported. You can only have same role names across tenants.

Will it be supported in the future? It's a pretty big limitation in my opinion.

This cannot be supported. Each user is associated with a single tenant and this is how a deployed application will know which tenant to use when you login with a user.

UPDATE: Actually we might found a way to enable this, I'll post more info later this week.

Here is how to enable this for your app/database:


Thank you.

By deleting the database unique index it now allows the same username within the same tenant which is an issue.

What is the purpose of the Hosts column in the AspNetTenants table?

I did read through the documentation, I see how you can setup the hosts, but it also says the tenant is determined by the username alone? ("Now when login with a user in development the application will use user tenant as active tenant.")

I have the hosts the same for both of my tenants, so now with the same username allowed, I do not think the tenant can reliably be determined?

A design idea I have would be:

--code such that the hosts are forced to be unique for each tenant (both through application code/validation and also a database constraint)

--code such that the usernames must be unique for each tenant.

--code such that the combination of the username and the host are used determine the tenant, not just the username alone.

I am not certain, but I think it is how systems I have seen work. They use subdomain along with username to determine the tenant.

You can add validation in the same method similar to the login - check the code for reference.

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] ApplicationUser data)
        {
            var email = data.Email;
            var password = data.Password;

            var user = new ApplicationUser { UserName = email, Email = email, EmailConfirmed = true };

            user.TenantId = data.TenantId;

            userManager.UserValidators.Clear();

            if (context.Users.Any(u => u.TenantId == user.TenantId && u.UserName == user.Name))
            {
                ModelState.AddModelError("", "User with the same name already exist");
                return BadRequest(ModelState);
            }

            IdentityResult result = await userManager.CreateAsync(user, password);

            if (result.Succeeded && data.Roles != null)
            {
                result = await userManager.AddToRolesAsync(user, data.Roles.Select(r => r.Name));
            }

            if (result.Succeeded)
            {
                OnUserCreated(user);

                return Created($"odata/Identity/Users('{user.Id}')", user);
            }
            else
            {
                return IdentityError(result);
            }
        }

No. The tenant is retrieved from the host. Check the code to see how the current host is used to filter or get the current tenant.

Indeed currently there is no validation for having same hosts in different tenants however again you can extend your app in vary simple way to add such validation.

Here is also how to extend the MultiTenancyUserStore to get the right user per tenant:

        public override async Task<ApplicationUser> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default)
        {
            if (normalizedName.ToLower() == "tenantsadmin")
            {
                return await base.FindByNameAsync(normalizedName, cancellationToken);
            }

            var tenant = GetTenant();
            ApplicationUser role = null;

            if (tenant != null)
            {
                role = await Context.Set<ApplicationUser>().SingleOrDefaultAsync(r => r.NormalizedUserName == normalizedName && r.TenantId == tenant.Id, cancellationToken);
            }

            return role;
        }
1 Like

Thank you.

Only comment is I would think you would to add the validation to the main code base so others can benefit. So others don't run into the same problems or have the same questions.

It’s already part of our templates and will be released tomorrow in our next update.

1 Like

this isn't working for me. nuget 4.8.4 RBS 1.8.0

with the validation off and the sql index deleted I can create same username for both tenants, I used the same password as well.

I have IIS set up, site is working at both URLs tenant1.local.com tenant2.local.com

I can login to tenant1.local.com
I can NOT login to tenant2.local.com, it says "Invalid user or password" (I deleted and recreated the account 3 times just to ensure there was no mistakes with the password)

SQL table for verification:

I also have two URLs on Azure, same result.

Try to debug your application locally when you attempt to login with this user.

It's working great now after the RBS 1.8.1 installed and re ran the security scaffolding

struggling getting the tenant name.

I should be able to get to it from anywhere?

I'm generally getting Security.Tenant is only not null when I'm logged in as 'tenantsadmin'.

Should it be available regardless of which account is logged in?

Have you tried to step thru GetTenant() in debug mode?

Thank you.

korchev pointed out it's in the documentation

I was close, but I was testing in debug with a user with username admin which is a special case in SecurityService