This is an automated email from the ASF dual-hosted git repository.
xuang7 pushed a commit to branch release/v1.2
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/release/v1.2 by this push:
new c9909b692d fix(frontend): stop forum login from looping when Flarum
auth fails (#5311) [release/v1.2 backport] (#5615)
c9909b692d is described below
commit c9909b692d8926a4e8490a1b22d553d65d99318e
Author: Matthew B. <[email protected]>
AuthorDate: Wed Jun 10 22:12:45 2026 -0700
fix(frontend): stop forum login from looping when Flarum auth fails (#5311)
[release/v1.2 backport] (#5615)
### What changes were proposed in this PR?
Backport of #5311 ("fix(frontend): stop forum login from looping when
Flarum auth fails") to `release/v1.2`.
Applies as a clean `git cherry-pick -x` of the merged squash commit
`af664995`, no conflicts and no prerequisite chain. Frontend-only: 2
files changed, 75 insertions, 4 deletions. The introduced diff is
identical to the original PR (only blob-index hashes in the patch header
differ).
For the full change description see #5311. In brief:
`DashboardComponent.forumLogin` recursed `auth -> register -> auth`
without bound when Flarum `auth()` kept failing with a non-404/500
status. This adds an `attemptRegister` flag so registration is attempted
at most once (the post-register call passes `forumLogin(false)`, which
then falls through to `displayForum = false`), and adds an error handler
on the `register()` subscription so a failed registration hides the
forum instead of being silently dropped.
### Any related issues, documentation, discussions?
Closes: #5618 (backport tracking task). Backports #5311 (which closes
#5310 on `main`). Requested by @xuang7 for the v1.2 release; the PR was
merged before the `release/v1.2` label existed, so auto-backport did not
trigger.
### How was this PR tested?
This is a backport with no changes beyond the single cherry-picked
commit, so it relies on the existing tests carried over from #5311
(added cases in `dashboard.component.spec.ts` covering the bounded retry
and the failed-registration path). Run them with `yarn test
--include='**/dashboard.component.spec.ts'` from `frontend/`.
Backport fidelity was verified locally: the diff introduced by the
cherry-pick is byte-identical to the original #5311 diff. The
`dashboard.component.ts` file is not byte-identical to `main@af664995`
only because `release/v1.2` lacks the unrelated footer change
(#5153/#5444) that renamed `gitCommitHash`/`Version.raw` to
`buildNumber`/`Version.buildNumber`; that line is outside the
`forumLogin` fix and is left untouched here.
Full compile and unit-test runs are left to CI.
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)
Signed-off-by: Matthew B. <[email protected]>
---
.../component/dashboard.component.spec.ts | 67 +++++++++++++++++++++-
.../app/dashboard/component/dashboard.component.ts | 12 +++-
2 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/frontend/src/app/dashboard/component/dashboard.component.spec.ts
b/frontend/src/app/dashboard/component/dashboard.component.spec.ts
index 29ed8426fe..a53244b3cd 100644
--- a/frontend/src/app/dashboard/component/dashboard.component.spec.ts
+++ b/frontend/src/app/dashboard/component/dashboard.component.spec.ts
@@ -21,7 +21,7 @@ import { ComponentFixture, TestBed } from
"@angular/core/testing";
import { DashboardComponent } from "./dashboard.component";
import { ChangeDetectorRef, EventEmitter, NgZone } from "@angular/core";
import { By } from "@angular/platform-browser";
-import { EMPTY, of } from "rxjs";
+import { EMPTY, of, throwError } from "rxjs";
import { UserService } from "../../common/service/user/user.service";
import { FlarumService } from "../service/user/flarum/flarum.service";
@@ -178,6 +178,71 @@ describe("DashboardComponent", () => {
expect(fixture.debugElement.query(By.css("#powered-by"))).toBeTruthy();
});
+ describe("forumLogin", () => {
+ const clearForumCookie = () =>
+ (document.cookie = "flarum_remember=; expires=Thu, 01 Jan 1970 00:00:00
GMT; path=/");
+
+ beforeEach(() => {
+ clearForumCookie();
+ (userServiceMock.isLogin as Mock).mockReturnValue(true);
+ component.isLogin = true;
+ component.displayForum = true;
+ (flarumServiceMock.auth as Mock).mockClear();
+ (flarumServiceMock.register as Mock).mockClear();
+ });
+
+ afterEach(() => clearForumCookie());
+
+ it("stores the flarum_remember cookie on successful auth and does not
register", () => {
+ (flarumServiceMock.auth as Mock).mockReturnValue(of({ token: "tok123"
}));
+
+ component.forumLogin();
+
+ expect(document.cookie).toContain("flarum_remember=tok123");
+ expect(flarumServiceMock.register).not.toHaveBeenCalled();
+ });
+
+ it("hides the forum and does not register when auth fails with 404/500",
() => {
+ (flarumServiceMock.auth as Mock).mockReturnValue(throwError(() => ({
status: 404 })));
+
+ component.forumLogin();
+
+ expect(component.displayForum).toBe(false);
+ expect(flarumServiceMock.register).not.toHaveBeenCalled();
+ });
+
+ it("registers at most once and stops when auth keeps failing (no infinite
loop)", () => {
+ (flarumServiceMock.auth as Mock).mockReturnValue(throwError(() => ({
status: 401 })));
+ (flarumServiceMock.register as Mock).mockReturnValue(of(null));
+
+ component.forumLogin();
+
+ // auth -> register -> auth -> stop: register fires once, auth twice,
then it terminates.
+ expect(flarumServiceMock.register).toHaveBeenCalledTimes(1);
+ expect(flarumServiceMock.auth).toHaveBeenCalledTimes(2);
+ expect(component.displayForum).toBe(false);
+ });
+
+ it("hides the forum when registration fails", () => {
+ (flarumServiceMock.auth as Mock).mockReturnValue(throwError(() => ({
status: 401 })));
+ (flarumServiceMock.register as Mock).mockReturnValue(throwError(() => ({
status: 500 })));
+
+ component.forumLogin();
+
+ expect(flarumServiceMock.register).toHaveBeenCalledTimes(1);
+ expect(component.displayForum).toBe(false);
+ });
+
+ it("does nothing when a flarum_remember cookie is already present", () => {
+ document.cookie = "flarum_remember=existing;path=/";
+
+ component.forumLogin();
+
+ expect(flarumServiceMock.auth).not.toHaveBeenCalled();
+ expect(flarumServiceMock.register).not.toHaveBeenCalled();
+ });
+ });
+
it("should hide the navbar on workflow workspace routes", () => {
expect(component.isNavbarEnabled("/user/workflow/42")).toBe(false);
expect(component.isNavbarEnabled("/user/workflow")).toBe(true);
diff --git a/frontend/src/app/dashboard/component/dashboard.component.ts
b/frontend/src/app/dashboard/component/dashboard.component.ts
index 8dc5d601dd..cec80766fc 100644
--- a/frontend/src/app/dashboard/component/dashboard.component.ts
+++ b/frontend/src/app/dashboard/component/dashboard.component.ts
@@ -204,7 +204,7 @@ export class DashboardComponent implements OnInit {
});
}
- forumLogin() {
+ forumLogin(attemptRegister: boolean = true) {
if (!document.cookie.includes("flarum_remember") && this.isLogin) {
this.flarumService
.auth()
@@ -214,13 +214,19 @@ export class DashboardComponent implements OnInit {
document.cookie = `flarum_remember=${response.token};path=/`;
},
error: (err: unknown) => {
- if ([404, 500].includes((err as HttpErrorResponse).status)) {
+ // Stop retrying on a missing/broken forum service, or once we have
+ // already attempted a registration, to avoid an infinite
+ // auth -> register -> auth loop when auth keeps failing.
+ if ([404, 500].includes((err as HttpErrorResponse).status) ||
!attemptRegister) {
this.displayForum = false;
} else {
this.flarumService
.register()
.pipe(untilDestroyed(this))
- .subscribe(() => this.forumLogin());
+ .subscribe({
+ next: () => this.forumLogin(false),
+ error: () => (this.displayForum = false),
+ });
}
},
});