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 4e0e8a11fb..b07b5e9b5f 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,5 +1,5 @@ import type { UmbAppErrorElement } from './app-error.element.js'; -import { UMB_AUTH, UmbAuthFlow } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, UmbAuthFlow, UmbAuthContext } from '@umbraco-cms/backoffice/auth'; import { UMB_APP, UmbAppContext } from '@umbraco-cms/backoffice/context'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UUIIconRegistryEssential } from '@umbraco-cms/backoffice/external/uui'; @@ -81,7 +81,9 @@ export class UmbAppElement extends UmbLitElement { this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); - this.provideContext(UMB_AUTH, this.#authFlow); + const authContext = new UmbAuthContext(this, this.#authFlow); + + this.provideContext(UMB_AUTH, authContext); this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); @@ -99,6 +101,8 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.WITH_CREDENTIALS = true; } + authContext.isLoggedIn.next(true); + // Initialise the router this.#redirect(); } catch (error) { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts index b55f498667..beda25493a 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/users.data.ts @@ -1,3 +1,4 @@ +import { UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; import { UmbData } from './data.js'; import { PagedUserResponseModel, UserResponseModel, UserStateModel } from '@umbraco-cms/backoffice/backend-api'; @@ -18,6 +19,25 @@ class UmbUsersData extends UmbData { return this.data.find((user) => user.id === id); } + getCurrentUser(): UmbLoggedInUser { + const firstUser = this.data[0]; + + return { + $type: 'CurrentUserResponseModel', + id: firstUser.id, + name: firstUser.name, + email: firstUser.email, + userName: firstUser.email, + avatarUrls: [], + hasAccessToAllLanguages: true, + languageIsoCode: firstUser.languageIsoCode, + languages: [], + contentStartNodeIds: firstUser.contentStartNodeIds, + mediaStartNodeIds: firstUser.mediaStartNodeIds, + permissions: [], + }; + } + save(id: string, saveItem: UserResponseModel) { const foundIndex = this.data.findIndex((item) => item.id === id); if (foundIndex !== -1) { @@ -96,9 +116,9 @@ export const data: Array = [ $type: 'UserResponseModel', contentStartNodeIds: [], mediaStartNodeIds: [], - name: 'Erny Baptista', - email: 'ebaptista1@csmonitor.com', - languageIsoCode: 'Kannada', + name: 'Umbraco User', + email: 'noreply@umbraco.com', + languageIsoCode: 'en-US', state: UserStateModel.ACTIVE, lastLoginDate: '9/10/2022', lastLockoutDate: '11/23/2021', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts index 3d9ceee1b0..9da37812be 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/user.handlers.ts @@ -3,18 +3,31 @@ const { rest } = window.MockServiceWorker; import { umbUsersData } from '../data/users.data.js'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; -let isAuthenticated = true; const slug = '/user'; export const handlers = [ - rest.get(umbracoPath(`${slug}`), (req, res, ctx) => { + rest.get(umbracoPath(`${slug}/filter`), (req, res, ctx) => { + //TODO: Implementer filter const response = umbUsersData.getAll(); return res(ctx.status(200), ctx.json(response)); }), - rest.get(umbracoPath(`${slug}/filter`), (req, res, ctx) => { - //TODO: Implementer filter + rest.get(umbracoPath(`${slug}/current`), (_req, res, ctx) => { + const loggedInUser = umbUsersData.getCurrentUser(); + return res(ctx.status(200), ctx.json(loggedInUser)); + }), + + rest.get(umbracoPath(`${slug}/sections`), (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + sections: ['Umb.Section.Content', 'Umb.Section.Media', 'Umb.Section.Settings', 'My.Section.Custom'], + }) + ); + }), + + rest.get(umbracoPath(`${slug}`), (req, res, ctx) => { const response = umbUsersData.getAll(); return res(ctx.status(200), ctx.json(response)); @@ -40,51 +53,4 @@ export const handlers = [ return res(ctx.status(200), ctx.json(saved)); }), - rest.post(umbracoPath('/user/login'), (_req, res, ctx) => { - // Persist user's authentication in the session - isAuthenticated = true; - return res( - // Respond with a 200 status code - ctx.status(201) - ); - }), - - rest.post(umbracoPath('/user/logout'), (_req, res, ctx) => { - // Persist user's authentication in the session - isAuthenticated = false; - return res( - // Respond with a 200 status code - ctx.status(201) - ); - }), - - rest.get(umbracoPath('/user'), (_req, res, ctx) => { - // Check if the user is authenticated in this session - if (!isAuthenticated) { - // If not authenticated, respond with a 403 error - return res( - ctx.status(403), - ctx.json({ - errorMessage: 'Not authorized', - }) - ); - } - // If authenticated, return a mocked user details - return res( - ctx.status(200), - ctx.json({ - username: 'admin', - role: 'administrator', - }) - ); - }), - - rest.get(umbracoPath('/user/sections'), (_req, res, ctx) => { - return res( - ctx.status(200), - ctx.json({ - sections: ['Umb.Section.Content', 'Umb.Section.Media', 'Umb.Section.Settings', 'My.Section.Custom'], - }) - ); - }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts index 9596b05cf3..ae5609b1fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts @@ -1,12 +1,40 @@ -import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; +import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-umbraco-news-dashboard') -export class UmbUmbracoNewsDashboardElement extends LitElement { +export class UmbUmbracoNewsDashboardElement extends UmbLitElement { + #auth?: typeof UMB_AUTH.TYPE; + + @state() + private name = ''; + + constructor() { + super(); + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; + this.#observeCurrentUser(); + }); + } + + #observeCurrentUser(): void { + if (!this.#auth) return; + this.observe(this.#auth.currentUser, (user) => { + this.name = user?.name ?? ''; + }); + } + render() { return html` -

Welcome

-

You can find details about the POC in the readme.md file.

+

Welcome, ${this.name}

+

This is a preview version of Umbraco, where you can have a first-hand look at the new Backoffice.

+

There is currently very limited functionality.

+

+ Please refer to the + documentation to learn more about + what is possible. +

`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts index 44ed9f9c36..860e011f32 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user-header-app.element.ts @@ -1,5 +1,3 @@ -import type { UmbLoggedInUser } from './types.js'; -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from './current-user.store.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, CSSResultGroup, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { @@ -8,13 +6,14 @@ import { UMB_CURRENT_USER_MODAL, } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-current-user-header-app') export class UmbCurrentUserHeaderAppElement extends UmbLitElement { @state() private _currentUser?: UmbLoggedInUser; - private _currentUserStore?: UmbCurrentUserStore; + private _auth?: typeof UMB_AUTH.TYPE; private _modalContext?: UmbModalManagerContext; constructor() { @@ -24,16 +23,16 @@ export class UmbCurrentUserHeaderAppElement extends UmbLitElement { this._modalContext = instance; }); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this._auth = instance; this._observeCurrentUser(); }); } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this._auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this._auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts deleted file mode 100644 index d188a75ba4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/current-user.store.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { UmbLoggedInUser } from './types.js'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; - -export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbCurrentUserStore'); - -export class UmbCurrentUserStore { - #currentUser = new UmbObjectState(undefined); - public readonly currentUser = this.#currentUser.asObservable(); -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts index 30f15ab9c5..0b68da794d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/index.ts @@ -1,4 +1,2 @@ -export * from './types.js'; // TODO:Do not export store, but instead export future repository -export * from './current-user.store.js'; export * from './current-user-history.store.js'; 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 83e31e00aa..eb2012cd16 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,6 +1,4 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user.store.js'; -import type { UmbLoggedInUser } from '../../types.js'; -import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; import { UMB_APP } from '@umbraco-cms/backoffice/context'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; @@ -15,8 +13,6 @@ export class UmbCurrentUserModalElement extends UmbLitElement { @state() private _currentUser?: UmbLoggedInUser; - private _currentUserStore?: UmbCurrentUserStore; - #auth?: typeof UMB_AUTH.TYPE; #appContext?: typeof UMB_APP.TYPE; @@ -24,8 +20,8 @@ export class UmbCurrentUserModalElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; this._observeCurrentUser(); }); @@ -41,9 +37,9 @@ export class UmbCurrentUserModalElement extends UmbLitElement { } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this.#auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this.#auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts deleted file mode 100644 index adfe453bf4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { UmbEntityBase } from '@umbraco-cms/backoffice/models'; - -export interface UserEntity extends UmbEntityBase { - type: 'user'; -} - -export type UserStatus = 'enabled' | 'inactive' | 'invited' | 'disabled'; - -export interface UmbLoggedInUser extends UserEntity { - email: string; - status: UserStatus; - language: string; - lastLoginDate?: string; - lastLockoutDate?: string; - lastPasswordChangeDate?: string; - updateDate: string; - createDate: string; - failedLoginAttempts: number; - userGroups: Array; - contentStartNodes: Array; - mediaStartNodes: Array; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts index e449d86358..7bc4c986fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/current-user/user-profile-apps/user-profile-app-profile.element.ts @@ -1,5 +1,3 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../current-user.store.js'; -import type { UmbLoggedInUser } from '../types.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -8,6 +6,7 @@ import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT_TOKEN, } from '@umbraco-cms/backoffice/modal'; +import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-user-profile-app-profile') export class UmbUserProfileAppProfileElement extends UmbLitElement { @@ -15,7 +14,7 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { private _currentUser?: UmbLoggedInUser; private _modalContext?: UmbModalManagerContext; - private _currentUserStore?: UmbCurrentUserStore; + private _auth?: typeof UMB_AUTH.TYPE; constructor() { super(); @@ -24,8 +23,8 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { this._modalContext = instance; }); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (instance) => { - this._currentUserStore = instance; + this.consumeContext(UMB_AUTH, (instance) => { + this._auth = instance; this._observeCurrentUser(); }); @@ -33,9 +32,9 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement { } private async _observeCurrentUser() { - if (!this._currentUserStore) return; + if (!this._auth) return; - this.observe(this._currentUserStore.currentUser, (currentUser) => { + this.observe(this._auth.currentUser, (currentUser) => { this._currentUser = currentUser; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/index.ts b/src/Umbraco.Web.UI.Client/src/packages/users/index.ts index 2fe1593f81..eb363b6357 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/index.ts @@ -3,7 +3,6 @@ import { manifests as userManifests } from './users/manifests.js'; import { manifests as userSectionManifests } from './user-section/manifests.js'; import { manifests as currentUserManifests } from './current-user/manifests.js'; -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from './current-user/current-user.store.js'; import { UmbCurrentUserHistoryStore, UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, @@ -21,7 +20,6 @@ export const manifests = [...userGroupManifests, ...userManifests, ...userSectio export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => { extensionRegistry.registerMany(manifests); - new UmbContextProviderController(host, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, new UmbCurrentUserStore()); new UmbUserItemStore(host); new UmbUserGroupItemStore(host); new UmbContextProviderController( diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts index 1ec1477af1..1fb55ddaac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace-editor.element.ts @@ -1,4 +1,3 @@ -import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user/current-user.store.js'; import { getLookAndColorFromUserStatus } from '../../utils.js'; import { UmbUserRepository } from '../repository/user.repository.js'; import { UmbUserGroupInputElement } from '../../user-groups/components/input-user-group/user-group-input.element.js'; @@ -24,16 +23,17 @@ import { UserResponseModel, UserStateModel } from '@umbraco-cms/backoffice/backe import { createExtensionClass } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import { UMB_AUTH, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; @customElement('umb-user-workspace-editor') export class UmbUserWorkspaceEditorElement extends UmbLitElement { @state() - private _currentUser?: any; + private _currentUser?: UmbLoggedInUser; @state() private _user?: UserResponseModel; - #currentUserStore?: UmbCurrentUserStore; + #auth?: typeof UMB_AUTH.TYPE; #modalContext?: UmbModalManagerContext; #languages = []; //TODO Add languages #workspaceContext?: UmbUserWorkspaceContext; @@ -43,8 +43,8 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, (store) => { - this.#currentUserStore = store; + this.consumeContext(UMB_AUTH, (instance) => { + this.#auth = instance; this.#observeCurrentUser(); }); @@ -76,8 +76,8 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { } #observeCurrentUser() { - if (!this.#currentUserStore) return; - this.observe(this.#currentUserStore.currentUser, (currentUser) => (this._currentUser = currentUser)); + if (!this.#auth) return; + this.observe(this.#auth.currentUser, (currentUser) => (this._currentUser = currentUser)); } #onUserStatusChange() { 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 1820234ff6..251bdea185 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 @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ -import type { IUmbAuth } from './auth.interface.js'; import { BaseTokenRequestHandler, BasicQueryStringUtils, @@ -81,7 +80,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 implements IUmbAuth { +export class UmbAuthFlow { // handlers readonly #notifier: AuthorizationNotifier; readonly #authorizationHandler: RedirectRequestHandler; @@ -164,7 +163,6 @@ export class UmbAuthFlow implements IUmbAuth { if (response.isValid()) { this.#accessTokenResponse = response; this.#refreshToken = this.#accessTokenResponse.refreshToken; - return; } } @@ -225,7 +223,7 @@ export class UmbAuthFlow implements IUmbAuth { } /** - * This method will check if the user is logged in by validting the timestamp of the stored token. + * This method will check if the user is logged in by validating the timestamp of the stored token. * If no token is stored, it will return false. * * @returns true if the user is logged in, false otherwise. 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 new file mode 100644 index 0000000000..ce31ca56cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.context.ts @@ -0,0 +1,46 @@ +import { IUmbAuth } from './auth.interface.js'; +import { UmbAuthFlow } from './auth-flow.js'; +import { UmbLoggedInUser } from './types.js'; +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'; + +export class UmbAuthContext implements IUmbAuth { + #currentUser = new UmbObjectState(undefined); + readonly currentUser = this.#currentUser.asObservable(); + readonly isLoggedIn = new ReplaySubject(1); + + #host; + #authFlow; + + constructor(host: UmbControllerHostElement, authFlow: UmbAuthFlow) { + this.#host = host; + this.#authFlow = authFlow; + + this.isLoggedIn.subscribe((isLoggedIn) => { + if (isLoggedIn) { + this.fetchCurrentUser(); + } + }); + } + + async fetchCurrentUser(): Promise { + const { data } = await tryExecuteAndNotify(this.#host, UserResource.getUserCurrent()); + + if (!data) return; + + this.#currentUser.next(data); + + return data; + } + + performWithFreshTokens(): Promise { + return this.#authFlow.performWithFreshTokens(); + } + + signOut(): Promise { + return this.#authFlow.signOut(); + } +} 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 a71be30e2f..6a90a3dc0d 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 @@ -1,3 +1,6 @@ +import type { UmbLoggedInUser } from './types.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; + export interface IUmbAuth { /** * Get the current user's access token. @@ -10,6 +13,16 @@ export interface IUmbAuth { */ performWithFreshTokens(): Promise; + /** + * Get the current user model of the current user. + */ + get currentUser(): Observable; + + /** + * Make a server request for the current user and save the state + */ + fetchCurrentUser(): Promise; + /** * Sign out the current user. */ diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts new file mode 100644 index 0000000000..36a5862575 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts @@ -0,0 +1,7 @@ +import { IUmbAuth } from './auth.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_AUTH = new UmbContextToken( + 'UmbAuth', + 'An instance of UmbAuthFlow that should be shared across the app.' +); 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 391382b79c..e32f1d152b 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/index.ts @@ -1,10 +1,6 @@ -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 { UmbAuthContext } from './auth.context.js'; -export const UMB_AUTH = new UmbContextToken( - 'UmbAuth', - 'An instance of UmbAuthFlow that should be shared across the app.' -); +export * from './types.js'; +export * from './auth.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts new file mode 100644 index 0000000000..3b481a3c47 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/types.ts @@ -0,0 +1 @@ +export type { CurrentUserResponseModel as UmbLoggedInUser } from '@umbraco-cms/backoffice/backend-api';