From 3601af0acaab0e2086955dd19a957d9cc171849e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:06:47 +0200 Subject: [PATCH 01/15] remove unused line --- .../modals/current-user/current-user-modal.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user/current-user-modal.element.ts index 809eb5715a..c6aebb4229 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user/current-user-modal.element.ts @@ -44,7 +44,6 @@ export class UmbCurrentUserModalElement extends UmbLitElement { private async _logout() { if (!this.#authContext) return; - this.#authContext.performWithFreshTokens; await this.#authContext.signOut(); let newUrl = this.#appContext ? `${this.#appContext.getBackofficePath()}/login` : '/'; newUrl = newUrl.replace(/\/\//g, '/'); From a23a1a490b37ddb25bbaccff8f43f368a4592744 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:07:05 +0200 Subject: [PATCH 02/15] avoid using UmbAuthFlow directly in app element --- .../src/apps/app/app.element.ts | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) 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 8d8f3d2e8e..ce710f7f33 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,7 +1,7 @@ import type { UmbAppErrorElement } from './app-error.element.js'; import { UMB_APP, UmbAppContext } from './app.context.js'; import { umbLocalizationRegistry } from '@umbraco-cms/backoffice/localization'; -import { UMB_AUTH, UmbAuthFlow, UmbAuthContext } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, UmbAuthContext } from '@umbraco-cms/backoffice/auth'; 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'; @@ -56,7 +56,7 @@ export class UmbAppElement extends UmbLitElement { }, ]; - #authFlow?: UmbAuthFlow; + #authContext?: UmbAuthContext; #umbIconRegistry = new UmbIconRegistry(); #uuiIconRegistry = new UUIIconRegistryEssential(); #runtimeLevel = RuntimeLevelModel.UNKNOWN; @@ -81,15 +81,21 @@ export class UmbAppElement extends UmbLitElement { } } - #listenForLanguageChange(authContext: UmbAuthContext) { + #listenForLanguageChange() { // This will wait for the default language to be loaded before attempting to load the current user language // just in case the user language is not the default language. // We **need** to do this because the default language (typically en-us) holds all the fallback keys for all the other languages. // This way we can ensure that the document language is always loaded first and subsequently registered as the fallback language. umbLocalizationRegistry.isDefaultLoaded.subscribe((isDefaultLoaded) => { + if (!this.#authContext) { + console.error('[Fatal] AuthContext requested before it was initialised'); + return; + } + if (!isDefaultLoaded) return; + this.observe( - authContext.languageIsoCode, + this.#authContext.languageIsoCode, (currentLanguageIsoCode) => { umbLocalizationRegistry.loadLanguage(currentLanguageIsoCode); }, @@ -104,11 +110,9 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.BASE = this.serverUrl; const redirectUrl = `${window.location.origin}${this.backofficePath}`; - this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); + this.#authContext = new UmbAuthContext(this, this.serverUrl, redirectUrl); - const authContext = new UmbAuthContext(this, this.#authFlow); - - this.provideContext(UMB_AUTH, authContext); + this.provideContext(UMB_AUTH, this.#authContext); this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); @@ -120,9 +124,9 @@ export class UmbAppElement extends UmbLitElement { // If the runtime level is "install" we should clear any cached tokens // else we should try and set the auth status if (this.#runtimeLevel === RuntimeLevelModel.INSTALL) { - await authContext.signOut(); + await this.#authContext.signOut(); } else { - await this.#setAuthStatus(authContext); + await this.#setAuthStatus(); } // Initialise the router @@ -180,19 +184,24 @@ export class UmbAppElement extends UmbLitElement { this.#runtimeLevel = data?.serverStatus ?? RuntimeLevelModel.UNKNOWN; } - async #setAuthStatus(authContext: UmbAuthContext) { + async #setAuthStatus() { + if (!this.#authContext) { + console.error('[Fatal] AuthContext requested before it was initialised'); + return; + } + if (this.bypassAuth === false) { // Get service configuration from authentication server - await authContext.setInitialState(); + await this.#authContext.setInitialState(); // Instruct all requests to use the auth flow to get and use the access_token for all subsequent requests - OpenAPI.TOKEN = () => this.#authFlow!.performWithFreshTokens(); + OpenAPI.TOKEN = () => this.#authContext!.performWithFreshTokens(); OpenAPI.WITH_CREDENTIALS = true; } - this.#listenForLanguageChange(authContext); + this.#listenForLanguageChange(); - authContext.isLoggedIn.next(true); + this.#authContext!.isLoggedIn.next(true); } #redirect() { @@ -233,8 +242,8 @@ export class UmbAppElement extends UmbLitElement { } #isAuthorized(): boolean { - if (!this.#authFlow) return false; - return this.bypassAuth ? true : this.#authFlow.loggedIn(); + if (!this.#authContext) return false; + return this.bypassAuth ? true : this.#authContext.isLoggedIn.value; } #isAuthorizedGuard(): Guard { @@ -247,7 +256,7 @@ export class UmbAppElement extends UmbLitElement { window.sessionStorage.setItem('umb:auth:redirect', location.href); // Make a request to the auth server to start the auth flow - this.#authFlow!.makeAuthorizationRequest(); + this.#authContext!.login(); // Return false to prevent the route from being rendered return false; From 5d22f6bcdf6a259be21b2b913f2821c7f80574e5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:07:19 +0200 Subject: [PATCH 03/15] construct UmbAuthFlow internally in UmbAuthContext --- .../src/shared/auth/auth.context.ts | 15 +++++++++++---- .../src/shared/auth/index.ts | 5 ++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts index b9c00be403..821484e919 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -5,20 +5,20 @@ import { UserResource } from '@umbraco-cms/backoffice/backend-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; -import { ReplaySubject } from '@umbraco-cms/backoffice/external/rxjs'; +import { BehaviorSubject } from '@umbraco-cms/backoffice/external/rxjs'; export class UmbAuthContext implements IUmbAuth { #currentUser = new UmbObjectState(undefined); readonly currentUser = this.#currentUser.asObservable(); - readonly isLoggedIn = new ReplaySubject(1); + readonly isLoggedIn = new BehaviorSubject(false); readonly languageIsoCode = this.#currentUser.asObservablePart((user) => user?.languageIsoCode ?? 'en-us'); #host; #authFlow; - constructor(host: UmbControllerHostElement, authFlow: UmbAuthFlow) { + constructor(host: UmbControllerHostElement, serverUrl: string, redirectUrl: string) { this.#host = host; - this.#authFlow = authFlow; + this.#authFlow = new UmbAuthFlow(serverUrl, redirectUrl); this.isLoggedIn.subscribe((isLoggedIn) => { if (isLoggedIn) { @@ -27,6 +27,13 @@ export class UmbAuthContext implements IUmbAuth { }); } + /** + * Initiates the login flow. + */ + login(): void { + return this.#authFlow.makeAuthorizationRequest(); + } + setInitialState(): Promise { return this.#authFlow.setInitialState(); } diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts index e32f1d152b..b4989d9932 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts @@ -1,6 +1,5 @@ -export type { IUmbAuth } from './auth.interface.js'; -export { UmbAuthFlow } from './auth-flow.js'; -export { UmbAuthContext } from './auth.context.js'; +export * from './auth.interface.js'; +export * from './auth.context.js'; export * from './types.js'; export * from './auth.token.js'; From f67ae31662cfbe2d3cac84c0e34c4a10c27ddca0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:16:01 +0200 Subject: [PATCH 04/15] send a revoke token request after logout --- .../src/shared/auth/auth-flow.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 34134e3503..a57a410c9b 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 @@ -24,6 +24,7 @@ import { AuthorizationServiceConfiguration, GRANT_TYPE_AUTHORIZATION_CODE, GRANT_TYPE_REFRESH_TOKEN, + RevokeTokenRequest, TokenRequest, TokenResponse, LocationLike, @@ -227,6 +228,17 @@ export class UmbAuthFlow { */ async signOut() { // forget all cached token state + if (!this.#accessTokenResponse) { + return; + } + + const tokenRevokeRequest = new RevokeTokenRequest({ + token: this.#accessTokenResponse.accessToken, + client_id: this.#clientId, + token_type_hint: 'access_token', + }); + + await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); this.#accessTokenResponse = undefined; this.#refreshToken = undefined; await this.#storageBackend.removeItem(TOKEN_RESPONSE_NAME); From 0e801cedfefd55aa9fb9587f1acfb9909212e175 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:16:06 +0200 Subject: [PATCH 05/15] exports --- src/Umbraco.Web.UI.Client/src/external/openid/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/index.ts b/src/Umbraco.Web.UI.Client/src/external/openid/index.ts index baaee116db..ff18b32b45 100644 --- a/src/Umbraco.Web.UI.Client/src/external/openid/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/openid/index.ts @@ -4,6 +4,7 @@ export { FetchRequestor, LocalStorageBackend, RedirectRequestHandler, + RevokeTokenRequest, } from '@openid/appauth'; export { AuthorizationRequest } from '@openid/appauth/built/authorization_request'; export { AuthorizationNotifier } from '@openid/appauth/built/authorization_request_handler'; From 21d508a224e823e5f93022e6620e1f0e1243bdf8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:16:23 +0200 Subject: [PATCH 06/15] rename method to getLatestToken --- .../src/shared/auth/auth.context.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts index 821484e919..cefd2838d1 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -46,7 +46,15 @@ export class UmbAuthContext implements IUmbAuth { return data; } - performWithFreshTokens(): Promise { + /** + * Gets the latest token from the Management API. + * If the token is expired, it will be refreshed. + * + * NB! The user may experience being redirected to the login screen if the token is expired. + * + * @returns The latest token from the Management API + */ + getLatestToken(): Promise { return this.#authFlow.performWithFreshTokens(); } From 3fa2387410627d8c5d9670ef8d71fd352575127f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:16:27 +0200 Subject: [PATCH 07/15] documentation --- src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts index cefd2838d1..32bc11fd29 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -58,6 +58,9 @@ export class UmbAuthContext implements IUmbAuth { return this.#authFlow.performWithFreshTokens(); } + /** + * Signs the user out by removing any tokens from the browser. + */ signOut(): Promise { return this.#authFlow.signOut(); } From 41bc048536a9e7726642ad3607e93e9c0eb747cc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:18:52 +0200 Subject: [PATCH 08/15] documentation --- .../src/shared/auth/auth.interface.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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 index b70031f8d7..9e197cd717 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts @@ -2,21 +2,31 @@ import type { UmbLoggedInUser } from './types.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; export interface IUmbAuth { + /** + * Initiates the login flow. + */ + login(): void; + /** * Initialise the auth flow. */ setInitialState(): Promise; /** - * Get the current user's access token. + * Gets the latest token from the Management API. + * If the token is expired, it will be refreshed. + * + * NB! The user may experience being redirected to the login screen if the token is expired. * * @example * ```js - * const token = await auth.getAccessToken(); + * const token = await authContext.getLatestToken(); * const result = await fetch('https://my-api.com', { headers: { Authorization: `Bearer ${token}` } }); * ``` + * + * @returns The latest token from the Management API */ - performWithFreshTokens(): Promise; + getLatestToken(): Promise; /** * Get the current user model of the current user. @@ -34,7 +44,7 @@ export interface IUmbAuth { fetchCurrentUser(): Promise; /** - * Sign out the current user. + * Signs the user out by removing any tokens from the browser. */ signOut(): Promise; } From 55a3f141df91870390c9b9f291322e385d6e9e2f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:19:07 +0200 Subject: [PATCH 09/15] rename method --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ce710f7f33..fefe1a55a2 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 @@ -195,7 +195,7 @@ export class UmbAppElement extends UmbLitElement { await this.#authContext.setInitialState(); // Instruct all requests to use the auth flow to get and use the access_token for all subsequent requests - OpenAPI.TOKEN = () => this.#authContext!.performWithFreshTokens(); + OpenAPI.TOKEN = () => this.#authContext!.getLatestToken(); OpenAPI.WITH_CREDENTIALS = true; } From 242b4e0ed72e3e5650a6c789b2a954cc38656a35 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:36:53 +0200 Subject: [PATCH 10/15] only set login to true if authorized --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 fefe1a55a2..1f5fc0691f 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 @@ -201,7 +201,11 @@ export class UmbAppElement extends UmbLitElement { this.#listenForLanguageChange(); - this.#authContext!.isLoggedIn.next(true); + if (this.#authContext?.isAuthorized()) { + this.#authContext.isLoggedIn.next(true); + } else { + this.#authContext?.isLoggedIn.next(false); + } } #redirect() { From 37f1b0092c68c81a99d519b6c9f316da7b55d17c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:37:41 +0200 Subject: [PATCH 11/15] fix: check it authflow has a token and only use that for authorizedguard --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1f5fc0691f..501cad9257 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 @@ -247,7 +247,7 @@ export class UmbAppElement extends UmbLitElement { #isAuthorized(): boolean { if (!this.#authContext) return false; - return this.bypassAuth ? true : this.#authContext.isLoggedIn.value; + return this.bypassAuth ? true : this.#authContext.isAuthorized(); } #isAuthorizedGuard(): Guard { From 29328a332e95c7fb1276131f3eac4e59692d8c20 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:37:53 +0200 Subject: [PATCH 12/15] throw errors instead of console to stop the application --- .../src/apps/app/app.element.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 501cad9257..f51e46a216 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 @@ -88,8 +88,7 @@ export class UmbAppElement extends UmbLitElement { // This way we can ensure that the document language is always loaded first and subsequently registered as the fallback language. umbLocalizationRegistry.isDefaultLoaded.subscribe((isDefaultLoaded) => { if (!this.#authContext) { - console.error('[Fatal] AuthContext requested before it was initialised'); - return; + throw new Error('[Fatal] AuthContext requested before it was initialised'); } if (!isDefaultLoaded) return; @@ -185,12 +184,11 @@ export class UmbAppElement extends UmbLitElement { } async #setAuthStatus() { - if (!this.#authContext) { - console.error('[Fatal] AuthContext requested before it was initialised'); - return; - } - if (this.bypassAuth === false) { + if (!this.#authContext) { + throw new Error('[Fatal] AuthContext requested before it was initialised'); + } + // Get service configuration from authentication server await this.#authContext.setInitialState(); From cea210a56c04a8b8a84864a02aa29a92c3ec668b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:38:08 +0200 Subject: [PATCH 13/15] export the isAuthorized function --- src/Umbraco.Web.UI.Client/src/shared/auth/auth-flow.ts | 2 +- src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts | 4 ++++ src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts | 5 +++++ 3 files changed, 10 insertions(+), 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 a57a410c9b..c776944107 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 @@ -219,7 +219,7 @@ export class UmbAuthFlow { * * @returns true if the user is logged in, false otherwise. */ - loggedIn(): boolean { + isAuthorized(): boolean { return !!this.#accessTokenResponse && this.#accessTokenResponse.isValid(); } diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts index 32bc11fd29..0a25ed014d 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -34,6 +34,10 @@ export class UmbAuthContext implements IUmbAuth { return this.#authFlow.makeAuthorizationRequest(); } + isAuthorized() { + return this.#authFlow.isAuthorized(); + } + setInitialState(): Promise { return this.#authFlow.setInitialState(); } 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 index 9e197cd717..82bf9d544e 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.interface.ts @@ -12,6 +12,11 @@ export interface IUmbAuth { */ setInitialState(): Promise; + /** + * Checks if there is a token and it is still valid. + */ + isAuthorized(): boolean; + /** * Gets the latest token from the Management API. * If the token is expired, it will be refreshed. From 2e98e55674a24aeea46d617491adec225023b768 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:39:27 +0200 Subject: [PATCH 14/15] disable logic not supported yet by the server --- .../src/shared/auth/auth-flow.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 c776944107..8486d344fa 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 @@ -232,13 +232,15 @@ export class UmbAuthFlow { return; } - const tokenRevokeRequest = new RevokeTokenRequest({ - token: this.#accessTokenResponse.accessToken, - client_id: this.#clientId, - token_type_hint: 'access_token', - }); + // TODO: Enable this when the server supports it + // const tokenRevokeRequest = new RevokeTokenRequest({ + // token: this.#accessTokenResponse.accessToken, + // client_id: this.#clientId, + // token_type_hint: 'access_token', + // }); + + // await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); - await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); this.#accessTokenResponse = undefined; this.#refreshToken = undefined; await this.#storageBackend.removeItem(TOKEN_RESPONSE_NAME); From 7e90949ff6e218bb25cb0a225ebbe6d1b375930b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:54:56 +0200 Subject: [PATCH 15/15] add revoke requests for future --- .../src/shared/auth/auth-flow.ts | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) 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 8486d344fa..8f9ec99743 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 @@ -228,22 +228,33 @@ export class UmbAuthFlow { */ async signOut() { // forget all cached token state - if (!this.#accessTokenResponse) { - return; + await this.#storageBackend.removeItem(TOKEN_RESPONSE_NAME); + + if (this.#accessTokenResponse) { + // TODO: Enable this when the server supports it + // const tokenRevokeRequest = new RevokeTokenRequest({ + // token: this.#accessTokenResponse.accessToken, + // client_id: this.#clientId, + // token_type_hint: 'access_token', + // }); + + // await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); + + this.#accessTokenResponse = undefined; } - // TODO: Enable this when the server supports it - // const tokenRevokeRequest = new RevokeTokenRequest({ - // token: this.#accessTokenResponse.accessToken, - // client_id: this.#clientId, - // token_type_hint: 'access_token', - // }); + if (this.#refreshToken) { + // TODO: Enable this when the server supports it + // const tokenRevokeRequest = new RevokeTokenRequest({ + // token: this.#refreshToken, + // client_id: this.#clientId, + // token_type_hint: 'refresh_token', + // }); - // await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); + // await this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest); - this.#accessTokenResponse = undefined; - this.#refreshToken = undefined; - await this.#storageBackend.removeItem(TOKEN_RESPONSE_NAME); + this.#refreshToken = undefined; + } } /**