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.' +);