diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts index fd54f80ec6..a0b7abb0d2 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts @@ -7,6 +7,7 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; export class UmbAppAuthController extends UmbControllerBase { #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; + #isFirstCheck = true; constructor(host: UmbControllerHost) { super(host); @@ -37,7 +38,18 @@ export class UmbAppAuthController extends UmbControllerBase { const isAuthorized = this.#authContext.getIsAuthorized(); if (isAuthorized) { - return true; + // If this is the first time we are checking the authorization state (i.e. on first load), we need to make sure + // that the token is still valid. If it is not, we need to start the authorization flow. + // If the token is still valid, we can return true. + if (this.#isFirstCheck) { + this.#isFirstCheck = false; + const isValid = await this.#authContext.validateToken(); + if (isValid) { + return true; + } + } else { + return true; + } } // Make a request to the auth server to start the auth flow diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts index 4b02ecb73e..1c139bf595 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts @@ -307,29 +307,17 @@ export class UmbAuthFlow { return Promise.resolve(this.#tokenResponse.accessToken); } - // if the refresh token is not set (maybe the provider doesn't support them) - if (!this.#tokenResponse?.refreshToken) { - this.#timeoutSignal.next(); - return Promise.reject('Missing refreshToken.'); - } + const success = await this.makeRefreshTokenRequest(); - const request = new TokenRequest({ - client_id: this.#clientId, - redirect_uri: this.#redirectUri, - grant_type: GRANT_TYPE_REFRESH_TOKEN, - code: undefined, - refresh_token: this.#tokenResponse.refreshToken, - extras: undefined, - }); - - await this.#performTokenRequest(request); - - if (!this.#tokenResponse) { + if (!success) { + this.clearTokenStorage(); this.#timeoutSignal.next(); return Promise.reject('Missing tokenResponse.'); } - return Promise.resolve(this.#tokenResponse.accessToken); + return this.#tokenResponse + ? Promise.resolve(this.#tokenResponse.accessToken) + : Promise.reject('Missing tokenResponse.'); } /** @@ -364,18 +352,36 @@ export class UmbAuthFlow { await this.#performTokenRequest(request); } + async makeRefreshTokenRequest(): Promise { + if (!this.#tokenResponse?.refreshToken) { + return false; + } + + const request = new TokenRequest({ + client_id: this.#clientId, + redirect_uri: this.#redirectUri, + grant_type: GRANT_TYPE_REFRESH_TOKEN, + code: undefined, + refresh_token: this.#tokenResponse.refreshToken, + extras: undefined, + }); + + return this.#performTokenRequest(request); + } + /** * This method will make a token request to the server using the refresh token. * If the request fails, it will sign the user out (clear the token state). */ - async #performTokenRequest(request: TokenRequest): Promise { + async #performTokenRequest(request: TokenRequest): Promise { try { this.#tokenResponse = await this.#tokenHandler.performTokenRequest(this.#configuration, request); this.#saveTokenState(); + return true; } catch (error) { - // If the token request fails, it means the code or refresh token is invalid - this.clearTokenStorage(); console.error('Token request error', error); + this.clearTokenStorage(); + return false; } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts index 922a66ea83..e2a408ae0b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts @@ -174,6 +174,15 @@ export class UmbAuthContext extends UmbContextBase { return this.#authFlow.performWithFreshTokens(); } + /** + * Validates the token against the server and returns true if the token is valid. + * @memberof UmbAuthContext + * @returns True if the token is valid, otherwise false + */ + async validateToken(): Promise { + return this.#authFlow.makeRefreshTokenRequest(); + } + /** * Clears the token storage. * @memberof UmbAuthContext @@ -188,7 +197,6 @@ export class UmbAuthContext extends UmbContextBase { * @memberof UmbAuthContext */ timeOut() { - this.clearTokenStorage(); this.#isAuthorized.setValue(false); this.#isTimeout.next(); }