Base solution for your next web application
Open Closed

Tenant URL Resolution #6120


User avatar
0
cyklussoftware created

I am running ASP.NET Core + Angular v6.3.1 behind an Nginx proxy.

Similar to this question, https://support.aspnetzero.com/QA/Questions/5665, I want to use {TENANCY_URL}.websitename.com to resolve tenants on the Angular site, but I want to use api.websitename.com to access my API. When I use default.websitename.com and log in, I end up logging into the Host project. Based on your description from the post above, this is intended behavior. Please correct me if I'm wrong.

I added some debugging lines and default is being pulled correctly from the URL. Where is the best place to use the _accountService.isTenantAvailable function to resolve the Tenant ID based on the name?

Please note the following:

In the Angular appconfig.json files I have set the appBaseUrl to https://{TENANCY_NAME}.websitename.com and the removeServiceBaseUrl to https://api.websitename.com.

In the Host appsettings.json files I have set the https://api.websitename.com to be https://api.websitename.com, the ClientRootAddress to https://{TENANCY_NAME}.websitename.com and the CorsOrigins to https://*.websitename.com.

Like I said, I can log into the Host just fine with no errors.


6 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @cyklussoftware

    If you are using seperate apps for Angular & Host, api.websitename.com should be deployed as a separate app on IIS. If so, you can redirect api.websitename.com to this specific app. So, when you enter api.websitename.com on the browser, you will not see Angular app.

    It seems like this should be handled outside of the app by playing with domain records.

    Please correct me If I got you wrong.

  • User Avatar
    0
    cyklussoftware created

    @ismcagdas

    You are correct. The Host project and Angular project are completely separated apps. I'm using Nginx instead of IIS, but it's the same concept. If I navigate my browser to api.websitename.com, I see the Swagger UI.

    Everything works fine between Angular and the Host in this configuration if I manually set the tenant name on the Login page of the Angular app. This is because all of the API requests get an HTTP header of Abp.TenantId added to them.

    If I try to resolve the tenant name from the URL, the Angular app is aware that my tenant name is Default, but because the URL to the Host is just api.websitename.com, the Host doesn't know what tenant I am. I am logged into the Host rather than the Default Tenant.

    Basically, using {TENANCY_NAME}.websitename.com for Angular and api.websitename.com for the Host project logs me into the Host rather than the Default tenant because Angular isn't sending the Abp.TenantId header and the Host project isn't expecting the tenant name to be in the URL.

    Hopefully that makes sense.

    I guess my questions is this: How do I use a common API URL (rather than a {TENANCY_NAME} based URL) for the Host project and a {TENANCY_NAME} based URL for the Angular project?

  • User Avatar
    0
    ryancyq created
    Support Team

    Hi, can you take a screenshot of the network request to ensure that Abp.TenantId is not sent by angular app?

    I think this might be related to https://github.com/aspnetboilerplate/aspnetboilerplate/issues/3275

  • User Avatar
    0
    cyklussoftware created

    @ryancyq I actually found that post a couple of days ago. I was having that problem and fixed it before I tried switching to the {TENANCY_NAME} based tenant resolution.

    Anyways, my proxy and the whole system works just fine if I don't get the TENANCY_NAME from the URL and manually specify it in the Login page instead.

  • User Avatar
    1
    cyklussoftware created

    I put together a little "hack" to get this to work.

    This is my new "getApplicationConfig" method from the AppPreBootstrap.ts file in my Angular project:

    private static getApplicationConfig(appRootUrl: string, callback: () => void) {
    let type = 'GET';
    let url = appRootUrl + 'assets/' + environment.appConfig;
    let customHeaders = [
    {
    name: 'Abp.TenantId',
    value: abp.multiTenancy.getTenantIdCookie() + ''
    }];
    
    
    XmlHttpRequestHelper.ajax(type, url, customHeaders, null, (result) => {
        const subdomainTenancyNameFinder = new SubdomainTenancyNameFinder();
        const tenancyName = subdomainTenancyNameFinder.getCurrentTenancyNameOrNull(result.appBaseUrl);
    
        // CUSTOM ADDITION HERE
        let getTenantIdURL = result.remoteServiceBaseUrl + '/api/services/app/Account/IsTenantAvailable';
        let getTenantIdData =
        {
            tenancyName: tenancyName
        };
        XmlHttpRequestHelper.ajax('POST', getTenantIdURL, null, JSON.stringify(getTenantIdData), (tenantResult) => {
            abp.multiTenancy.setTenantIdCookie(tenantResult.result.tenantId);
    
            // pulled from surrounding ajax method
            AppConsts.appBaseUrlFormat = result.appBaseUrl;
            AppConsts.remoteServiceBaseUrlFormat = result.remoteServiceBaseUrl;
            AppConsts.localeMappings = result.localeMappings;
    
            if (tenancyName == null) {
                AppConsts.appBaseUrl = result.appBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
                AppConsts.remoteServiceBaseUrl = result.remoteServiceBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
            } else {
                AppConsts.appBaseUrl = result.appBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl, tenancyName);
                AppConsts.remoteServiceBaseUrl = result.remoteServiceBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl, tenancyName);
            }
            callback();
        });
        // END CUSTOM ADDITION
    });
    

    Now my appconfig.json in my Angular project includes this:

    "remoteServiceBaseUrl": "https://api.websitename.io",
    "appBaseUrl": "https://{TENANCY_NAME}.websitename.io",
    

    and my appsettings.json file for the Host project includes this:

    "ServerRootAddress": "https://api.websitename.io",
    "ClientRootAddress": "https://dev.websitename.io",
    "CorsOrigins": "https://*.websitename.io"
    

    With this implementation, I can navigate my browser to default.websitename.io and I am logged into the default tenant. I can go to host.websitename.io or any other URL that contains an invalid tenant name and I am logged in the Host.

    It's probably not the best way to do things, but it seems to work just fine.

  • User Avatar
    0
    maharatha created

    i am looking for a solution to this exact problem. I am dedicating a sub domain called api and the rest all tenant name.

    My Angular app is hosted on a S3 bucet and Cloud Front on top of it.

    I tried the above solution but of no sucess.

    "Cookie “Abp.TenantId” will be soon rejected because it has the “sameSite” attribute set to “none” or an invalid value, without the “secure” attribute. To know more about the “sameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite"

    Any other solution to set the TenantId by reading the sub domain from the angular app ?