Base solution for your next web application

Activities of "ips-ad"

Hi, I can finally confirm that with upgrading to Zero 12.4.2 these old sessions are being cleared.

Hello @ismcagdas, How can we resolve this now? I think it's something that should be fixed properly in the next Zero version, as the current entities created by Power Tools are clearly buggy. I see the following issues:

  • The current filtering implementation can lead to wrong results (especially on large databases where filter operations have varying durations)
  • Each additional character launches an additional call on GetAll without cancelling previous one(s) that still might be running (easily leads to performance issues)
  • Spinner is hiding the filterText input, but user can still continue to type into it
  • Audit log gets spammed with GetAll entries

I think this calls for implementing task cancellation in the backend and some UI optimization to make look and feel consistent?

Any news on this please..? We need to fix this soon for our customers. Thanks again!

Hi, We're currently using version 12.4.2 (.net, Angular) and created (and also recreated/updated) a couple of entites according to the current output from Power Tools (version 4.2.0).

It has the feature where the user gets almost instant search results while typing into the search field, almost known as state of the art today of course. It's realized like this with ngModelChange in the html part:

<div class="input-group mb-3">
                                        <input
                                            [(ngModel)]="filterText"
                                            (ngModelChange)="getETests()"
                                            name="filterText"
                                            autoFocus
                                            type="text"
                                            class="form-control"
                                            [placeholder]="l('SearchWithThreeDot')"
                                        />
                                        <button class="btn btn-primary" type="submit" (click)="getETests()">
                                            <i class="flaticon-search-1"></i>
                                        </button>
                                    </div>

...and like this in the corresponding ts part:

getETests(event?: LazyLoadEvent) {
        if (this.primengTableHelper.shouldResetPaging(event)) {
            this.paginator.changePage(0);
            if (this.primengTableHelper.records && this.primengTableHelper.records.length > 0) {
                return;
            }
        }

        this.primengTableHelper.showLoadingIndicator();

        this._eTestsServiceProxy
            .getAll(
                this.filterText,
                this.titleFilter,
                this.primengTableHelper.getSorting(this.dataTable),
                this.primengTableHelper.getSkipCount(this.paginator, event),
                this.primengTableHelper.getMaxResultCount(this.paginator, event),
            )
            .subscribe((result) => {
                this.primengTableHelper.totalRecordsCount = result.totalCount;
                this.primengTableHelper.records = result.items;
                this.primengTableHelper.hideLoadingIndicator();
            });
    }

This causes two kinds of issues with entites having a couple more properties and especially thousands of records:

  • Every single character typed in by the user fires a call to the AppService instantly, there is no debouncing at all (ok, this is good for user experience, just creating much more load)
  • The really problematic is that the subscribed results may arrive in different order than the calls were sent (as different search inputs may take more or less time to be answered from the database)

How can we fix that, especially the latter? It's really a problem when a user e.g. types "backend" into the search field, but ends up with results for "back pain", too, because the result for "back" came in last for whatever reason...

Thanks!

It's an updated solution but I think we have overwritten all those "basic" parts with the files of the brand new 12.4.2 project. I'll check when there is some time left for this!

Thanks for your reply. I had a look at this documentation in the past already, but unfortunately that won't work in this case, at least as far as I understand.

The essential part is that the Onlyoffice server must be able to make calls to our Zero endpoint. Onlyoffice provides a token with that, signed with a security key that both sides share. We have no chance to change anything on the Onlyoffice side, except for the security key of course. We don't have user context with these calls, but that's not an issue because we know the record id of the concerned document in our database. So basically we need a Zero endpoint accepting the calls from Onlyoffice and verifying the token against a shared security key as they are.

It worked fine in Zero 8.1 with adding this to AuthConfigurer.cs (and the related settings in appconfig.json of course):

[...]
        public static void Configure(IServiceCollection services, IConfiguration configuration)
        {
[...]
            if (bool.Parse(configuration["OnlyOffice:IsEnabled"]))
            {
                var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(configuration["OnlyOffice:CallBackSecret"]));
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer("OnlyOfficeBearerToken",
                    options =>
                    {
                        options.TokenValidationParameters = new TokenValidationParameters()
                        {
                            ValidateAudience = false,
                            ValidateActor = false,
                            ValidateIssuer = false,
                            ValidateIssuerSigningKey = true,
                            ValidateLifetime = true,
                            IssuerSigningKey = securityKey //the security key shared with Onlyoffice
                        };
                        options.RequireHttpsMetadata = false;
                        options.SaveToken = true;
                    });
            }
[...]

...and creating a controller for the Zero API endpoint like this:

public class DocumentController : XXXControllerBase
[...]
        [DisableAuditing]
        [Authorize(AuthenticationSchemes = "OnlyOfficeBearerToken")]
        [HttpPost]
        [RequestSizeLimit(100_000_000)]
        public async Task OnlyOfficeCallbackHandler()
        {
            Claim payloadclaim = this.HttpContext.User.FindFirst(c => c.Type == "payload");
            string body = payloadclaim?.Value;
            //proceed body etc...
[...]

With Zero 12.4.2, it doesn't accept the token from Onlyoffice anymore, as mentioned initially:

Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10503: Signature validation failed. Token does not have a kid. Keys tried: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Number of keys in TokenValidationParameters: '1'. 
Number of keys in Configuration: '0'. 
Exceptions caught:
 '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10503 for details.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignatureAndIssuerSecurityKey(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateJWS(String token, TokenValidationParameters validationParameters, BaseConfiguration currentConfiguration, SecurityToken& signatureValidatedToken, ExceptionDispatchInfo& exceptionThrown)
--- End of stack trace from previous location ---

I hope that I could explain it in such a way that it makes sense to you :-)

Hi @ismcagdas

When calling/opening Onlyoffice, we have to build the token like this from the Angular frontend:

let oHeader = { alg: 'HS256', typ: 'JWT' };
let sHeader = JSON.stringify(oHeader);
let sPayload = JSON.stringify(config);
let sJWT = KJUR.jws.JWS.sign('HS256', sHeader, sPayload, this.officeCredentials);

This token was and is still fine, it's accepted by Onlyoffice with the same officeCredentials configured. Onlyoffice is then downloading the document from Zero backend via /File/DownloadTempFile/....

Onlyoffice then starts to make calls back to the Zero backend, where it builds the token in the same way (as described here https://api.onlyoffice.com/editors/security). We have a custom controller/endpoint class which handles these callbacks for status handling and saving the document back to our DB on closing. This is the part that worked with 8.1, but doesn't with 12.4.2, as described in my original post:

public class DocumentController : XXXControllerBase
[...]
        [DisableAuditing]
        [Authorize(AuthenticationSchemes = "OnlyOfficeBearerToken")]
        [HttpPost]
        [RequestSizeLimit(100_000_000)]
        public async Task OnlyOfficeCallbackHandler()
        {
            Claim payloadclaim = this.HttpContext.User.FindFirst(c => c.Type == "payload");
            string body = payloadclaim?.Value;
            //proceed body etc...

Hi, We're currently updating our project from Zero 8.1 (Angular, .net) to 12.4.2. Our project has an integration for Onlyoffice, a web based solution to edit office documents. Opening the editor still works fine, i.e. we provide it with basically a FileDto and a set of config parameters, then it downloads the document from our Zero backend and displays it for editing.

The issue now is when Onlyoffice needs to write an edited document back to our Zero backend. It authenticates via JWT with a pre-shared secret, therefor we extended AuthConfigurer.cs with the last if-block (configuration["OnlyOffice:IsEnabled"]):

    public static class AuthConfigurer
    {
        public static void Configure(IServiceCollection services, IConfiguration configuration)
        {
            var authenticationBuilder = services.AddAuthentication();
            
            if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
            {
                authenticationBuilder.AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        // The signing key must match!
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),

                        // Validate the JWT Issuer (iss) claim
                        ValidateIssuer = true,
                        ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],

                        // Validate the JWT Audience (aud) claim
                        ValidateAudience = true,
                        ValidAudience = configuration["Authentication:JwtBearer:Audience"],

                        // Validate the token expiry
                        ValidateLifetime = true,

                        // If you want to allow a certain amount of clock drift, set that here
                        ClockSkew = TimeSpan.Zero
                    };

                    options.SecurityTokenValidators.Clear();
                    options.SecurityTokenValidators.Add(new IPSwebJwtSecurityTokenHandler());

                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = QueryStringTokenResolver
                    };
                });
            }

            if (bool.Parse(configuration["IdentityServer:IsEnabled"]))
            {
                IdentityModelEventSource.ShowPII = true;
                authenticationBuilder.AddIdentityServerAuthentication("IdentityBearer", options =>
                {
                    options.Authority = configuration["IdentityServer:Authority"];
                    options.ApiName = configuration["IdentityServer:ApiName"];
                    options.ApiSecret = configuration["IdentityServer:ApiSecret"];
                    options.RequireHttpsMetadata = false;
                });
            }

            if (bool.Parse(configuration["OnlyOffice:IsEnabled"]))
            {
                var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(configuration["OnlyOffice:CallBackSecret"]));
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer("OnlyOfficeBearerToken",
                    options =>
                    {
                        options.TokenValidationParameters = new TokenValidationParameters()
                        {
                            ValidateAudience = false,
                            ValidateActor = false,
                            ValidateIssuer = false,
                            ValidateIssuerSigningKey = true,
                            ValidateLifetime = true,
                            IssuerSigningKey = securityKey
                        };
                        options.RequireHttpsMetadata = false;
                        options.SaveToken = true;
                    });
            }
        }

Then, a controller in the Zero Backend takes care of the calls returning from Onlyoffice like this:

        [DisableAuditing]
        [Authorize(AuthenticationSchemes = "OnlyOfficeBearerToken")]
        [HttpPost]
        [RequestSizeLimit(100_000_000)]
        public async Task<ActionResult> OnlyOfficeCallbackHandler()
        {
            Claim payloadclaim = this.HttpContext.User.FindFirst(c => c.Type == "payload");
            string body = payloadclaim?.Value;
            //proceed body etc...

This worked well for many years, but now is broken since the update to 12.4.2, Onlyoffice giving the following errors:

Error: Error response: statusCode:302; headers:{"server":"nginx/1.21.1","date":"Mon, 01 Apr 2024 08:43:22 GMT","content-length":"0","connection":"keep-alive","location":"/Error?statusCode=401","www-authenticate":"Bearer error=\"invalid_token\", error_description=\"The signature key was not found; The signature key was not found\""

...and Zero giving the following errors:

Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10503: Signature validation failed. Token does not have a kid. Keys tried: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Number of keys in TokenValidationParameters: '1'. 
Number of keys in Configuration: '0'. 
Exceptions caught:
 '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10503 for details.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignatureAndIssuerSecurityKey(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateJWS(String token, TokenValidationParameters validationParameters, BaseConfiguration currentConfiguration, SecurityToken& signatureValidatedToken, ExceptionDispatchInfo& exceptionThrown)
--- End of stack trace from previous location ---

We tried many things, like adding a kid, but it looks just like it's not possible anymore to extend AuthConfigurer.cs like before. Is there an easy way to fix it, or do we have to switch the approach?

This is how Onlyoffice works: https://api.onlyoffice.com/editors/save

Thanks for your help!

Hi, We're currently working on upgrading our project from Zero 8 to 12.4.2, .NET Core & Angular. Most of the work should be done and the project is running again in dev environment, i.e. frontend and backend up and running.

But after creating and deploying Docker images to our testing environment, we're getting the following error:

This happens after logging in (login page showed up properly), and the spinner keeps spinning forever. The backend seems to be running fine so far, at least we're able to log in as a tenant and query some app services through the swagger interface.

It's hard to find information about what NG0202 means, so at the moment I'm not even sure where to start fixing it. Any help is appreciated!

Hi, Thanks for this information. Updating will take a while - just to confirm, 12.4.2 contains a fix regarding idle connections that 12.1.0 doesn't contain? Because 12.1.0 also had idle connections "ClientRead" older than 300 seconds, even shortly after startup and without any load at all. Those should get closed too, right?

Showing 1 to 10 of 11 entries