From 65fcd19bfecb1128cdd9eff1d919cbd0db479eb6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:32:12 +0200 Subject: [PATCH 1/6] move UmbAuthFlow to a shared folder --- .../src/apps/app/auth/index.ts | 1 - .../src/{apps/app => shared}/auth/auth-flow.ts | 11 +++++++---- .../src/shared/auth/auth.interface.ts | 17 +++++++++++++++++ .../src/shared/auth/index.ts | 10 ++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/apps/app/auth/index.ts rename src/Umbraco.Web.UI.Client/src/{apps/app => shared}/auth/auth-flow.ts (96%) create mode 100644 src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts create mode 100644 src/Umbraco.Web.UI.Client/src/shared/auth/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/auth/index.ts b/src/Umbraco.Web.UI.Client/src/apps/app/auth/index.ts deleted file mode 100644 index 16f8c38e67..0000000000 --- a/src/Umbraco.Web.UI.Client/src/apps/app/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './auth-flow.js'; diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/apps/app/auth/auth-flow.ts rename to src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts index 191a61aafa..5d25ec40bf 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ +import type { IUmbAuth } from './auth.interface.js'; import { BaseTokenRequestHandler, BasicQueryStringUtils, @@ -32,6 +33,8 @@ import { const requestor = new FetchRequestor(); +const TOKEN_RESPONSE_NAME = 'umb:tokenResponse'; + /** * This class is needed to prevent the hash from being parsed as part of the query string. */ @@ -78,7 +81,7 @@ class UmbNoHashQueryStringUtils extends BasicQueryStringUtils { * a. This will redirect the user to the authorization endpoint of the server * 4. After login, get the latest token before each request to the server by calling the `performWithFreshTokens` method */ -export class UmbAuthFlow { +export class UmbAuthFlow implements IUmbAuth { // handlers readonly #notifier: AuthorizationNotifier; readonly #authorizationHandler: RedirectRequestHandler; @@ -155,7 +158,7 @@ export class UmbAuthFlow { async setInitialState() { // Ensure there is a connection to the server await this.fetchServiceConfiguration(); - const tokenResponseJson = await this.#storageBackend.getItem('tokenResponse'); + const tokenResponseJson = await this.#storageBackend.getItem(TOKEN_RESPONSE_NAME); if (tokenResponseJson) { const response = new TokenResponse(JSON.parse(tokenResponseJson)); if (response.isValid()) { @@ -238,7 +241,7 @@ export class UmbAuthFlow { // forget all cached token state this.#accessTokenResponse = undefined; this.#refreshToken = undefined; - await this.#storageBackend.removeItem('tokenResponse'); + await this.#storageBackend.removeItem(TOKEN_RESPONSE_NAME); } /** @@ -282,7 +285,7 @@ export class UmbAuthFlow { */ async #saveTokenState() { if (this.#accessTokenResponse) { - await this.#storageBackend.setItem('tokenResponse', JSON.stringify(this.#accessTokenResponse.toJson())); + await this.#storageBackend.setItem(TOKEN_RESPONSE_NAME, JSON.stringify(this.#accessTokenResponse.toJson())); } } diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts new file mode 100644 index 0000000000..a71be30e2f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts @@ -0,0 +1,17 @@ +export interface IUmbAuth { + /** + * Get the current user's access token. + * + * @example + * ```js + * const token = await auth.getAccessToken(); + * const result = await fetch('https://my-api.com', { headers: { Authorization: `Bearer ${token}` } }); + * ``` + */ + performWithFreshTokens(): Promise; + + /** + * Sign out the current user. + */ + signOut(): Promise; +} diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts new file mode 100644 index 0000000000..391382b79c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts @@ -0,0 +1,10 @@ +import { IUmbAuth } from './auth.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export type { IUmbAuth } from './auth.interface.js'; +export { UmbAuthFlow } from './auth-flow.js'; + +export const UMB_AUTH = new UmbContextToken( + 'UmbAuth', + 'An instance of UmbAuthFlow that should be shared across the app.' +); From 584be4fe4cca59d3ae284d01fb3b262de7644acb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:32:25 +0200 Subject: [PATCH 2/6] move UmbAppContext to a shared folder --- .../src/{apps/app => shared/context}/app.context.ts | 2 +- src/Umbraco.Web.UI.Client/src/shared/context/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename src/Umbraco.Web.UI.Client/src/{apps/app => shared/context}/app.context.ts (84%) create mode 100644 src/Umbraco.Web.UI.Client/src/shared/context/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.context.ts b/src/Umbraco.Web.UI.Client/src/shared/context/app.context.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/apps/app/app.context.ts rename to src/Umbraco.Web.UI.Client/src/shared/context/app.context.ts index 42e314745d..f4ccd4b79f 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.context.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/context/app.context.ts @@ -1,4 +1,4 @@ -import { UmbAppContextConfig } from './app-context-config.interface.js'; +import { UmbAppContextConfig } from '../../apps/app/app-context-config.interface.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbAppContext { diff --git a/src/Umbraco.Web.UI.Client/src/shared/context/index.ts b/src/Umbraco.Web.UI.Client/src/shared/context/index.ts new file mode 100644 index 0000000000..cbe999e3a3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/context/index.ts @@ -0,0 +1 @@ +export * from './app.context.js'; From 99fb53fd29db1d8237dbd629bf4762e12ce0c8d6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:33:29 +0200 Subject: [PATCH 3/6] set up imports and exports --- src/Umbraco.Web.UI.Client/package.json | 2 ++ src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 6 ++++-- src/Umbraco.Web.UI.Client/src/apps/app/index.ts | 1 - src/Umbraco.Web.UI.Client/tsconfig.json | 2 ++ src/Umbraco.Web.UI.Client/web-test-runner.config.mjs | 2 ++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index f34a5b3e9b..5b9baa4e6d 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -11,6 +11,8 @@ "./element-api": "./dist-cms/libs/element-api/index.js", "./extension-api": "./dist-cms/libs/extension-api/index.js", "./observable-api": "./dist-cms/libs/observable-api/index.js", + "./auth": "./dist-cms/shared/auth/index.js", + "./context": "./dist-cms/shared/context/index.js", "./events": "./dist-cms/shared/umb-events/index.js", "./models": "./dist-cms/shared/models/index.js", "./repository": "./dist-cms/shared/repository/index.js", 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 ed09065f09..db309ee6dc 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 @@ -1,6 +1,6 @@ +import { UMB_AUTH, UmbAuthFlow } from '@umbraco-cms/backoffice/auth'; +import { UMB_APP, UmbAppContext } from '@umbraco-cms/backoffice/context'; import type { UmbAppErrorElement } from './app-error.element.js'; -import { UmbAuthFlow } from './auth/index.js'; -import { UMB_APP, UmbAppContext } from './app.context.js'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UUIIconRegistryEssential } from '@umbraco-cms/backoffice/external/uui'; import { UmbIconRegistry } from '@umbraco-cms/backoffice/icon'; @@ -81,6 +81,8 @@ export class UmbAppElement extends UmbLitElement { this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); + this.provideContext(UMB_AUTH, this.#authFlow); + this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); // Try to initialise the auth flow and get the runtime status diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/index.ts b/src/Umbraco.Web.UI.Client/src/apps/app/index.ts index 8eb4f65238..0834e3b2c8 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/index.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/index.ts @@ -1,4 +1,3 @@ export * from './app-context-config.interface.js'; export * from './app-error.element.js'; -export * from './app.context.js'; export * from './app.element.js'; diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 65b090a30a..a3baeab158 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -35,6 +35,8 @@ "@umbraco-cms/backoffice/observable-api": ["src/libs/observable-api"], // SHARED + "@umbraco-cms/backoffice/auth": ["src/shared/auth"], + "@umbraco-cms/backoffice/context": ["src/shared/context"], "@umbraco-cms/backoffice/events": ["src/shared/umb-events"], "@umbraco-cms/backoffice/models": ["src/shared/models"], "@umbraco-cms/backoffice/repository": ["src/shared/repository"], diff --git a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs index d5627f2078..fe00353f9c 100644 --- a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs +++ b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs @@ -48,6 +48,8 @@ export default { '@umbraco-cms/backoffice/extension-api': './src/libs/extension-api/index.ts', '@umbraco-cms/backoffice/observable-api': './src/libs/observable-api/index.ts', + '@umbraco-cms/backoffice/auth': './src/shared/auth/index.ts', + '@umbraco-cms/backoffice/context': './src/shared/context/index.ts', '@umbraco-cms/backoffice/events': './src/shared/umb-events/index.ts', '@umbraco-cms/backoffice/models': './src/shared/models/index.ts', '@umbraco-cms/backoffice/repository': './src/shared/repository/index.ts', From 2cca885fde5ce81bcac057345d62bb967fea3b54 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 5 Jun 2023 10:33:49 +0200 Subject: [PATCH 4/6] set up logout button to call logout --- .../current-user-modal.element.ts | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts index a8f567f82e..7ca048eb62 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts @@ -1,7 +1,9 @@ +import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; +import { UMB_APP } from '@umbraco-cms/backoffice/context'; import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user.store.js'; import type { UmbLoggedInUser } from '../../types.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; -import { css, CSSResultGroup, html , customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -15,6 +17,10 @@ export class UmbCurrentUserModalElement extends UmbLitElement { private _currentUserStore?: UmbCurrentUserStore; + #auth?: typeof UMB_AUTH.TYPE; + + #appContext?: typeof UMB_APP.TYPE; + constructor() { super(); @@ -23,6 +29,14 @@ export class UmbCurrentUserModalElement extends UmbLitElement { this._observeCurrentUser(); }); + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; + }); + + this.consumeContext(UMB_APP, (instance) => { + this.#appContext = instance; + }); + this._observeCurrentUser(); } @@ -38,8 +52,13 @@ export class UmbCurrentUserModalElement extends UmbLitElement { this.modalHandler?.submit(); } - private _logout() { - alert('implement log out'); + private async _logout() { + if (!this.#auth) return; + this.#auth.performWithFreshTokens; + await this.#auth.signOut(); + let newUrl = this.#appContext ? `${this.#appContext.getBackofficePath()}/login` : '/'; + newUrl = newUrl.replace(/\/\//g, '/'); + location.href = newUrl; } render() { From aae870a07d4fe9421cbd124a3560b1a558093428 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:49:13 +0200 Subject: [PATCH 5/6] change name of token --- src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts index 5d25ec40bf..1820234ff6 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts @@ -33,7 +33,7 @@ import { const requestor = new FetchRequestor(); -const TOKEN_RESPONSE_NAME = 'umb:tokenResponse'; +const TOKEN_RESPONSE_NAME = 'umb:userAuthTokenResponse'; /** * This class is needed to prevent the hash from being parsed as part of the query string. From 4de2a023b98c264427a4b153f62fc653ef36eeca Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 6 Jun 2023 08:57:14 +0200 Subject: [PATCH 6/6] remove UmbModalHandler --- .../modals/current-user/current-user-modal.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts index 2bd37e1b95..b0cbbf80ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/modals/current-user/current-user-modal.element.ts @@ -4,7 +4,6 @@ import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../.. import type { UmbLoggedInUser } from '../../types.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; import { UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';