Base solution for your next web application

Activities of "henriksorensen"

Hello We fixed this problem in a slightly different way using the AppUrlService.appRootUrl service property. But we are still wondering if there are ways to fix this problem without modifying the code? Or should we expect a fix for this problem in future versions of ASP.NET Zero?

We've run into an issue with handling an issue on the angular side when our angular application has a relative path like /application/, for example, http://localhost/application/

The default ASP.NET Zero handler redirects the user according to the following code (zero-template-http-configuration.service.ts):

import { Injectable } from '@angular/core'; import { AbpHttpConfigurationService, LogService, MessageService } from 'abp-ng2-module'; import { Router } from '@angular/router';

@Injectable({ providedIn: 'root', }) export class ZeroTemplateHttpConfigurationService extends AbpHttpConfigurationService { constructor(messageService: MessageService, logService: LogService, private _route: Router) { super(messageService, logService); }

// Override handleUnAuthorizedRequest so it doesn't refresh the page during failed login attempts.
handleUnAuthorizedRequest(messagePromise: any, targetUrl?: string) {
    if (this._route.url && (
        this._route.url.startsWith('/account/login') || this._route.url.startsWith('/account/session-locked')
    )) {
        return;
    }

    const self = this;

    if (messagePromise) {
        messagePromise.done(() => {
            this.handleTargetUrl(targetUrl || '/');
        });
    } else {
        self.handleTargetUrl(targetUrl || '/');
    }
}

}

Actual result

The redirect occurs to the domain name and does not take into account the relative path.

Expected result After the redirect, the user is redirected to the correct root URL

So, could you please provide us: 1. How to pass targetUrl argument to this method? In our case it is NULL

2. Or how can we solve the problem with an application whose relative path is different from the domain name = '/'?

Error message with the inability to log out

We encountered an error when the user's session has expired and the user is trying to log out of the site. It leads to an error message with the inability to log out.

Pre-conditions: User session is expired (AccessTokenExpirationMinutes parameter is used to set session timeout)

Debug steps:

  1. User clicks Logout button

  2. From angular project, the GET request is sent to back-end Logout endpoint - /api/TokenAuth/LogOut Original ASP NET Zero code: logout(reload?: boolean, returnUrl?: string): void { let customHeaders = { [abp.multiTenancy.tenantIdCookieName]: abp.multiTenancy.getTenantIdCookie(), Authorization: 'Bearer ' + abp.auth.getToken(), };

     XmlHttpRequestHelper.ajax(
         'GET',
         AppConsts.remoteServiceBaseUrl + '/api/TokenAuth/LogOut',
         customHeaders,
         null,
         () => {
             abp.auth.clearToken();
             abp.auth.clearRefreshToken();
             new LocalStorageService().removeItem(AppConsts.authorization.encrptedAuthTokenName, () => {
                 if (reload !== false) {
                     if (returnUrl) {
                         location.href = returnUrl;
                     } else {
                         location.href = '';
                     }
                 }
             });
         }
     );
    

    }

  3. This endpoint looks like: Original ASP NET Zero code:

     [HttpGet]
     [AbpMvcAuthorize]
     public async Task LogOut()
     {
         if (AbpSession.UserId != null)
         {
             var tokenValidityKeyInClaims = User.Claims.First(c => c.Type == AppConsts.TokenValidityKey);
             await RemoveTokenAsync(tokenValidityKeyInClaims.Value);
    
             var refreshTokenValidityKeyInClaims =
                 User.Claims.FirstOrDefault(c => c.Type == AppConsts.RefreshTokenValidityKey);
             if (refreshTokenValidityKeyInClaims != null)
             {
                 await RemoveTokenAsync(refreshTokenValidityKeyInClaims.Value);
             }
    
             if (AllowOneConcurrentLoginPerUser())
             {
                 await _securityStampHandler.RemoveSecurityStampCacheItem(
                     AbpSession.TenantId,
                     AbpSession.GetUserId()
                 );
             }
         }
     }
    
  4. The main problem is with the AbpMvcAuthorize attribute [AbpMvcAuthorize] Since user is already not authenticated, then it leads to 404 response from server(suggest, one more error in ASP NET Zero, it should be 401).

  5. On front-end part this response is handled as:

export class XmlHttpRequestHelper { static ajax(type: string, url: string, customHeaders: any, data: any, success: any) { let xhr = new XMLHttpRequest();

    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                let result = JSON.parse(xhr.responseText);
                success(result);
            } else if (xhr.status !== 0) {
                alert(abp.localization.localize('InternalServerError', 'AbpWeb'));
            }
        }
    };

    url += (url.indexOf('?') >= 0 ? '&' : '?') + 'd=' + new Date().getTime();
    xhr.open(type, url, true);

    for (let property in customHeaders) {
        if (customHeaders.hasOwnProperty(property)) {
            xhr.setRequestHeader(property, customHeaders[property]);
        }
    }

    xhr.setRequestHeader('Content-type', 'application/json');
    if (data) {
        xhr.send(data);
    } else {
        xhr.send();
    }
}

}

Alert message is shown:

alert(abp.localization.localize('InternalServerError', 'AbpWeb'));

Expected result Alert Internal Server Error message

Actual result No errors, user is logged out, login page is displayed

Question

Do you agree that the expected result is not as desired? Will you fix this in a later release? (we are using 11.4)

Thanks Asp.Net Zero Support

We have now tried adding that attribute, see below. No change experienced, still same issue.

    [DisableAuditing]
    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
    {

Any other suggestions or ideas?

Sometimes, during healthchecking, we get error about connection to database. It happens 2-3 times in 2500 healthcheck requests. Could you help us to find possible reason of this error?

Log: TASWebclient;2022-10-30 23:34:50,139;ERROR;Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService;[.NET ThreadPool Worker];(null);(null);Health check Database Connection with user check with status Unhealthy completed after 4.4974ms with message 'WebClientDbContext could not connect to database.';System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Scheme() at Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(HttpRequest request) at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection1 entityEntries) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext3.SaveChangesAsync(CancellationToken cancellationToken) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUowAsync() at Abp.Domain.Uow.UnitOfWorkBase.CompleteAsync() at TAS.WebClient.HealthChecks.WebClientDbContextUsersHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken) in D:\BuildAgents\DevOpsAgent39_Work\4\s\TASWebclient\aspnet-core\src\TAS.WebClient.Application\HealthChecks\WebClientDbContextUsersHealthCheck.cs:line 58 ;

WebClientDbContextUsersHealthCheck.cs :

using System;
using System.Threading;
using System.Threading.Tasks;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using TAS.WebClient.EntityFrameworkCore;

namespace TAS.WebClient.HealthChecks
{
    public class WebClientDbContextUsersHealthCheck : IHealthCheck
    {
        private readonly IDbContextProvider<WebClientDbContext> _dbContextProvider;
        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public WebClientDbContextUsersHealthCheck(
            IDbContextProvider<WebClientDbContext> dbContextProvider,
            IUnitOfWorkManager unitOfWorkManager
            )
        {
            _dbContextProvider = dbContextProvider;
            _unitOfWorkManager = unitOfWorkManager;
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
        {
            try
            {
                using (var uow = _unitOfWorkManager.Begin())
                {
                    // Switching to host is necessary for single tenant mode.
                    using (_unitOfWorkManager.Current.SetTenantId(null))
                    {
                        var dbContext = await _dbContextProvider.GetDbContextAsync();
                        if (!await dbContext.Database.CanConnectAsync(cancellationToken))
                        {
                            return HealthCheckResult.Unhealthy(
                                "WebClientDbContext could not connect to database"
                            );
                        }

                        var user = await dbContext.Users.AnyAsync(cancellationToken);
                        await uow.CompleteAsync();

                        if (user)
                        {
                            return HealthCheckResult.Healthy("WebClientDbContext connected to database and checked whether user added");
                        }

                        return HealthCheckResult.Unhealthy("WebClientDbContext connected to database but there is no user.");

                    }
                }
            }
            catch (Exception e)
            {
                return HealthCheckResult.Unhealthy("WebClientDbContext could not connect to database.", e);
            }
        }
    }
}

AbpZeroHealthCheck.cs:

using Microsoft.Extensions.DependencyInjection;
using TAS.WebClient.HealthChecks;

namespace TAS.WebClient.Web.HealthCheck
{
    public static class AbpZeroHealthCheck
    {
        public static IHealthChecksBuilder AddAbpZeroHealthCheck(this IServiceCollection services)
        {
            var builder = services.AddHealthChecks();
            builder.AddCheck<WebClientDbContextHealthCheck>("Database Connection");
            builder.AddCheck<WebClientDbContextUsersHealthCheck>("Database Connection with user check");
            builder.AddCheck<CacheHealthCheck>("Cache");
            builder.AddCheck<ExternalApiHealthCheck>("ExternalApi Connection");
            builder.AddCheck<DocumentApiHealthCheck>("DocumentApi Connection");

            // add your custom health checks here
            // builder.AddCheck<MyCustomHealthCheck>("my health check");

            return builder;
        }
    }
}

Startup.cs:

if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                services.AddAbpZeroHealthCheck();

                var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");

                if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
                {
                    services.Configure<HealthChecksUISettings>(settings =>
                    {
                        healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
                    });
                    services.AddHealthChecksUI()
                        .AddInMemoryStorage();
                }
            }

...



app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapHub<ChatHub>("/signalr-chat");

                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
                {
                    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
                    {
                        Predicate = _ => true,
                        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                    });
                }

                app.ApplicationServices.GetRequiredService<IAbpAspNetCoreConfiguration>().EndpointConfiguration.ConfigureAllEndpoints(endpoints);
            });

            if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
                {
                    app.UseHealthChecksUI();
                }
            }

appsettings.json:

  "HealthChecks": {
    "HealthChecksEnabled": true,
    "HealthChecksUI": {
      "HealthChecksUIEnabled": true,
      "HealthChecks": [
        {
          "Name": "TAS.WebClient.Web.Host",
          "Uri": "https://localhost:44301/health"
        }
      ],
      "EvaluationTimeOnSeconds": 10,
      "MinimumSecondsBetweenFailureNotifications": 60
    }
  },
  

Product Version 9.1 Angular .Net Core ABP version 51.5

<br> And I am unable to configure the grant type / Response type in the OpenId configuring section. I'm able to configure ClientId, Client Secret etc. but not grant type. I want use Granttype = code. It looks like Angular is using grant type = "hybrid" by default. Please confirm?

I'm able to configure/code the same in the .Web.Startup.AuthConfigurer class in the MVC version of Asp.Net Zero (my demo solution). Like this

authenticationBuilder.AddOpenIdConnect(options => { options.ClientId = configuration["Authentication:OpenId:ClientId"]; options.Authority = configuration["Authentication:OpenId:Authority"]; options.SignedOutRedirectUri = configuration["App:WebSiteRootAddress"] + "Account/Logout"; options.ResponseType = OpenIdConnectResponseType.Code; // Grantstype = code

But I can't find a similar way for the angular solution? Please assist

Best regards Tom

My Configuration:

AllowSocialLoginSettingsPerTenant = true and "OpenId": { "IsEnabled": "true", "Authority": "https://localhost:44313", "LoginUrl": "https://localhost:44313/Account/Login?", "ClientId": "my-webclient", "ClientSecret": "XYZ", "ValidateIssuer": "false", "ClaimsMapping": [ { "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "key": "http://schemas.microsoft.com/identity/claims/objectidentifier" } ] },

IdentityServer4 3.1 running on "https://localhost: 44313" with "my-webclient" like this "AllowedGrantTypes": [ "authorization_code" ], "RequirePkce": true,

@ismcagdas

Just want to let you know I have fixed my upgrade problem. Did a upgrade from 5.1.0 to 5.2.0.

Thanks, Henrik

@ismcagdas

Yes I have regenerate service-proxies by doing "../angular/nswag/refresh.bat" and have also modified service-proxies.module.ts like your commit.

@ismcagdas

Thanks. I have made the necessary changes. The solution builds and i can go to localhost:4200 and i see the spinner but then nothing happens. In chrome developer the last successful request is to GetCurrentLoginInformations, then it hangs at <a class="postlink" href="ws://localhost:4200/sockjs-node/450/g15t5ui3/websocket">ws://localhost:4200/sockjs-node/450/g15t5ui3/websocket</a>.

@ismcagdas , thanks. I have upgraded abp-ng2-module to v2. I get some errors.

In root.modul.ts "ABP_HTTP_PROVIDER" is not found when

import { AbpModule , ABP_HTTP_PROVIDER } from '@abp/abp.module';
providers: [
        ABP_HTTP_PROVIDER,

In tenant-settings.component.ts and change-profile-picture-modal.component.ts should i replace 7

import { IAjaxResponse } from '@abp/abpHttp'; with
import { HttpInterceptor } from '@angular/common/http';

and replace const ajaxResponse = <IAjaxResponse>JSON.parse(response); with HttpInterceptor?

Can I download the above files somewhere or can past what to change. I am unsure what to change.

Thanks, Henrik

Showing 1 to 10 of 15 entries