From 2383fbcd704461159b39b5aca43ff34635e3db07 Mon Sep 17 00:00:00 2001
From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
Date: Mon, 25 Nov 2024 17:32:02 +0100
Subject: [PATCH] V15: Show a loader during the login procedures rather than
oddly styled content (#17618)
* feat: show only a loader on a default login flow
if the flow fails, the app-error component will be shown, or if the flow is initialised inside a popup (i.e. the session was lost)
* fix: hasOwnOpener did not recognize the local vite url as its own pathname
it should work better by checking the `startsWith` comparing the pathname, and besides, it seems to work better for the understanding of the function to inverse the true/false check
* chore: adjust imports
* chore: formatting
---
.../src/apps/app/app-oauth.element.ts | 45 +++++++++++++++++++
.../src/apps/app/app.element.ts | 31 +++++++------
.../utils/path/has-own-opener.function.ts | 6 +--
3 files changed, 63 insertions(+), 19 deletions(-)
create mode 100644 src/Umbraco.Web.UI.Client/src/apps/app/app-oauth.element.ts
diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app-oauth.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app-oauth.element.ts
new file mode 100644
index 0000000000..658e390d1b
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/apps/app/app-oauth.element.ts
@@ -0,0 +1,45 @@
+import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
+import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
+
+import './app-error.element.js';
+
+/**
+ * A full page error element that can be used either solo or for instance as the error 500 page and BootFailed
+ */
+@customElement('umb-app-oauth')
+export class UmbAppOauthElement extends UmbLitElement {
+
+ /**
+ * Set to true if the login failed. A message will be shown instead of the loader.
+ * @attr
+ */
+ @property({ type: Boolean })
+ failure = false;
+
+ override render() {
+ // If we have a message, we show the error page
+ // this is most likely happening inside a popup
+ if (this.failure) {
+ return html``;
+ }
+
+ // If we don't have a message, we show the loader, this is most likely happening in the main app
+ // for the normal login flow
+ return html`
+
+
+
+ `;
+ }
+}
+
+export default UmbAppOauthElement;
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'umb-app-oauth': UmbAppOauthElement;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts
index d48bbbd57d..57bd044230 100644
--- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts
@@ -20,6 +20,9 @@ import {
} from '@umbraco-cms/backoffice/extension-registry';
import { filter, first, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { hasOwnOpener, retrieveStoredPath } from '@umbraco-cms/backoffice/utils';
+import type { UmbAppOauthElement } from './app-oauth.element.js';
+
+import './app-oauth.element.js';
@customElement('umb-app')
export class UmbAppElement extends UmbLitElement {
@@ -60,31 +63,27 @@ export class UmbAppElement extends UmbLitElement {
},
{
path: 'oauth_complete',
- component: () => import('./app-error.element.js'),
+ component: () => import('./app-oauth.element.js'),
setup: (component) => {
if (!this.#authContext) {
- throw new Error('[Fatal] Auth context is not available');
+ (component as UmbAppOauthElement).failure = true;
+ console.error('[Fatal] Auth context is not available');
+ return;
}
const searchParams = new URLSearchParams(window.location.search);
const hasCode = searchParams.has('code');
- (component as UmbAppErrorElement).hideBackButton = true;
- (component as UmbAppErrorElement).errorHeadline = this.localize.term('general_login');
+ if (!hasCode) {
+ (component as UmbAppOauthElement).failure = true;
+ console.error('[Fatal] No code in query parameters');
+ return;
+ }
- // If there is an opener, we are in a popup window, and we should show a different message
- // than if we are in the main window. If we are in the main window, we should redirect to the root.
+ // If we are in the main window (i.e. no opener), we should redirect to the root after the authorization request is completed.
// The authorization request will be completed in the active window (main or popup) and the authorization signal will be sent.
// If we are in a popup window, the storage event in UmbAuthContext will catch the signal and close the window.
// If we are in the main window, the signal will be caught right here and the user will be redirected to the root.
- if (hasOwnOpener(this.backofficePath)) {
- (component as UmbAppErrorElement).errorMessage = hasCode
- ? this.localize.term('errors_externalLoginSuccess')
- : this.localize.term('errors_externalLoginFailed');
- } else {
- (component as UmbAppErrorElement).errorMessage = hasCode
- ? this.localize.term('errors_externalLoginRedirectSuccess')
- : this.localize.term('errors_externalLoginFailed');
-
+ if (!hasOwnOpener(this.backofficePath)) {
this.observe(this.#authContext.authorizationSignal, () => {
// Redirect to the saved state or root
const url = retrieveStoredPath();
@@ -99,7 +98,7 @@ export class UmbAppElement extends UmbLitElement {
}
// Complete the authorization request, which will send the authorization signal
- this.#authContext.completeAuthorizationRequest();
+ this.#authContext.completeAuthorizationRequest().catch(() => undefined);
},
},
{
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/has-own-opener.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/has-own-opener.function.ts
index 72e72090af..3fda364240 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/has-own-opener.function.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/has-own-opener.function.ts
@@ -22,11 +22,11 @@ export function hasOwnOpener(pathname?: string, windowLike: Window = globalThis.
return false;
}
- if (pathname && openerLocation.pathname !== pathname) {
- return false;
+ if (pathname && openerLocation.pathname.startsWith(pathname)) {
+ return true;
}
- return true;
+ return false;
} catch {
// If there is a security error, it means that the opener is from a different origin, so we let it fall through
return false;