Hi,
I'm trying to use feature shared user account "Inviting an Admin After Tenant Creation" like this guide https://github.com/abpframework/abp/blob/dev/docs/en/modules/account/shared-user-accounts.md
But unfortunately this use email to send invitation, but in my company's network, It doesn't allow to call to smtp or any email server. So how to by pass this in admin UI to accept this invitation or even what tables to update/insert to be able by pass this email flow and simulate user already click accept through email ?
I have tried to duplicated admin user from host and change TenantId of the tenant I use to test but when try to swich tenant in Admin UI, I check log of auth-server I got this
and then I tried to add grant_type SwitchTenant to "Angular" OpenIddict App

Thanks
7 Answer(s)
-
0
Hi,
A couple of things to clear up first:
- The "copy host admin and change
TenantId" trick won't work — the tenant admin lives in the tenant DB (Isolated mode) and just changingTenantIdon a host user breaks the multi-tenant integrity. SwitchTenantgrant only works whenAbpMultiTenancyOptions.UserSharingStrategy == Shared. In Isolated mode the grant handler explicitly returnsunsupported_grant_type, so adding it on the Angular OpenIddict client has no effect — please removeSwitchTenantfrom the Angular app'sExtensionGrantTypes.
For your "no SMTP allowed" scenario, the cleanest workaround is to replace the OOTB
IIdentityEmailSenderso the existing "Invite admin" button never calls SMTP. Instead, the accept-link is written to the log (or your own table / webhook / Redis — whatever your ops team can read). The host admin grabs the link from there and forwards it to the invitee through any internal channel (Slack/Teams/internal portal). The invitee opens the link, the OOTBAccount/InviteUserpage loads, they pick a username/password and accept.Zero UI change, zero new endpoints, no client-proxy regeneration in layered solutions. The OOTB "Invite admin" button keeps working unchanged in MVC, Blazor, and Angular.
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IIdentityEmailSender))] public class NoSmtpIdentityEmailSender : IdentityEmailSender { public NoSmtpIdentityEmailSender( IEmailSender emailSender, ITemplateRenderer templateRenderer, IStringLocalizer<IdentityResource> stringLocalizer, IAppUrlProvider appUrlProvider, UserSharingManager userSharingManager, UserInvitationManager userInvitationManager) : base(emailSender, templateRenderer, stringLocalizer, appUrlProvider, userSharingManager, userInvitationManager) { } public override async Task SendInviteEmailAsync(UserInvitation invitation, bool requireRegister, string appName) { var url = await AppUrlProvider.GetInviteUserUrlAsync(appName); var token = await UserInvitationManager.GenerateTokenFromInvitationIdAsync(invitation.Id); var link = $"{url}?token={UrlEncoder.Default.Encode(token)}"; Logger.LogWarning( "[Invite] Tenant={tenantId} Email={email} RequireRegister={requireRegister} Link={link}", invitation.InviterTenantId, invitation.InviteeEmail, requireRegister, link); // Or persist to your own table / push to a webhook / write to Redis - // whatever your ops team can read from to retrieve the link. } }Drop this class into your Application module. Each invite produces a log line like:
[Invite] Tenant=... Email=admin@mycorp.com RequireRegister=True Link=https://your-app/Account/InviteUser?token=CfDJ8...Just copy the
Link=...value and forward it to the invitee. Done — no SMTP, noSwitchTenant, no host-user copying.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - The "copy host admin and change
-
0
thanks for your guide, after implement
NoSmtpIdentityEmailSenderNow I can get the invite link to accept this.But the same thing I got related to
SwitchTenantin code I have also set all services with
Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = true; options.UserSharingStrategy = TenantUserSharingStrategy.Shared; });Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Glad the
NoSmtpIdentityEmailSenderpart is working.The
SwitchTenanterror is unrelated toUserSharingStrategy = Shared— that switch is correct and required, but the actual blocker is that the Angular OpenIddict application doesn't have thegt:SwitchTenantpermission in the database.You probably added
SwitchTenantto ExtensionGrantTypes in the OpenIddict admin UI, but the next DbMigrator run wipes it out. Look atOpenIddictDataSeeder.csin yourDbMigratorproject —CreateApplicationAsyncdoes this:if (!HasSameScopes(client, application)) { client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString())); await _applicationManager.UpdateAsync(client.ToModel()); }HasSameScopesactually compares the fullPermissionsJSON, so any permission you add manually in the UI (likegt:SwitchTenant) makes the comparison fail and the seeder overwrites the whole permission list with what's defined in code.Fix: add
"SwitchTenant"to the Angular client'sgrantTypeslist inOpenIddictDataSeeder.cs, then re-runDbMigrator://Angular Client await CreateApplicationAsync( name: "Angular", type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Angular Client", secret: null, grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.RefreshToken, OpenIddictConstants.GrantTypes.Password, "LinkLogin", "Impersonation", "SwitchTenant" // <-- add this }, scopes: ..., ... );After DbMigrator runs, the
OpenIddictApplicationPermissionstable for theAngularclient should contain a rowgt:SwitchTenant. Then the Switch tenant action in Angular will succeed.You can also add new grant types to an application directly from the OpenIddict admin UI (OpenIddict → Applications → edit → ExtensionGrantTypes). It's persisted to the database immediately, but as shown above it will be wiped on the next DbMigrator run unless you keep
OpenIddictDataSeeder.csin sync. So use the UI for quick experiments, and update the seeder for the permanent fix.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
-
0
Hi,
Confirmed —
SwitchTenantis registered with the OpenIddict server but its handler is missing from the ABP token-grant dispatcher, that's why you see "not implemented".Please add the following to your
AuthServermodule'sConfigureServicesas a workaround:using Volo.Abp.Account.Web.ExtensionGrants; using Volo.Abp.OpenIddict.ExtensionGrantTypes; public override void ConfigureServices(ServiceConfigurationContext context) { // ... existing code ... Configure<AbpOpenIddictExtensionGrantsOptions>(options => { options.Grants[SwitchTenantExtensionGrant.ExtensionGrantName] = new SwitchTenantExtensionGrant(); }); }After this, the
SwitchTenantflow will dispatch correctly. We'll ship a permanent fix on our side in the next ABP release.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)





