Hi,
First of all, this is a great feature β thank you π
I created two tenants (with separate databases) using the same username (admin) for testing purposes, and the system allowed this.
However, during authentication, only the account associated with the first created tenant can be accessed successfully. Attempts to log in with the second tenant result in the error message: "Invalid username or password."
In the AbpUsers table (Host side), two users are created with the same username but different email addresses.
According to the documentation for Shared User Accounts, username and email uniqueness should be enforced globally. However, in this case, the system allows duplicate usernames across tenants.
After a user clicks on the invitation link and chooses a username, there does not appear to be any validation preventing duplicates.
Could you please verify whether this behavior is expected, a configuration issue, or a potential bug?
This is not urgent for us, as we are not in production yet. I was just testing this functionality and wanted to share the result i got.
For reference, I have added the following in the Domain module:
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = true;
options.UserSharingStrategy = TenantUserSharingStrategy.Shared;
});
Thanks.
5 Answer(s)
-
0
- With
TenantUserSharingStrategy.Shared, username/email must be globally unique (Host + all tenants). So having two different users with the sameUserName = adminis not expected in a correctly working Shared User Accounts setup. - The symptom you see during login (βonly the first tenantβs admin works; the other returns Invalid username or passwordβ) is consistent with this: the login flow (host-side) resolves a user by username/email, finds the first matching βadminβ, then password verification is performed for that user. If the password you typed belongs to the other βadminβ record, it fails and you get βInvalid username or passwordβ.
What to do (verified, definitive)
Do not seed/create βadminβ separately per tenant when using Shared strategy.
- In Shared mode, the recommended flow is:
- Create tenant
- Invite an existing global user (or let them register from invitation)
- Assign roles in that tenant
- If you created tenants in a way that auto-creates a tenant-local
adminuser (classic isolated behavior), you can end up with conflicting global identities.
- In Shared mode, the recommended flow is:
Fix your current data
- In the Host database, ensure there is only one
AbpUsersrecord for a givenUserName(and also uniqueness across emails). - Then associate that single global user with multiple tenants via the Shared User Accounts invitation/association mechanism (rather than having separate βadminβ users).
- In the Host database, ensure there is only one
Re-test using invitations
- Create a single global user (e.g.,
admin) once. - Invite that user into tenant A and tenant B.
- On login, ABP should authenticate once and then (if user belongs to multiple tenants) show a tenant selection step.
- Create a single global user (e.g.,
Conclusion
This behavior is not expected under Shared User Accounts; it indicates your tenants were created/seeded in a way that produced duplicate global identities (two different host users with the same username). The correct approach is one user globally + tenant associations, not βone admin per tenantβ.
- https://abp.io/docs/latest/modules/account/shared-user-accounts
- https://abp.io/community/articles/mf3bkg79
This response was generated by AI. If it didnβt solve your problem, feel free to post a new answer β our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - With
-
0
Hi,
You're right, this is a problem. The invite-accept path didn't run the global username/email uniqueness check in shared mode, so two different invitees could each claim
adminin their tenant. HostAbpUsersended up with duplicate rows and login can only resolve the first one, which is why the second tenant's admin can't sign in.Until the fix ships in 10.2.2 / 10.3.1 you can either give each tenant's admin a unique username (e.g.
admin-tenant1/admin-tenant2), or drop thisUserSharingManageroverride into your Domain module to reject the duplicate right now:[Dependency(ReplaceServices = true)] [ExposeServices(typeof(UserSharingManager))] public class MyUserSharingManager : UserSharingManager { public MyUserSharingManager( IOptions<AbpMultiTenancyOptions> multiTenancyOptions, IDataFilter dataFilter, ITenantStore tenantStore, IdentityProUserManager userManager, IIdentityUserRepository userRepository, IdentityRoleManager roleManager, IIdentityRoleRepository roleRepository, UserInvitationManager userInvitationManager, IAbpDistributedLock distributedLock) : base(multiTenancyOptions, dataFilter, tenantStore, userManager, userRepository, roleManager, roleRepository, userInvitationManager, distributedLock) { } public override async Task AcceptInviteAsync(Guid invitationId, string username, string password, string passwordHash) { if (IsEnabled()) { var invitation = await UserInvitationManager.GetAsync(invitationId); if (invitation.Status == UserInvitationStatus.Pending) { var hostUsersWithUserName = await GetUsersByUserNameFromHostAsync(username, includeLeaved: true); if (hostUsersWithUserName.Any(x => !string.Equals(x.Email, invitation.InviteeEmail, StringComparison.OrdinalIgnoreCase))) { throw new BusinessException(IdentityProErrorCodes.InviteUserUserNameAlreadyExists).WithData("UserName", username); } var hostUsersWithEmail = await GetUsersByEmailFromHostAsync(invitation.InviteeEmail, includeLeaved: true); if (hostUsersWithEmail.Any(x => !string.Equals(x.UserName, username, StringComparison.OrdinalIgnoreCase))) { throw new BusinessException(IdentityProErrorCodes.InviteUserEmailAlreadyExists).WithData("Email", invitation.InviteeEmail); } } } await base.AcceptInviteAsync(invitationId, username, password, passwordHash); } }If you already have duplicate rows in host
AbpUsersfrom testing, delete one or rename itsUserNameso login can disambiguate.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Thank you for the explanation.
I came across this while testing and wasnβt sure of the best place to report it, so i decided to share it here. As mentioned earlier, we are still in the pre-production phase, so weβre happy to wait for the fix in the upcoming release.
Thanks again, glad i could help π
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Thanks for catching this during testing β really appreciate it, that kind of feedback before production saves us a lot of pain. The fix will land in the next release, so you can grab it from there.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)