Open Closed

Issue with Domain-Based Tenant Resolver Login (Angular + OpenIddict) #10261


User avatar
0
Rishi1532 created

Hello ABP Support Team,

We are currently implementing domain-based tenant resolution and have been following the official ABP blog below for the implementation steps:

https://abp.io/community/articles/how-to-use-domainbased-tenant-resolver-in-abp-with-angular-and-openiddict-v9y8da7v#step-2-configure-http-for-local-development-optional

Environment Details

  • Frontend: Angular

  • Backend: .NET 9.0

  • ABP Framework Version: 9.0.4

  • Authentication: OpenIddict

  • Multi-tenancy: Domain-based tenant resolver

Both the frontend and backend are running correctly, and the backend URLs are accessible for specific tenant domains.

However, we are facing the following two issues:


Issue 1: Host (Super Admin) Login Issue

We have renamed the Host Admin to Super Admin. When attempting to log in as the Super Admin:

  • The login screen remains stuck and does not proceed further.

  • If an incorrect password is entered, the error message is displayed correctly.

  • This indicates that authentication validation is occurring, but the login flow is not completing successfully.

  • The same behavior occurs when attempting to log in from the backend as well (i.e., Super Admin login is not working from either Angular or backend).


Issue 2: Tenant Login Issue via Angular (OpenIddict Error)

For tenant-specific domains:

  • The tenant frontend loads correctly.

  • However, when navigating to the login screen from the Angular frontend, an OpenIddict-related error occurs.

  • This issue does not occur when accessing the login screen directly from the backend.

This behavior suggests a possible issue with OpenIddict, tenant resolution during the Angular-based login flow.


Could you please advise on:

The correct way to handle host users login when domain-based tenant resolution is enabled.

Any additional configuration required for Angular + OpenIddict login when using tenant subdomains.

Whether there are known limitations or required settings for this setup in ABP 9.0.4.

Please let us know if you need any additional details, logs, or configuration snippets from our side.

Thank you for your support.

Best regards, Rishikesh


1 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent

    1. Solution

    I’ll separate the answer into:

    1. How host (Super Admin) login should work with domain-based tenant resolution.
    2. How to fix the Angular + OpenIddict “redirect_uri is not valid for this client” error for tenant domains.

    1. Host (Super Admin) login with domain-based tenant resolver

    Goal:

    • Host (Super Admin) should log in on the host domain (no tenant in domain).
    • Tenant users should log in on their tenant subdomain.

    With:

    Configure<AbpTenantResolveOptions>(options =>
    {
        options.AddDomainTenantResolver("{0}.multitenancydemo.local");
    });
    

    ABP behaves as:

    • multitenancydemo.localno tenant → Host context (CurrentTenant.IsAvailable == false).
    • test.multitenancydemo.local → tenant test.

    This is correct and does not prevent host login. If:

    • Wrong password shows “Invalid username or password”
    • Correct password makes the page “stick” or loop

    then the usual root cause is OpenIddict client configuration / redirect URIs, not the user itself.

    For host login to work:

    1. Make sure the host domain is registered as a redirect URI & CORS origin for the Angular app client.

      In OpenIddict (ABP 9+), this is done by the data seed (usually in YourProjectNameDomainDataSeedContributor, via OpenIddictApplicationManager).
      You must ensure your Angular client (e.g. DentPal_App) has:

    Redirect URIs:
         - http://multitenancydemo.local:4200     (host)
         - http://{0}.multitenancydemo.local:4200 (tenants via wildcard)
    
       Post Logout Redirect URIs:
         - same pattern as above
       CORS origins:
         - http://multitenancydemo.local:4200
         - http://*.multitenancydemo.local:4200 (or each tenant explicitly)
    

    In the blog/sample this is done by seeding with RootUrl = "http://{0}.multitenancydemo.local:4200".
    You must add another entry (or additional redirect URI) for the host root URL:

    await CreateApplicationAsync(
           name: "DentPal_App",
           type: OpenIddictConstants.ClientTypes.Public,
           consentType: OpenIddictConstants.ConsentTypes.Explicit,
           displayName: "DentPal Angular Application",
           secret: null,
           grantTypes: new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.RefreshToken },
           scopes: new[] { "DentPal", OpenIddictConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles },
           redirectUri: "http://{0}.multitenancydemo.local:4200",              // tenants
           extraRedirectUris: new[]
           {
               "http://multitenancydemo.local:4200"                            // host
           },
           postLogoutRedirectUri: "http://{0}.multitenancydemo.local:4200",
           extraPostLogoutRedirectUris: new[]
           {
               "http://multitenancydemo.local:4200"
           },
           clientUri: null,
           permissions: ...);
    

    (Exact method name/signature may differ, but the idea is: client has both wildcard and host URLs.)

    1. Angular environment for the host domain must match that host URL.

      For the host Angular app (where Super Admin logs in):

    const hostBaseUrl = 'http://multitenancydemo.local:4200';
    
       export const environment = {
         production: false,
         application: {
           baseUrl: hostBaseUrl,
           name: 'DentPal',
         },
         oAuthConfig: {
           issuer: 'http://multitenancydemo.local:44397', // your HttpApi.Host/OpenIddict endpoint
           redirectUri: hostBaseUrl,
           clientId: 'DentPal_App',
           responseType: 'code',
           scope: 'offline_access DentPal',
           requireHttps: false
         },
         apis: {
           default: {
             url: 'http://multitenancydemo.local:44397',
             rootNamespace: 'DentPal',
           },
         },
       };
    

    If the host Angular also uses {0}.multitenancydemo.local as baseUrl / redirectUri while you are on multitenancydemo.local, the generated redirect_uri won’t match OpenIddict’s registered URIs and the login flow gets stuck/loops.

    1. Confirm AppUrlOptions configuration doesn’t crash.

      If you override AppUrlOptions (e.g. in HttpApiHostModule.ConfigureServices) like:

    Configure<AppUrlOptions>(options =>
       {
           options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
           options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
       });
    

    make sure App:AngularUrl exists in appsettings.json and is not null (otherwise you can get hidden 500s that appear as “stuck login”).


    2. Tenant login from Angular – OpenIddict “redirect_uri is not valid for this client”

    Your screenshot shows:

    error_description: The specified 'redirect_uri' is not valid for this client application.

    The URL in the bar is something like:

    .../connect/authorize?client_id=DentPal_App&redirect_uri=http%3A%2F%2Ftest.multitenancydemo.local%3A4200...
    

    This means:

    • Angular uses redirectUri = 'http://{0}.multitenancydemo.local:4200'.
    • Running at http://test.multitenancydemo.local:4200 correctly produces redirect_uri=http://test.multitenancydemo.local:4200.
    • But OpenIddict’s client DentPal_App does not have this redirect URI registered (only the non-tenant URL, or only another domain).

    To fix it for all tenants:

    1. Enable wildcard domain support for OpenIddict (as in the article):

      In YourProjectNameHttpApiHostModule.PreConfigureServices:

    using Volo.Abp.OpenIddict.WildcardDomains;
    
       public override void PreConfigureServices(ServiceConfigurationContext context)
       {
       #if DEBUG
           PreConfigure<OpenIddictServerBuilder>(options =>
           {
               options.UseAspNetCore().DisableTransportSecurityRequirement();
           });
       #endif
    
           PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
           {
               options.EnableWildcardDomainSupport = true;
               options.WildcardDomainsFormat.Add("http://{0}.multitenancydemo.local:4200");
           });
       }
    

    This lets OpenIddict treat http://{0}.multitenancydemo.local:4200 as a wildcard redirect URI pattern.

    1. Seed the OpenIddict client with the wildcard RootUrl.

      In your DbMigrator appsettings.json (or wherever you seed applications):

    "OpenIddict": {
         "Applications": {
           "DentPal_App": {
             "ClientId": "DentPal_App",
             "RootUrl": "http://{0}.multitenancydemo.local:4200",
             "Scopes": "DentPal offline_access",
             "ClientType": "Public"
           }
         }
       }
    

    Or equivalent C# seeding where RootUrl = "http://{0}.multitenancydemo.local:4200".

    Then run the DbMigrator again so the OpenIddictApps / OpenIddictRedirectUris tables are updated.

    1. Angular tenant environment must use the same pattern.

      For the tenant-specific Angular app (the one accessed via http://{tenant}.multitenancydemo.local:4200):

    const tenantBaseUrl = 'http://{0}.multitenancydemo.local:4200';
    
       export const environment = {
         production: false,
         application: {
           baseUrl: tenantBaseUrl,
           name: 'DentPal',
         },
         oAuthConfig: {
           issuer: 'http://multitenancydemo.local:44397',   // same auth server (host) or shared auth
           redirectUri: tenantBaseUrl,                       // the {0} pattern
           clientId: 'DentPal_App',
           responseType: 'code',
           scope: 'offline_access DentPal',
           requireHttps: false
         },
         apis: {
           default: {
             url: 'http://multitenancydemo.local:44397',     // API host; tenant is resolved via domain resolver on backend
             rootNamespace: 'DentPal',
           },
         },
       };
    

    When the app runs at http://test.multitenancydemo.local:4200, the {0} placeholder resolves to test, and the generated redirect_uri will be:

    http://test.multitenancydemo.local:4200
    

    which matches the wildcard pattern allowed by AbpOpenIddictWildcardDomainOptions + RootUrl.

    1. Tenant resolution configuration (already done, just confirm).

      In HttpApiHostModule:

    using Volo.Abp.MultiTenancy;
    
       public override void ConfigureServices(ServiceConfigurationContext context)
       {
           var configuration = context.Services.GetConfiguration();
    
           Configure<AbpTenantResolveOptions>(options =>
           {
               options.AddDomainTenantResolver("{0}.multitenancydemo.local");
           });
       }
    

    No extra config is needed on Angular for tenant resolution; ABP Angular will automatically send the correct __tenant header based on the baseUrl/domain.


    3. Known requirements / limitations for ABP 9.0.4 in this setup

    With ABP 9.0.4 + Angular + OpenIddict + domain-based resolver:

    1. Redirect URIs must exactly match what OpenIddict knows.

      • Use wildcard support (AbpOpenIddictWildcardDomainOptions) rather than trying to store https://*.domain in DB (OpenIddict will reject *).
    2. You typically need both:

      • Wildcard tenant RootUrl (http://{0}.multitenancydemo.local:4200),
      • Explicit host RootUrl (http://multitenancydemo.local:4200) for Super Admin.
    3. If Angular is configured to auto-login in a component (like calling navigateToLogin() on component init), this can create loops if any mismatch exists. For debugging, keep auto-login disabled and trigger login only via a button until everything works.

    4. Changing the host admin’s username to SuperAdmin is fine and not related, as long as:

      • The user is a host user (no TenantId),
      • You are logging in from the host domain.

    2. Sources

    • Domain-based tenant resolver with Angular + OpenIddict (the blog you followed; includes wildcard configuration and sample repo):

      • https://abp.io/community/articles/how-to-use-domainbased-tenant-resolver-in-abp-with-angular-and-openiddict-v9y8da7v
      • https://github.com/mahmut-gundogdu/ABP-Domain-based-Tenant-Example
    • ABP multi-tenancy docs (domain/subdomain resolver & {0} placeholder):

      • https://abp.io/docs/latest/framework/architecture/multi-tenancy/index

    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.

Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on December 25, 2025, 06:16
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.