Fix frontend not redirecting on 401 (#1244)

- Ensures need-login event bubbles until handled
- Redirects on 401 from /refresh endpoint
- Go to previous URL upon login, rather than always to home page
- Shows accurate login notification (rather than less precise "couldn't retrieve org" or similar message)
This commit is contained in:
sua yoo 2023-10-04 00:17:22 -07:00 committed by GitHub
parent 38efeccc25
commit f2261bcb34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 15 deletions

View File

@ -13,7 +13,7 @@ import type { NotifyEvent, NavigateEvent } from "./utils/LiteElement";
import LiteElement, { html } from "./utils/LiteElement";
import APIRouter from "./utils/APIRouter";
import AuthService, { AuthState } from "./utils/AuthService";
import type { LoggedInEvent } from "./utils/AuthService";
import type { LoggedInEvent, NeedLoginEvent } from "./utils/AuthService";
import type { ViewState } from "./utils/APIRouter";
import type { CurrentUser, UserOrg } from "./types/user";
import type { AuthStorageEventData } from "./utils/AuthService";
@ -107,6 +107,7 @@ export class App extends LiteElement {
}
super.connectedCallback();
window.addEventListener("need-login", this.onNeedLogin);
window.addEventListener("popstate", () => {
this.syncViewState();
});
@ -585,7 +586,8 @@ export class App extends LiteElement {
@logged-in=${this.onLoggedIn}
.authState=${this.authService.authState}
.viewState=${this.viewState}
redirectUrl=${this.viewState.params.redirectUrl}
redirectUrl=${this.viewState.params.redirectUrl ||
this.viewState.data?.redirectUrl}
></btrix-log-in>`;
case "resetPassword":
@ -616,7 +618,6 @@ export class App extends LiteElement {
return html`<btrix-orgs
class="w-full md:bg-neutral-50"
@navigate="${this.onNavigateTo}"
@need-login="${this.onNeedLogin}"
.authState="${this.authService.authState}"
.userInfo="${this.userInfo}"
></btrix-orgs>`;
@ -632,7 +633,6 @@ export class App extends LiteElement {
return html`<btrix-org
class="w-full"
@navigate=${this.onNavigateTo}
@need-login=${this.onNeedLogin}
@update-user-info=${(e: CustomEvent) => {
e.stopPropagation();
this.updateUserInfo();
@ -653,7 +653,6 @@ export class App extends LiteElement {
class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border"
@navigate="${this.onNavigateTo}"
@logged-in=${this.onLoggedIn}
@need-login="${this.onNeedLogin}"
.authState="${this.authService.authState}"
.userInfo="${this.userInfo}"
></btrix-account-settings>`;
@ -665,7 +664,6 @@ export class App extends LiteElement {
class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border"
@navigate="${this.onNavigateTo}"
@logged-in=${this.onLoggedIn}
@need-login="${this.onNeedLogin}"
.authState="${this.authService.authState}"
.userInfo="${this.userInfo}"
></btrix-users-invite>`;
@ -684,7 +682,6 @@ export class App extends LiteElement {
return html`<btrix-crawls
class="w-full"
@navigate=${this.onNavigateTo}
@need-login=${this.onNeedLogin}
@notify=${this.onNotify}
.authState=${this.authService.authState}
crawlId=${this.viewState.params.crawlId}
@ -781,7 +778,7 @@ export class App extends LiteElement {
this.clearUser();
if (redirect) {
this.navigate("/log-in");
this.navigate(ROUTES.login);
}
}
@ -805,10 +802,24 @@ export class App extends LiteElement {
this.updateUserInfo();
}
onNeedLogin() {
onNeedLogin = (e: Event) => {
e.stopPropagation();
this.clearUser();
this.navigate(ROUTES.login);
}
const redirectUrl = (e as NeedLoginEvent).detail?.redirectUrl;
this.navigate(ROUTES.login, {
redirectUrl,
});
this.onNotify(
new CustomEvent("notify", {
detail: {
message: msg("Please log in to continue."),
variant: "warning" as any,
icon: "exclamation-triangle",
},
})
);
};
onNavigateTo(event: NavigateEvent) {
event.stopPropagation();

View File

@ -124,7 +124,7 @@ export class Org extends LiteElement {
}
async willUpdate(changedProperties: Map<string, any>) {
if (changedProperties.has("orgId") && this.orgId) {
if (changedProperties.has("orgId") && this.orgId && this.authState) {
try {
this.org = await this.getOrg(this.orgId);
this.checkStorageQuota();

View File

@ -1,3 +1,4 @@
import { ROUTES } from "../routes";
import { APIError } from "./api";
export type Auth = {
@ -27,6 +28,14 @@ export interface LoggedInEvent<T = LoggedInEventDetail> extends CustomEvent {
readonly detail: T;
}
export interface NeedLoginEvent extends CustomEvent {
readonly bubbles: boolean;
readonly composed: boolean;
readonly detail: {
redirectUrl?: string;
};
}
type AuthRequestEventData = {
name: "requesting_auth";
};
@ -51,6 +60,7 @@ export default class AuthService {
static storageKey = "btrix.auth";
static unsupportedAuthErrorCode = "UNSUPPORTED_AUTH_TYPE";
static loggedInEvent = "logged-in";
static needLoginEvent = "need-login";
static broadcastChannel = new BroadcastChannel(AuthService.storageKey);
static storage = {
@ -85,6 +95,14 @@ export default class AuthService {
return new CustomEvent(AuthService.loggedInEvent, { detail });
};
static createNeedLoginEvent = (redirectUrl?: string): NeedLoginEvent => {
return new CustomEvent(AuthService.needLoginEvent, {
bubbles: true,
composed: true,
detail: { redirectUrl },
});
};
static async login({
email,
password,
@ -307,6 +325,14 @@ export default class AuthService {
}, FRESHNESS_TIMER_INTERVAL);
} catch (e) {
console.debug(e);
this.logout();
const { pathname, search, hash } = window.location;
const redirectUrl =
pathname !== ROUTES.login && pathname !== ROUTES.home
? `${pathname}${search}${hash}`
: "";
window.dispatchEvent(AuthService.createNeedLoginEvent(redirectUrl));
}
}
}

View File

@ -3,6 +3,7 @@ import type { TemplateResult } from "lit";
import { msg } from "@lit/localize";
import type { Auth } from "../utils/AuthService";
import AuthService from "../utils/AuthService";
import { APIError } from "./api";
export interface NavigateEvent extends CustomEvent {
@ -147,7 +148,7 @@ export default class LiteElement extends LitElement {
switch (resp.status) {
case 401: {
this.dispatchEvent(new CustomEvent("need-login"));
this.dispatchEvent(AuthService.createNeedLoginEvent());
errorMessage = msg("Need login");
break;
}

View File

@ -1,6 +1,6 @@
import LiteElement from "../utils/LiteElement";
import type { AuthState } from "../utils/AuthService";
import type { CurrentUser } from "../types/user";
import AuthService from "../utils/AuthService";
/**
* Block rendering and dispatch event if user is not logged in
@ -27,7 +27,11 @@ export function needLogin<T extends { new (...args: any[]): LiteElement }>(
if (this.authState) {
super.update(changedProperties);
} else {
this.dispatchEvent(new CustomEvent("need-login"));
this.dispatchEvent(
AuthService.createNeedLoginEvent(
`${window.location.pathname}${window.location.search}${window.location.hash}`
)
);
}
}
};