Skip to content

Conversation

@chippison
Copy link
Contributor

Description

Dev-19785

This PR is to fix the regression we had when we tried to redirect invalid session errors to login page (https://innocraft.atlassian.net/browse/UX-305).

We only want to force the redirect when we actually detect an invalid session issue and not to redirect ALL NoAccessExceptions. We will retain the original behaviour of NoAccessException for ajax calls to just fail silently and just returning the error message.

Testing:

NOTE: The only way I could test this fix was to try to do some datatable actions that will trigger saving of table preference. This would show errors and will refresh the page with no change in the sorting when the issue is not fixed. This happened when the regression was introduced and was noticeable on our Demo Site.
If the issue is fixed, the datatable should not give any errors and sorting should work (although it will not be saved because it is 'anonymous' user).

This should still error and redirect if a session becomes invalid (session expires or is deleted). I was able to test this by logging in as an admin to my local site. Then deleted my session (or changing the expire date to something in the past), then clicking on a new category/page or even just trying to sort the datatable.

Checklist

  • [✔] I have understood, reviewed, and tested all AI outputs before use
  • [✔] All AI instructions respect security, IP, and privacy rules

Review

@chippison chippison requested a review from a team December 23, 2025 03:46
@chippison chippison marked this pull request as ready for review December 23, 2025 03:46
@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

If you don't want this PR to be closed automatically in 28 days then you need to assign the label 'Do not close'.

@github-actions github-actions bot added the Stale The label used by the Close Stale Issues action label Jan 7, 2026
@chippison chippison added the Do not close PRs with this label won't be marked as stale by the Close Stale Issues action label Jan 7, 2026
@sgiehl sgiehl removed Do not close PRs with this label won't be marked as stale by the Close Stale Issues action Stale The label used by the Close Stale Issues action labels Jan 15, 2026
@sgiehl sgiehl force-pushed the fix-ajax-no-access-error branch from b8ea408 to b795c1a Compare January 15, 2026 08:27
@sgiehl sgiehl added the Regression Indicates a feature used to work in a certain way but it no longer does even though it should. label Jan 15, 2026
@sgiehl sgiehl added this to the 5.7.0 milestone Jan 15, 2026
Copy link
Member

@sgiehl sgiehl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally the changes might work, but are far away from "perfect". When working on such legacy parts it's important to also challenge the current implementation and consider fundamental changes instead of just relying on existing behavior.

I've used codex to create some code upon your changes, to implement a proper session timeout handling that is less error prone and improve the code overall a bit:

  • Relying on 401 status code might still cause problems, as plugins might send 401 status codes, that should not result in reloads. I've update that to use a custom header instead.
  • Setting a global Access state when the session has a real timeout makes the whole handling a lot more accurate, than trying to detect that based on the referrer.
  • As the more accurate timeout detection would prevent the session timeout message to be displayed after page reload this state is temporarily stored in a cookie
  • Properly differ between 401 (fully unauthorized requests) and 403 (authorized requests without permission)

See fix-ajax-no-access-error...fix-session-timeout-handling for the proposed changes.

Note: This was built upon my rough ideas. I haven't done a proper review or deeper testing yet

const loginForm = await page.waitForSelector('#login_form_login', { visible: true });
expect(loginForm).to.be.ok;

const expectedText = 'Error: Your session has expired due to inactivity. Please log in to continue.';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically that might already have been incorrect before. Removing cookies isn't really a session timeout. It's just an unauthenticated request, that Matomo incorrectly reports as session timeout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written here before. This can't be considered a session timeout. A missing cookie needs to be interpreted as unauthorized.

@chippison
Copy link
Contributor Author

Generally the changes might work, but are far away from "perfect". When working on such legacy parts it's important to also challenge the current implementation and consider fundamental changes instead of just relying on existing behavior.

I've used codex to create some code upon your changes, to implement a proper session timeout handling that is less error prone and improve the code overall a bit:

  • Relying on 401 status code might still cause problems, as plugins might send 401 status codes, that should not result in reloads. I've update that to use a custom header instead.
  • Setting a global Access state when the session has a real timeout makes the whole handling a lot more accurate, than trying to detect that based on the referrer.
  • As the more accurate timeout detection would prevent the session timeout message to be displayed after page reload this state is temporarily stored in a cookie
  • Properly differ between 401 (fully unauthorized requests) and 403 (authorized requests without permission)

See fix-ajax-no-access-error...fix-session-timeout-handling for the proposed changes.

Note: This was built upon my rough ideas. I haven't done a proper review or deeper testing yet

Thanks for your input on this @sgiehl.
It was really helpful to point me in the right direction.
I have merged your changes(fix-session-timeout-handling) to this PR.
I also added some code to make it work if anonymous users are allowed to view the site.
Hopefully my changes are all good. 😁

Cheers

@chippison chippison requested a review from sgiehl January 16, 2026 02:58
Copy link
Member

@sgiehl sgiehl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments. I don't think the changes you made regarding the anonymous user are needed at all. The anonymous user does not need to log in. So even if the session times out, that user would not be redirected to the login form if view access to the website is granted.

const result = new Promise<T | ErrorResponse>((resolve, reject) => {
this.requestHandle!.then((data: unknown) => {
this.requestHandle!.then((data: unknown, _textStatus: string, xhr: jqXHR) => {
const isInApp = !document.querySelector('#login_form');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see a good reason why this would be needed. If the request returns a 200, there was no problem. Even if the session might have timed out - e.g. for anonymous - it was no problem. Otherwise it would result in a 40X.

}

it('should display password reset form when forgot password link clicked', async function () {
xit('should display password reset form when forgot password link clicked', async function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are those changes good for?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That change looks incorrect. Why should it show the session message?

const loginForm = await page.waitForSelector('#login_form_login', { visible: true });
expect(loginForm).to.be.ok;

const expectedText = 'Error: Your session has expired due to inactivity. Please log in to continue.';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written here before. This can't be considered a session timeout. A missing cookie needs to be interpreted as unauthorized.

{
$expirationTime = $sessionFingerprint->getExpirationTime();
if (empty($expirationTime)) {
$this->sessionExpired = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line causes the test changes and should be reverted. From a technical perspective a session without an expiration time should not occur. It happens if there is no session at all. So the very first call to Matomo (without any cookie) is now always marked as timed out. Same happens for the tests using FakeAccess, as they are not using the SessionAuth.

@sgiehl sgiehl force-pushed the fix-ajax-no-access-error branch 3 times, most recently from 345e17b to ebc08c1 Compare January 20, 2026 09:04
@sgiehl sgiehl force-pushed the fix-ajax-no-access-error branch from ebc08c1 to 07714c8 Compare January 20, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Regression Indicates a feature used to work in a certain way but it no longer does even though it should.

Development

Successfully merging this pull request may close these issues.

3 participants