Starts in:
1 DAY
23 HRS
42 MIN
42 SEC
Starts in:
1 D
23 H
42 M
42 S

Activities of "LinchArnold"

Thank you Linch, what problem you been solving by rewriting angular domain resolver? As i understand it supposed to work out of the box without rewriting it?

Hi, kirotech, I just updated the code of multi-tenancy-utils.ts , because it is work for datdv1 https://support.abp.io/QA/Questions/5650#answer-3a0d82ab-0969-e910-4824-cab59dc00e91

Maybe you can try this to solve your problem. Hope it is work for you.

import {
  ABP,
  CORE_OPTIONS,
  Environment,
  EnvironmentService,
  createTokenParser,
  getRemoteEnv,
} from '@abp/ng.core';
import { Injector } from '@angular/core';
import clone from 'just-clone';

const tenancyPlaceholder = '{0}';

function getCurrentTenancyName(appBaseUrl: string): string {
  if (appBaseUrl.charAt(appBaseUrl.length - 1) !== '/') appBaseUrl += '/';
  const parseTokens = createTokenParser(appBaseUrl);
  const token = tenancyPlaceholder.replace(/[}{]/g, '');
  return parseTokens(window.location.href)[token]?.[0];
}

export function cleanPlaceholderFromHostUrl(injector: Injector) {
  const fn = async () => {
    const environmentService = injector.get(EnvironmentService);
    const options = injector.get(CORE_OPTIONS) as ABP.Root;
    environmentService.setState(options.environment as Environment);
    await getRemoteEnv(injector, options.environment);
    const baseUrl =
      environmentService.getEnvironment()['appBaseUrl'] ||
      environmentService.getEnvironment().application?.baseUrl ||
      '';
    const tenancyName = getCurrentTenancyName(baseUrl);

    if (!tenancyName) {
      /**
       * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests.
       */
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.');
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder);
    }

    return Promise.resolve();
  };

  return fn;
}

function replaceTenantNameWithinEnvironment(
  injector: Injector,
  tenancyName: string,
  placeholder = tenancyPlaceholder
) {
  const environmentService = injector.get(EnvironmentService);

  const environment = clone(environmentService.getEnvironment()) as Environment;

  if (environment.application.baseUrl) {
    environment.application.baseUrl = environment.application.baseUrl.replace(
      placeholder,
      tenancyName
    );
  }

  if (environment.oAuthConfig?.redirectUri) {
    environment.oAuthConfig.redirectUri = environment.oAuthConfig.redirectUri.replace(
      placeholder,
      tenancyName
    );
  }

  if (!environment.oAuthConfig) {
    environment.oAuthConfig = {};
  }
  environment.oAuthConfig.issuer = (environment.oAuthConfig.issuer || '').replace(
    placeholder,
    tenancyName
  );

  environment.oAuthConfig.clientId = (environment.oAuthConfig.clientId || '').replace(
    placeholder,
    tenancyName
  );

  Object.keys(environment.apis).forEach(api => {
    Object.keys(environment.apis[api]).forEach(key => {
      environment.apis[api][key] = (environment.apis[api][key] || '').replace(
        placeholder,
        tenancyName
      );
    });
  });

  return environmentService.setState(environment);
}

Hello Anjali_Musmade, Thanks a lot, this works.

Hello alper, article helped to solve redirect_uri issue, but now I face angular problem where if you go to https://testtenant.ng.abp.net:4200 angular doesn't load because if he thinks that user is not authenticated no matter authserver returned valid token and issuer matching angular issuer environment config.

I do have all this abp commercial microservices tenant subdomain mapping config in separate test repository which im ok to share with you if needed.

I still need help with finishing this abp commercial microservices feature work.

Hi, kirotech, if you have problem at the angular side, you can try the following steps, maybe it will work:

  1. Create a multi-tenancy-utils.ts file, this will be override the default implementation of ABP framework:
import {
  ABP,
  CORE_OPTIONS,
  Environment,
  EnvironmentService,
  createTokenParser,
  getRemoteEnv,
} from '@abp/ng.core';
import { Injector } from '@angular/core';
import clone from 'just-clone';

const tenancyPlaceholder = '{0}';

function getCurrentTenancyName(appBaseUrl: string): string {
  if (appBaseUrl.charAt(appBaseUrl.length - 1) !== '/') appBaseUrl += '/';
  const parseTokens = createTokenParser(appBaseUrl);
  const token = tenancyPlaceholder.replace(/[}{]/g, '');
  return parseTokens(window.location.href)[token]?.[0];
}

export function cleanPlaceholderFromHostUrl(injector: Injector) {
  const fn = async () => {
    const environmentService = injector.get(EnvironmentService);
    const options = injector.get(CORE_OPTIONS) as ABP.Root;
    environmentService.setState(options.environment as Environment);
    await getRemoteEnv(injector, options.environment);
    const baseUrl =
      environmentService.getEnvironment()['appBaseUrl'] ||
      environmentService.getEnvironment().application?.baseUrl ||
      '';
    const tenancyName = getCurrentTenancyName(baseUrl);

    if (!tenancyName) {
      /**
       * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests.
       */
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.');
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder);
    }

    return Promise.resolve();
  };

  return fn;
}

function replaceTenantNameWithinEnvironment(
  injector: Injector,
  tenancyName: string,
  placeholder = tenancyPlaceholder
) {
  const environmentService = injector.get(EnvironmentService);

  const environment = clone(environmentService.getEnvironment()) as Environment;

  if (environment.application.baseUrl) {
    environment.application.baseUrl = environment.application.baseUrl.replace(
      placeholder,
      tenancyName
    );
  }

  if (environment.oAuthConfig?.redirectUri) {
    environment.oAuthConfig.redirectUri = environment.oAuthConfig.redirectUri.replace(
      placeholder,
      tenancyName
    );
  }

  if (!environment.oAuthConfig) {
    environment.oAuthConfig = {};
  }
  environment.oAuthConfig.issuer = (environment.oAuthConfig.issuer || '').replace(
    placeholder,
    tenancyName
  );

  environment.oAuthConfig.clientId = (environment.oAuthConfig.clientId || '').replace(
    placeholder,
    tenancyName
  );

  Object.keys(environment.apis).forEach(api => {
    Object.keys(environment.apis[api]).forEach(key => {
      environment.apis[api][key] = (environment.apis[api][key] || '').replace(
        placeholder,
        tenancyName
      );
    });
  });

  return environmentService.setState(environment);
}

  1. Create domain-resolver.module.ts file:
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, Injector, NgModule } from '@angular/core';
import { cleanPlaceholderFromHostUrl } from './multi-tenancy-utils';

@NgModule({
  imports: [CommonModule, HttpClientModule],
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      deps: [Injector],
      useFactory: cleanPlaceholderFromHostUrl,
    },
  ],
})
export class DomainResolverModule {}
  1. Import the DomainResolverModule in your AppModule:
import { CoreModule } from '@abp/ng.core';
import { GdprConfigModule } from '@volo/abp.ng.gdpr/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { HTTP_ERROR_HANDLER, ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CommercialUiConfigModule } from '@volo/abp.commercial.ng.ui/config';
import { AccountAdminConfigModule } from '@volo/abp.ng.account/admin/config';
import { AccountPublicConfigModule } from '@volo/abp.ng.account/public/config';
import { AuditLoggingConfigModule } from '@volo/abp.ng.audit-logging/config';
import { IdentityConfigModule } from '@volo/abp.ng.identity/config';
import { LanguageManagementConfigModule } from '@volo/abp.ng.language-management/config';
import { registerLocale } from '@volo/abp.ng.language-management/locale';
import { SaasConfigModule } from '@volo/abp.ng.saas/config';
import { TextTemplateManagementConfigModule } from '@volo/abp.ng.text-template-management/config';
import { HttpErrorComponent, ThemeLeptonModule } from '@volo/abp.ng.theme.lepton';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { OpeniddictproConfigModule } from '@volo/abp.ng.openiddictpro/config';
import { FeatureManagementModule } from '@abp/ng.feature-management';
import { AbpOAuthModule } from '@abp/ng.oauth';
import { ServiceWorkerModule } from '@angular/service-worker';
import { APP_VALIDATION_PROVIDER } from './validation/app.validation.provider';
import { DomainResolverModule } from './customization';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    DomainResolverModule,
    CoreModule.forRoot({
      environment,
      registerLocaleFn: registerLocale(),
    }),
    AbpOAuthModule.forRoot(),
    ThemeSharedModule.forRoot({
      httpErrorConfig: {
        errorScreen: {
          component: HttpErrorComponent,
          forWhichErrors: [401, 403, 404, 500],
          hideCloseIcon: true,
        },
      },
    }),
    // Other import statements
  ],
  providers: [
    APP_ROUTE_PROVIDER,
    APP_VALIDATION_PROVIDER
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Hi, I came across this problem months ago.

After some research, I fixed it with the following steps:

In AuthServer Module.ts :

public override void PreConfigureServices(ServiceConfigurationContext context)
{
    // other codes.

    PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
            {
                options.AddDevelopmentEncryptionAndSigningCertificate = false;
            });

            PreConfigure<OpenIddictServerBuilder>(builder =>
            {
                builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
                if (!hostingEnvironment.IsProduction())
                {
                    builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
                }
            });
            
                PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
                {
                    options.EnableWildcardDomainSupport = true;
                    options.WildcardDomainsFormat.Add(configuration["App:AngularWildcardUrl"]);
                });

                PreConfigure<OpenIddictServerOptions>(options =>
                {
                    options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
                    options.TokenValidationParameters.ValidIssuers = new[]
                    {
                        configuration["AuthServer:Authority"].EnsureEndsWith('/'),
                        configuration["AuthServer:WildcardAuthority"].EnsureEndsWith('/')
                    };
                });
      
}

Note: TokenWildcardIssuerValidator.IssuerValidator is from package <PackageReference Include="Owl.TokenWildcardIssuerValidator" Version="1.0.0" />

In HttpApi.Host module ts:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    // other codes
    context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = configuration["AuthServer:Authority"];
                options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                options.Audience = "PayloadLimitPfmNext";

                if (hostingEnvironment.IsProduction())
                {
                    options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
                    options.TokenValidationParameters.ValidIssuers = new[]
                    {
                        configuration["AuthServer:Authority"].EnsureEndsWith('/'),
                        configuration["AuthServer:WildcardAuthority"].EnsureEndsWith('/')
                    };
                }
            });
}

Note: TokenWildcardIssuerValidator.IssuerValidator is from package <PackageReference Include="Owl.TokenWildcardIssuerValidator" Version="1.0.0" />

In Angular:

  1. Create a multi-tenancy-utils.ts file, this will be override the default implementation of ABP framework:
import {
  ABP,
  CORE_OPTIONS,
  Environment,
  EnvironmentService,
  createTokenParser,
  getRemoteEnv,
} from '@abp/ng.core';
import { Injector } from '@angular/core';
import clone from 'just-clone';

const tenancyPlaceholder = '{0}';

function getCurrentTenancyName(appBaseUrl: string): string {
  if (appBaseUrl.charAt(appBaseUrl.length - 1) !== '/') appBaseUrl += '/';
  const parseTokens = createTokenParser(appBaseUrl);
  const token = tenancyPlaceholder.replace(/[}{]/g, '');
  return parseTokens(window.location.href)[token]?.[0];
}

export function cleanPlaceholderFromHostUrl(injector: Injector) {
  const fn = async () => {
    const environmentService = injector.get(EnvironmentService);
    const options = injector.get(CORE_OPTIONS) as ABP.Root;
    environmentService.setState(options.environment as Environment);
    await getRemoteEnv(injector, options.environment);
    const baseUrl =
      environmentService.getEnvironment()['appBaseUrl'] ||
      environmentService.getEnvironment().application?.baseUrl ||
      '';
    const tenancyName = getCurrentTenancyName(baseUrl);

    if (!tenancyName) {
      /**
       * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests.
       */
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.');
      replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder);
    }

    return Promise.resolve();
  };

  return fn;
}

function replaceTenantNameWithinEnvironment(
  injector: Injector,
  tenancyName: string,
  placeholder = tenancyPlaceholder
) {
  const environmentService = injector.get(EnvironmentService);

  const environment = clone(environmentService.getEnvironment()) as Environment;

  if (environment.application.baseUrl) {
    environment.application.baseUrl = environment.application.baseUrl.replace(
      placeholder,
      tenancyName
    );
  }

  if (environment.oAuthConfig?.redirectUri) {
    environment.oAuthConfig.redirectUri = environment.oAuthConfig.redirectUri.replace(
      placeholder,
      tenancyName
    );
  }

  if (!environment.oAuthConfig) {
    environment.oAuthConfig = {};
  }
  environment.oAuthConfig.issuer = (environment.oAuthConfig.issuer || '').replace(
    placeholder,
    tenancyName
  );

  environment.oAuthConfig.clientId = (environment.oAuthConfig.clientId || '').replace(
    placeholder,
    tenancyName
  );

  Object.keys(environment.apis).forEach(api => {
    Object.keys(environment.apis[api]).forEach(key => {
      environment.apis[api][key] = (environment.apis[api][key] || '').replace(
        placeholder,
        tenancyName
      );
    });
  });

  return environmentService.setState(environment);
}
  1. Create domain-resolver.module.ts file:
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, Injector, NgModule } from '@angular/core';
import { cleanPlaceholderFromHostUrl } from './multi-tenancy-utils';

@NgModule({
  imports: [CommonModule, HttpClientModule],
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      deps: [Injector],
      useFactory: cleanPlaceholderFromHostUrl,
    },
  ],
})
export class DomainResolverModule {}
  1. Import the DomainResolverModule in your AppModule:
import { CoreModule } from '@abp/ng.core';
import { GdprConfigModule } from '@volo/abp.ng.gdpr/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { HTTP_ERROR_HANDLER, ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CommercialUiConfigModule } from '@volo/abp.commercial.ng.ui/config';
import { AccountAdminConfigModule } from '@volo/abp.ng.account/admin/config';
import { AccountPublicConfigModule } from '@volo/abp.ng.account/public/config';
import { AuditLoggingConfigModule } from '@volo/abp.ng.audit-logging/config';
import { IdentityConfigModule } from '@volo/abp.ng.identity/config';
import { LanguageManagementConfigModule } from '@volo/abp.ng.language-management/config';
import { registerLocale } from '@volo/abp.ng.language-management/locale';
import { SaasConfigModule } from '@volo/abp.ng.saas/config';
import { TextTemplateManagementConfigModule } from '@volo/abp.ng.text-template-management/config';
import { HttpErrorComponent, ThemeLeptonModule } from '@volo/abp.ng.theme.lepton';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { OpeniddictproConfigModule } from '@volo/abp.ng.openiddictpro/config';
import { FeatureManagementModule } from '@abp/ng.feature-management';
import { AbpOAuthModule } from '@abp/ng.oauth';
import { ServiceWorkerModule } from '@angular/service-worker';
import { APP_VALIDATION_PROVIDER } from './validation/app.validation.provider';
import { DomainResolverModule } from './customization';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    DomainResolverModule,
    CoreModule.forRoot({
      environment,
      registerLocaleFn: registerLocale(),
    }),
    AbpOAuthModule.forRoot(),
    ThemeSharedModule.forRoot({
      httpErrorConfig: {
        errorScreen: {
          component: HttpErrorComponent,
          forWhichErrors: [401, 403, 404, 500],
          hideCloseIcon: true,
        },
      },
    }),
    // Other import statements
  ],
  providers: [
    APP_ROUTE_PROVIDER,
    APP_VALIDATION_PROVIDER
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

After this, if the website wildcard is web{0}.sample.com, the web.sample.com will be host, webtenant1.sample.com will be tenant tenant1.

Thanks. @masum.ulu

Hello,

What is your angular version?

Is the defaultProject property exists in your angular.json?

We know defaultProject deprecated with angular 14. And we fixed proxy generation. The fix will be available in 6.0.2

Same error here, abp version 6.0.1, prompt enter target Angular project to place the generated code. when execute generate-proxy -t ng for the Application Module project type.

So what's the time for releasing 6.0.2?

That does not solve the problem!

Dear Support team, any update on the original issue?

Open your eyes widely please! I am not the abp team member, I am just came across the same errors as you. Just provide a additional evidence that abp 6.0.1 also has this bug. so it absolutely can not solve your problem. So see clearly what the other person said next time before your next reply, is that ok?

Hello,

What is your angular version?

Is the defaultProject property exists in your angular.json?

We know defaultProject deprecated with angular 14. And we fixed proxy generation. The fix will be available in 6.0.2

Same error here, abp version 6.0.1, prompt enter target Angular project to place the generated code. when execute generate-proxy -t ng for the Application Module project type. So what's the time for releasing 6.0.2?

Hi, muhammedaltug: I had update my angular version to 14. and update the angular packages to version 14 either.

And this is result of yarn why @abp/ng.schematics:

Hi, support team, it has been 5 days, any information?

our test team tested this issue and couldn't reproduce it. they just send feedback that it takes a long time to generate the UI otherwise it works. make sure your ABP Suite and your ABP project are the same version. Also, test it with the new 5.3.0 project to understand it works with the brand new template.

Thanks, I tried again, really wired, it worked, the exception's gone.

Showing 1 to 10 of 21 entries
Made with ❤️ on ABP v9.1.0-preview. Updated on November 20, 2024, 13:06