Merge pull request #979 from umbraco/feature-split-auth-and-user

Improvement: Split Current User from auth
This commit is contained in:
Mads Rasmussen
2023-11-13 16:37:09 +01:00
committed by GitHub
28 changed files with 454 additions and 322 deletions

View File

@@ -1,13 +1,16 @@
import { UmbAppContextConfig } from '../../apps/app/app-context-config.interface.js';
import { UmbAppContextConfig } from './app-context-config.interface.js';
import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export class UmbAppContext {
export class UmbAppContext extends UmbBaseController {
#serverUrl: string;
#backofficePath: string;
constructor(config: UmbAppContextConfig) {
constructor(host: UmbControllerHost, config: UmbAppContextConfig) {
super(host);
this.#serverUrl = config.serverUrl;
this.#backofficePath = config.backofficePath;
this.provideContext(UMB_APP_CONTEXT, this);
}
getBackofficePath() {
@@ -19,4 +22,4 @@ export class UmbAppContext {
}
}
export const UMB_APP = new UmbContextToken<UmbAppContext>('UMB_APP');
export const UMB_APP_CONTEXT = new UmbContextToken<UmbAppContext>('UMB_APP');

View File

@@ -1,6 +1,6 @@
import type { UmbAppErrorElement } from './app-error.element.js';
import { UMB_APP, UmbAppContext } from './app.context.js';
import { umbLocalizationRegistry } from '@umbraco-cms/backoffice/localization';
import { UmbAppContext } from './app.context.js';
import { UmbServerConnection } from './server-connection.js';
import { UMB_AUTH_CONTEXT, 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';
@@ -8,9 +8,8 @@ import { UmbIconRegistry } from '@umbraco-cms/backoffice/icon';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { Guard, UmbRoute } from '@umbraco-cms/backoffice/router';
import { pathWithoutBasePath } from '@umbraco-cms/backoffice/router';
import { tryExecute } from '@umbraco-cms/backoffice/resources';
import { OpenAPI, RuntimeLevelModel, ServerResource } from '@umbraco-cms/backoffice/backend-api';
import { contextData, umbDebugContextEventType } from '@umbraco-cms/backoffice/context-api';
import { OpenAPI, RuntimeLevelModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextDebugController } from '@umbraco-cms/backoffice/debug';
@customElement('umb-app')
export class UmbAppElement extends UmbLitElement {
@@ -56,73 +55,44 @@ export class UmbAppElement extends UmbLitElement {
},
];
#authContext?: UmbAuthContext;
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#umbIconRegistry = new UmbIconRegistry();
#uuiIconRegistry = new UUIIconRegistryEssential();
#runtimeLevel = RuntimeLevelModel.UNKNOWN;
#serverConnection?: UmbServerConnection;
constructor() {
super();
new UmbContextDebugController(this);
this.#umbIconRegistry.attach(this);
this.#uuiIconRegistry.attach(this);
}
connectedCallback(): void {
super.connectedCallback();
this.#setLanguage();
this.#setup();
}
#setLanguage() {
if (this.lang) {
umbLocalizationRegistry.loadLanguage(this.lang);
}
}
#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.
this.observe(umbLocalizationRegistry.isDefaultLoaded, (isDefaultLoaded) => {
if (!this.#authContext) {
throw new Error('[Fatal] AuthContext requested before it was initialized');
}
if (!isDefaultLoaded) return;
this.observe(
this.#authContext.languageIsoCode,
(currentLanguageIsoCode) => {
umbLocalizationRegistry.loadLanguage(currentLanguageIsoCode);
},
'languageIsoCode',
);
});
}
async #setup() {
if (this.serverUrl === undefined) throw new Error('No serverUrl provided');
/* All requests to the server requires the base URL to be set.
We make sure it happens before we get the server status.
TODO: find the right place to set this
*/
OpenAPI.BASE = this.serverUrl;
const redirectUrl = `${window.location.origin}${this.backofficePath}`;
this.#authContext = new UmbAuthContext(this, this.serverUrl, redirectUrl);
this.#serverConnection = await new UmbServerConnection(this.serverUrl).connect();
this.provideContext(UMB_AUTH_CONTEXT, this.#authContext);
this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl }));
this.#authContext = new UmbAuthContext(this, this.serverUrl, this.backofficePath, this.bypassAuth);
new UmbAppContext(this, { backofficePath: this.backofficePath, serverUrl: this.serverUrl });
// Try to initialise the auth flow and get the runtime status
try {
// Get the current runtime level
await this.#setInitStatus();
// 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) {
if (this.#serverConnection.getStatus() === RuntimeLevelModel.INSTALL) {
await this.#authContext.signOut();
} else {
await this.#setAuthStatus();
@@ -150,63 +120,26 @@ export class UmbAppElement extends UmbLitElement {
// Redirect to the error page
this.#errorPage(errorMsg, error);
}
// TODO: wrap all debugging logic in a separate class. Maybe this could be part of the context-api? When we create a new root, we could attach the debugger to it?
// Listen for the debug event from the <umb-debug> component
this.addEventListener(umbDebugContextEventType, (event: any) => {
// Once we got to the outter most component <umb-app>
// we can send the event containing all the contexts
// we have collected whilst coming up through the DOM
// and pass it back down to the callback in
// the <umb-debug> component that originally fired the event
if (event.callback) {
event.callback(event.instances);
}
// Massage the data into a simplier format
// Why? Can't send contexts data directly - browser seems to not serialize it and says its null
// But a simple object works fine for browser extension to consume
const data = {
contexts: contextData(event.instances),
};
// Emit this new event for the browser extension to listen for
this.dispatchEvent(new CustomEvent('umb:debug-contexts:data', { detail: data, bubbles: true }));
});
}
async #setInitStatus() {
const { data, error } = await tryExecute(ServerResource.getServerStatus());
if (error) {
throw error;
}
this.#runtimeLevel = data?.serverStatus ?? RuntimeLevelModel.UNKNOWN;
}
// TODO: move set initial auth state into auth context
async #setAuthStatus() {
if (this.bypassAuth === false) {
if (!this.#authContext) {
throw new Error('[Fatal] AuthContext requested before it was initialized');
}
if (this.bypassAuth) return;
// Get service configuration from authentication server
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!.getLatestToken();
if (!this.#authContext) {
throw new Error('[Fatal] AuthContext requested before it was initialized');
}
this.#listenForLanguageChange();
// Get service configuration from authentication server
await this.#authContext?.setInitialState();
if (this.#authContext?.isAuthorized()) {
this.#authContext?.setLoggedIn(true);
} else {
this.#authContext?.setLoggedIn(false);
}
// Instruct all requests to use the auth flow to get and use the access_token for all subsequent requests
OpenAPI.TOKEN = () => this.#authContext!.getLatestToken();
OpenAPI.WITH_CREDENTIALS = true;
}
#redirect() {
switch (this.#runtimeLevel) {
switch (this.#serverConnection?.getStatus()) {
case RuntimeLevelModel.INSTALL:
history.replaceState(null, '', 'install');
break;
@@ -238,18 +171,17 @@ export class UmbAppElement extends UmbLitElement {
default:
// Redirect to the error page
this.#errorPage(`Unsupported runtime level: ${this.#runtimeLevel}`);
this.#errorPage(`Unsupported runtime level: ${this.#serverConnection?.getStatus()}`);
}
}
#isAuthorized(): boolean {
if (!this.#authContext) return false;
return this.bypassAuth ? true : this.#authContext.isAuthorized();
}
#isAuthorizedGuard(): Guard {
return () => {
if (this.#isAuthorized()) {
if (!this.#authContext) {
throw new Error('[Fatal] AuthContext requested before it was initialized');
}
if (this.#authContext.getIsAuthorized()) {
return true;
}
@@ -257,7 +189,8 @@ 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.#authContext!.login();
// TODO: find better name for this method
this.#authContext.login();
// Return false to prevent the route from being rendered
return false;

View File

@@ -0,0 +1,62 @@
import { RuntimeLevelModel, ServerResource } from '@umbraco-cms/backoffice/backend-api';
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
import { tryExecute } from '@umbraco-cms/backoffice/resources';
export class UmbServerConnection {
#url: string;
#status: RuntimeLevelModel = RuntimeLevelModel.UNKNOWN;
#isConnected = new UmbBooleanState(false);
isConnected = this.#isConnected.asObservable();
constructor(serverUrl: string) {
this.#url = serverUrl;
}
/**
* Connects to the server.
* @memberof UmbServerConnection
*/
async connect() {
await this.#setStatus();
return this;
}
/**
* Gets the URL of the server.
* @return {*}
* @memberof UmbServerConnection
*/
getUrl() {
return this.#url;
}
/**
* Gets the status of the server.
* @return {string}
* @memberof UmbServerConnection
*/
getStatus() {
if (!this.getIsConnected()) throw new Error('Server is not connected. Remember to await connect()');
return this.#status;
}
/**
* Checks if the server is connected.
* @return {boolean}
* @memberof UmbServerConnection
*/
getIsConnected() {
return this.#isConnected.getValue();
}
async #setStatus() {
const { data, error } = await tryExecute(ServerResource.getServerStatus());
if (error) {
throw error;
}
this.#isConnected.next(true);
this.#status = data?.serverStatus ?? RuntimeLevelModel.UNKNOWN;
}
}

View File

@@ -2,7 +2,7 @@ import { UmbEntityData } from './entity.data.js';
import { umbUserGroupData } from './user-group.data.js';
import { arrayFilter, stringFilter, queryFilter } from './utils.js';
import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
import {
CreateUserRequestModel,
CreateUserResponseModel,
@@ -21,8 +21,10 @@ const createUserItem = (item: UserResponseModel): UserItemResponseModel => {
};
};
const userGroupFilter = (filterOptions: any, item: UserResponseModel) => arrayFilter(filterOptions.userGroupIds, item.userGroupIds);
const userStateFilter = (filterOptions: any, item: UserResponseModel) => stringFilter(filterOptions.userStates, item.state);
const userGroupFilter = (filterOptions: any, item: UserResponseModel) =>
arrayFilter(filterOptions.userGroupIds, item.userGroupIds);
const userStateFilter = (filterOptions: any, item: UserResponseModel) =>
stringFilter(filterOptions.userStates, item.state);
const userQueryFilter = (filterOptions: any, item: UserResponseModel) => queryFilter(filterOptions.filter, item.name);
// Temp mocked database
@@ -89,7 +91,7 @@ class UmbUserData extends UmbEntityData<UserResponseModel> {
* @return {*} {UmbLoggedInUser}
* @memberof UmbUserData
*/
getCurrentUser(): UmbLoggedInUser {
getCurrentUser(): UmbCurrentUser {
const firstUser = this.data[0];
const permissions = firstUser.userGroupIds?.length ? umbUserGroupData.getPermissions(firstUser.userGroupIds) : [];
@@ -159,26 +161,31 @@ class UmbUserData extends UmbEntityData<UserResponseModel> {
this.createUser(invitedUser);
}
filter (options: any): PagedUserResponseModel {
filter(options: any): PagedUserResponseModel {
const { items: allItems } = this.getAll();
const filterOptions = {
skip: options.skip || 0,
take: options.take || 25,
orderBy: options.orderBy || 'name',
orderDirection: options.orderDirection || 'asc',
userGroupIds: options.userGroupIds,
userStates: options.userStates,
filter: options.filter,
};
const filterOptions = {
skip: options.skip || 0,
take: options.take || 25,
orderBy: options.orderBy || 'name',
orderDirection: options.orderDirection || 'asc',
userGroupIds: options.userGroupIds,
userStates: options.userStates,
filter: options.filter,
};
const filteredItems = allItems.filter((item) => userGroupFilter(filterOptions, item) && userStateFilter(filterOptions, item) && userQueryFilter(filterOptions, item));
const filteredItems = allItems.filter(
(item) =>
userGroupFilter(filterOptions, item) &&
userStateFilter(filterOptions, item) &&
userQueryFilter(filterOptions, item),
);
const totalItems = filteredItems.length;
const paginatedItems = filteredItems.slice(filterOptions.skip, filterOptions.skip + filterOptions.take);
return { total: totalItems, items: paginatedItems };
};
}
}
export const data: Array<UserResponseModel & { type: string }> = [

View File

@@ -12,7 +12,7 @@ import {
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
UmbModalManagerContext,
} from '@umbraco-cms/backoffice/modal';
import { UMB_APP } from '@umbraco-cms/backoffice/app';
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
/**
* @element umb-input-markdown
@@ -49,7 +49,7 @@ export class UmbInputMarkdownElement extends FormControlMixin(UmbLitElement) {
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
});
this.consumeContext(UMB_APP, (instance) => {
this.consumeContext(UMB_APP_CONTEXT, (instance) => {
this.serverUrl = instance.getServerUrl();
});
}

View File

@@ -9,7 +9,7 @@ import {
type RawEditorOptions,
renderEditor,
} from '@umbraco-cms/backoffice/external/tinymce';
import { UMB_AUTH_CONTEXT, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
import { TinyMcePluginArguments, UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/components';
import { ClassConstructor, hasDefaultExport, loadExtension } from '@umbraco-cms/backoffice/extension-api';
import { ManifestTinyMcePlugin, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
@@ -26,7 +26,7 @@ import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { UmbMediaHelper } from '@umbraco-cms/backoffice/utils';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import { UMB_APP } from '@umbraco-cms/backoffice/app';
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
import { UmbStylesheetRepository } from '@umbraco-cms/backoffice/stylesheet';
// TODO => integrate macro picker, update stylesheet fetch when backend CLI exists (ref tinymce.service.js in existing backoffice)
@@ -39,8 +39,8 @@ export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) {
private _tinyConfig: RawEditorOptions = {};
#mediaHelper = new UmbMediaHelper();
#currentUser?: UmbLoggedInUser;
#auth?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUser?: UmbCurrentUser;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
#plugins: Array<new (args: TinyMcePluginArguments) => UmbTinyMcePluginBase> = [];
#editorRef?: Editor | null = null;
#stylesheetRepository?: UmbStylesheetRepository;
@@ -56,7 +56,7 @@ export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) {
constructor() {
super();
this.consumeContext(UMB_APP, (instance) => {
this.consumeContext(UMB_APP_CONTEXT, (instance) => {
this.#serverUrl = instance.getServerUrl();
});
@@ -71,9 +71,9 @@ export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) {
}
async #observeCurrentUser() {
if (!this.#auth) return;
if (!this.#currentUserContext) return;
this.observe(this.#auth.currentUser, (currentUser) => (this.#currentUser = currentUser));
this.observe(this.#currentUserContext.currentUser, (currentUser) => (this.#currentUser = currentUser));
}
protected async firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): Promise<void> {

View File

@@ -0,0 +1,45 @@
import { contextData, umbDebugContextEventType } from '@umbraco-cms/backoffice/context-api';
import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
// Temp controller to get the code away from the app.element.ts
export class UmbContextDebugController extends UmbBaseController {
constructor(host: UmbControllerHost) {
super(host);
}
hostConnected(): void {
super.hostConnected();
// Maybe this could be part of the context-api? When we create a new root, we could attach the debugger to it?
// Listen for the debug event from the <umb-debug> component
this.getHostElement().addEventListener(umbDebugContextEventType, this.#onContextDebug as unknown as EventListener);
}
#onContextDebug = (event: any) => {
// Once we got to the outter most component <umb-app>
// we can send the event containing all the contexts
// we have collected whilst coming up through the DOM
// and pass it back down to the callback in
// the <umb-debug> component that originally fired the event
if (event.callback) {
event.callback(event.instances);
}
// Massage the data into a simplier format
// Why? Can't send contexts data directly - browser seems to not serialize it and says its null
// But a simple object works fine for browser extension to consume
const data = {
contexts: contextData(event.instances),
};
// Emit this new event for the browser extension to listen for
this.getHostElement().dispatchEvent(new CustomEvent('umb:debug-contexts:data', { detail: data, bubbles: true }));
};
hostDisconnected(): void {
super.hostDisconnected();
this.getHostElement().removeEventListener(
umbDebugContextEventType,
this.#onContextDebug as unknown as EventListener,
);
}
}

View File

@@ -1,2 +1,3 @@
export * from './debug.element.js';
export * from './context-debug.controller.js';
export * from './manifests.js';

View File

@@ -5,7 +5,7 @@ import {
UmbModalManagerContext,
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
} from '@umbraco-cms/backoffice/modal';
import { UMB_AUTH_CONTEXT, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
interface MediaPickerTargetData {
altText?: string;
@@ -26,9 +26,9 @@ interface MediaPickerResultData {
export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
#mediaHelper: UmbMediaHelper;
#currentUser?: UmbLoggedInUser;
#currentUser?: UmbCurrentUser;
#modalContext?: UmbModalManagerContext;
#auth?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
constructor(args: TinyMcePluginArguments) {
super(args);
@@ -41,8 +41,8 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
// TODO => this breaks tests. disabling for now
// will ignore user media start nodes
// this.host.consumeContext(UMB_AUTH, (instance) => {
// this.#auth = instance;
// this.host.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
// this.#currentUserContext = instance;
// this.#observeCurrentUser();
// });
@@ -55,9 +55,9 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
}
async #observeCurrentUser() {
if (!this.#auth) return;
if (!this.#currentUserContext) return;
this.observe(this.#auth.currentUser, (currentUser) => (this.#currentUser = currentUser));
this.observe(this.#currentUserContext.currentUser, (currentUser) => (this.#currentUser = currentUser));
}
async #onAction() {

View File

@@ -1,26 +1,26 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
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 UmbLitElement {
#auth?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
@state()
private name = '';
constructor() {
super();
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#auth = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this.#observeCurrentUser();
});
}
#observeCurrentUser(): void {
if (!this.#auth) return;
this.observe(this.#auth.currentUser, (user) => {
if (!this.#currentUserContext) return;
this.observe(this.#currentUserContext.currentUser, (user) => {
this.name = user?.name ?? '';
});
}

View File

@@ -6,39 +6,43 @@ import {
UMB_CURRENT_USER_MODAL,
} from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UMB_AUTH_CONTEXT, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
@customElement('umb-current-user-header-app')
export class UmbCurrentUserHeaderAppElement extends UmbLitElement {
@state()
private _currentUser?: UmbLoggedInUser;
private _currentUser?: UmbCurrentUser;
private _auth?: typeof UMB_AUTH_CONTEXT.TYPE;
private _modalContext?: UmbModalManagerContext;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
#modalManagerContext?: UmbModalManagerContext;
constructor() {
super();
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
this.#modalManagerContext = instance;
});
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this._auth = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this._observeCurrentUser();
});
}
private async _observeCurrentUser() {
if (!this._auth) return;
if (!this.#currentUserContext) return;
this.observe(this._auth.currentUser, (currentUser) => {
this._currentUser = currentUser;
}, 'umbCurrentUserObserver');
this.observe(
this.#currentUserContext.currentUser,
(currentUser) => {
this._currentUser = currentUser;
},
'umbCurrentUserObserver',
);
}
private _handleUserClick() {
this._modalContext?.open(UMB_CURRENT_USER_MODAL);
this.#modalManagerContext?.open(UMB_CURRENT_USER_MODAL);
}
render() {

View File

@@ -0,0 +1,72 @@
import { UmbCurrentUser } from './types.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { UserResource } from '@umbraco-cms/backoffice/backend-api';
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { umbLocalizationRegistry } from '@umbraco-cms/backoffice/localization';
export class UmbCurrentUserContext extends UmbBaseController {
#currentUser = new UmbObjectState<UmbCurrentUser | undefined>(undefined);
readonly currentUser = this.#currentUser.asObservable();
readonly languageIsoCode = this.#currentUser.asObservablePart((user) => user?.languageIsoCode ?? 'en-us');
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
constructor(host: UmbControllerHost) {
super(host);
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#authContext = instance;
this.#observeIsAuthorized();
});
// TODO: revisit this. It can probably be simplified
this.observe(umbLocalizationRegistry.isDefaultLoaded, (isDefaultLoaded) => {
if (!isDefaultLoaded) return;
this.observe(
this.languageIsoCode,
(currentLanguageIsoCode) => {
umbLocalizationRegistry.loadLanguage(currentLanguageIsoCode);
},
'umbCurrentUserLanguageIsoCode',
);
});
this.provideContext(UMB_CURRENT_USER_CONTEXT, this);
}
async requestCurrentUser() {
// TODO: use repository
const { data, error } = await tryExecuteAndNotify(this._host, UserResource.getUserCurrent());
// TODO: add current user store
this.#currentUser.next(data);
return { data, error };
}
/**
* Checks if a user is the current user.
*
* @param userId The user id to check
* @returns True if the user is the current user, otherwise false
*/
async isUserCurrentUser(userId: string): Promise<boolean> {
const currentUser = await firstValueFrom(this.currentUser);
return currentUser?.id === userId;
}
#observeIsAuthorized() {
if (!this.#authContext) return;
this.observe(this.#authContext.isAuthorized, (isAuthorized) => {
if (isAuthorized) {
this.requestCurrentUser();
}
});
}
}
export const UMB_CURRENT_USER_CONTEXT = new UmbContextToken<UmbCurrentUserContext>('UmbCurrentUserContext');

View File

@@ -1,3 +1,5 @@
// TODO:Do not export store, but instead export future repository
export * from './current-user-history.store.js';
export * from './utils/index.js';
export * from './current-user.context.js';
export * from './types.js';

View File

@@ -1,3 +1,4 @@
import { UmbCurrentUserContext } from './current-user.context.js';
import { manifests as modalManifests } from './modals/manifests.js';
import { manifests as userProfileAppsManifests } from './user-profile-apps/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
@@ -9,6 +10,12 @@ export const headerApps: Array<ManifestTypes> = [
name: 'Current User Store',
loader: () => import('./current-user-history.store.js'),
},
{
type: 'globalContext',
alias: 'Umb.GlobalContext.CurrentUser',
name: 'Current User',
api: UmbCurrentUserContext,
},
{
type: 'headerApp',
alias: 'Umb.HeaderApp.CurrentUser',

View File

@@ -1,9 +1,11 @@
import { UMB_APP } from '@umbraco-cms/backoffice/app';
import { UMB_AUTH_CONTEXT, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT } from '../../current-user.context.js';
import { UmbCurrentUser } from '../../types.js';
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbModalContext } from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
@customElement('umb-current-user-modal')
export class UmbCurrentUserModalElement extends UmbLitElement {
@@ -11,31 +13,39 @@ export class UmbCurrentUserModalElement extends UmbLitElement {
modalContext?: UmbModalContext;
@state()
private _currentUser?: UmbLoggedInUser;
private _currentUser?: UmbCurrentUser;
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#appContext?: typeof UMB_APP.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
#appContext?: typeof UMB_APP_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#authContext = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this._observeCurrentUser();
});
this.consumeContext(UMB_APP, (instance) => {
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#authContext = instance;
});
this.consumeContext(UMB_APP_CONTEXT, (instance) => {
this.#appContext = instance;
});
}
private async _observeCurrentUser() {
if (!this.#authContext) return;
if (!this.#currentUserContext) return;
this.observe(this.#authContext.currentUser, (currentUser) => {
this._currentUser = currentUser;
}, 'umbCurrentUserObserver');
this.observe(
this.#currentUserContext.currentUser,
(currentUser) => {
this._currentUser = currentUser;
},
'umbCurrentUserObserver',
);
}
private _close() {

View File

@@ -0,0 +1 @@
export type { CurrentUserResponseModel as UmbCurrentUser } from '@umbraco-cms/backoffice/backend-api';

View File

@@ -6,35 +6,39 @@ import {
UMB_CHANGE_PASSWORD_MODAL,
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
} from '@umbraco-cms/backoffice/modal';
import { UMB_AUTH_CONTEXT, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
@customElement('umb-user-profile-app-profile')
export class UmbUserProfileAppProfileElement extends UmbLitElement {
@state()
private _currentUser?: UmbLoggedInUser;
private _currentUser?: UmbCurrentUser;
private _modalContext?: UmbModalManagerContext;
private _auth?: typeof UMB_AUTH_CONTEXT.TYPE;
#modalManagerContext?: UmbModalManagerContext;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
this.#modalManagerContext = instance;
});
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this._auth = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this._observeCurrentUser();
});
}
private async _observeCurrentUser() {
if (!this._auth) return;
if (!this.#currentUserContext) return;
this.observe(this._auth.currentUser, (currentUser) => {
this._currentUser = currentUser;
}, 'umbCurrentUserObserver');
this.observe(
this.#currentUserContext.currentUser,
(currentUser) => {
this._currentUser = currentUser;
},
'umbCurrentUserObserver',
);
}
private _edit() {
@@ -44,9 +48,9 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement {
//TODO Implement modal routing for the current-user-modal, so that the modal closes when navigating to the edit profile page
}
private _changePassword() {
if (!this._modalContext) return;
this._modalContext.open(UMB_CHANGE_PASSWORD_MODAL, {
if (!this.#modalManagerContext) return;
this.#modalManagerContext.open(UMB_CHANGE_PASSWORD_MODAL, {
userId: this._currentUser?.id ?? '',
});
}

View File

@@ -1,13 +1,13 @@
import { IUmbAuth, UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT } from '../current-user.context.js';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export const isCurrentUser = async (host: UmbControllerHost, userId: string) => {
let authContext: IUmbAuth | undefined = undefined;
let currentUserContext: typeof UMB_CURRENT_USER_CONTEXT.TYPE | undefined;
await new UmbContextConsumerController(host, UMB_AUTH_CONTEXT, (context) => {
authContext = context;
await new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (context) => {
currentUserContext = context;
}).asPromise();
return await authContext!.isUserCurrentUser(userId);
return await currentUserContext!.isUserCurrentUser(userId);
};

View File

@@ -1,11 +1,18 @@
import { UmbUserItemRepository } from '../../user/repository/item/user-item.repository.js';
import { UMB_CURRENT_USER_CONTEXT } from '../../current-user/current-user.context.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, CSSResultGroup, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbChangePasswordModalData, UmbChangePasswordModalValue, UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import {
UmbChangePasswordModalData,
UmbChangePasswordModalValue,
UmbModalBaseElement,
} from '@umbraco-cms/backoffice/modal';
@customElement('umb-change-password-modal')
export class UmbChangePasswordModalElement extends UmbModalBaseElement<UmbChangePasswordModalData, UmbChangePasswordModalValue> {
export class UmbChangePasswordModalElement extends UmbModalBaseElement<
UmbChangePasswordModalData,
UmbChangePasswordModalValue
> {
@state()
private _headline: string = 'Change password';
@@ -13,7 +20,7 @@ export class UmbChangePasswordModalElement extends UmbModalBaseElement<UmbChange
private _isCurrentUser: boolean = false;
#userItemRepository = new UmbUserItemRepository(this);
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
#onClose() {
this.modalContext?.reject();
@@ -41,8 +48,8 @@ export class UmbChangePasswordModalElement extends UmbModalBaseElement<UmbChange
constructor() {
super();
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#authContext = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this.#setIsCurrentUser();
});
}
@@ -53,12 +60,12 @@ export class UmbChangePasswordModalElement extends UmbModalBaseElement<UmbChange
return;
}
if (!this.#authContext) {
if (!this.#currentUserContext) {
this._isCurrentUser = false;
return;
}
this._isCurrentUser = await this.#authContext.isUserCurrentUser(this.data.userId);
this._isCurrentUser = await this.#currentUserContext.isUserCurrentUser(this.data.userId);
}
protected async firstUpdated(): Promise<void> {

View File

@@ -1,4 +1,4 @@
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT } from '../../current-user/current-user.context.js';
import { UmbBaseController } from '@umbraco-cms/backoffice/controller-api';
import {
ManifestCondition,
@@ -17,11 +17,15 @@ export class UmbUserPermissionCondition extends UmbBaseController implements Umb
this.config = args.config;
this.#onChange = args.onChange;
this.consumeContext(UMB_AUTH_CONTEXT, (context) => {
this.observe(context.currentUser, (currentUser) => {
this.permitted = currentUser?.permissions?.includes(this.config.match) || false;
this.#onChange();
}, 'umbUserPermissionConditionObserver');
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => {
this.observe(
context.currentUser,
(currentUser) => {
this.permitted = currentUser?.permissions?.includes(this.config.match) || false;
this.#onChange();
},
'umbUserPermissionConditionObserver',
);
});
}
}

View File

@@ -4,7 +4,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UUISelectElement } from '@umbraco-cms/backoffice/external/uui';
import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_AUTH_CONTEXT, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUser } from '@umbraco-cms/backoffice/current-user';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
@@ -14,19 +14,19 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement {
private _user?: UserResponseModel;
@state()
private _currentUser?: UmbLoggedInUser;
private _currentUser?: UmbCurrentUser;
@state()
private languages: Array<{ name: string; value: string; selected: boolean }> = [];
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
#userWorkspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_AUTH_CONTEXT, (instance) => {
this.#authContext = instance;
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
this.#observeCurrentUser();
});
@@ -45,42 +45,46 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement {
}
#observeCurrentUser() {
if (!this.#authContext) return;
this.observe(this.#authContext.currentUser, async (currentUser) => {
this._currentUser = currentUser;
if (!this.#currentUserContext) return;
this.observe(
this.#currentUserContext.currentUser,
async (currentUser) => {
this._currentUser = currentUser;
if (!currentUser) {
return;
}
if (!currentUser) {
return;
}
// Find all translations and make a unique list of iso codes
const translations = await firstValueFrom(umbExtensionsRegistry.extensionsOfType('localization'));
// Find all translations and make a unique list of iso codes
const translations = await firstValueFrom(umbExtensionsRegistry.extensionsOfType('localization'));
this.languages = translations
.filter((isoCode) => isoCode !== undefined)
.map((translation) => ({
value: translation.meta.culture.toLowerCase(),
name: translation.name,
selected: false,
}));
this.languages = translations
.filter((isoCode) => isoCode !== undefined)
.map((translation) => ({
value: translation.meta.culture.toLowerCase(),
name: translation.name,
selected: false,
}));
const currentUserLanguageCode = currentUser.languageIsoCode?.toLowerCase();
const currentUserLanguageCode = currentUser.languageIsoCode?.toLowerCase();
// Set the current user's language as selected
const currentUserLanguage = this.languages.find((language) => language.value === currentUserLanguageCode);
// Set the current user's language as selected
const currentUserLanguage = this.languages.find((language) => language.value === currentUserLanguageCode);
if (currentUserLanguage) {
currentUserLanguage.selected = true;
} else {
// If users language code did not fit any of the options. We will create an option that fits, named unknown.
// In this way the user can keep their choice though a given language was not present at this time.
this.languages.push({
value: currentUserLanguageCode ?? 'en-us',
name: currentUserLanguageCode ? `${currentUserLanguageCode} (unknown)` : 'Unknown',
selected: true,
});
}
}, 'umbUserObserver');
if (currentUserLanguage) {
currentUserLanguage.selected = true;
} else {
// If users language code did not fit any of the options. We will create an option that fits, named unknown.
// In this way the user can keep their choice though a given language was not present at this time.
this.languages.push({
value: currentUserLanguageCode ?? 'en-us',
name: currentUserLanguageCode ? `${currentUserLanguageCode} (unknown)` : 'Unknown',
selected: true,
});
}
},
'umbUserObserver',
);
}
render() {

View File

@@ -5,20 +5,20 @@ import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controlle
import type { UpdateUserRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextConsumerController, UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth';
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
export class UmbUserWorkspaceContext
extends UmbWorkspaceContext<UmbUserRepository, UmbUserDetail>
implements UmbSaveableWorkspaceContextInterface<UmbUserDetail | undefined>
{
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
constructor(host: UmbControllerHostElement) {
super(host, 'Umb.Workspace.User', new UmbUserRepository(host));
new UmbContextConsumerController(host, UMB_AUTH_CONTEXT, (auth) => {
this.#authContext = auth;
new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT, (instance) => {
this.#currentUserContext = instance;
});
}
@@ -82,10 +82,10 @@ export class UmbUserWorkspaceContext
}
async #reloadCurrentUser(savedUserId: string): Promise<void> {
if (!this.#authContext) return;
const currentUser = await firstValueFrom(this.#authContext.currentUser);
if (!this.#currentUserContext) return;
const currentUser = await firstValueFrom(this.#currentUserContext.currentUser);
if (currentUser?.id === savedUserId) {
await this.#authContext.fetchCurrentUser();
await this.#currentUserContext.requestCurrentUser();
}
}

View File

@@ -1,7 +1,8 @@
import type { UmbLoggedInUser } from './types.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import { Observable } from '@umbraco-cms/backoffice/external/rxjs';
export interface IUmbAuthContext {
isAuthorized: Observable<boolean>;
export interface IUmbAuth {
/**
* Initiates the login flow.
*/
@@ -15,7 +16,7 @@ export interface IUmbAuth {
/**
* Checks if there is a token and it is still valid.
*/
isAuthorized(): boolean;
getIsAuthorized(): boolean;
/**
* Gets the latest token from the Management API.
@@ -33,28 +34,8 @@ export interface IUmbAuth {
*/
getLatestToken(): Promise<string>;
/**
* Get the current user model of the current user.
*/
get currentUser(): Observable<UmbLoggedInUser | undefined>;
/**
* Get the current user's language ISO code.
*/
languageIsoCode: Observable<string>;
/**
* Make a server request for the current user and save the state
*/
fetchCurrentUser(): Promise<UmbLoggedInUser | undefined>;
/**
* Signs the user out by removing any tokens from the browser.
*/
signOut(): Promise<void>;
/**
* Check if the given user is the current user.
*/
isUserCurrentUser(userId: string): Promise<boolean>;
}

View File

@@ -0,0 +1,4 @@
import { IUmbAuthContext } from './auth.context.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_AUTH_CONTEXT = new UmbContextToken<IUmbAuthContext>('UmbAuthContext');

View File

@@ -1,32 +1,25 @@
import { IUmbAuth } from './auth.interface.js';
import { IUmbAuthContext } from './auth.context.interface.js';
import { UmbAuthFlow } from './auth-flow.js';
import { UmbLoggedInUser } from './types.js';
import { UserResource } from '@umbraco-cms/backoffice/backend-api';
import { UMB_AUTH_CONTEXT } from './auth.context.token.js';
import { UmbBaseController, UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
export class UmbAuthContext extends UmbBaseController implements IUmbAuth {
export class UmbAuthContext extends UmbBaseController implements IUmbAuthContext {
#isAuthorized = new UmbBooleanState<boolean>(false);
readonly isAuthorized = this.#isAuthorized.asObservable();
#currentUser = new UmbObjectState<UmbLoggedInUser | undefined>(undefined);
readonly currentUser = this.#currentUser.asObservable();
#isLoggedIn = new UmbBooleanState<boolean>(false);
readonly isLoggedIn = this.#isLoggedIn.asObservable();
readonly languageIsoCode = this.#currentUser.asObservablePart((user) => user?.languageIsoCode ?? 'en-us');
#isBypassed = false;
#backofficePath: string;
#authFlow;
constructor(host: UmbControllerHostElement, serverUrl: string, redirectUrl: string) {
super(host)
this.#authFlow = new UmbAuthFlow(serverUrl, redirectUrl);
constructor(host: UmbControllerHostElement, serverUrl: string, backofficePath: string, isBypassed: boolean) {
super(host);
this.#isBypassed = isBypassed;
this.#backofficePath = backofficePath;
this.observe(this.isLoggedIn, (isLoggedIn) => {
if (isLoggedIn) {
this.fetchCurrentUser();
}
});
this.#authFlow = new UmbAuthFlow(serverUrl, this.#getRedirectUrl());
this.provideContext(UMB_AUTH_CONTEXT, this);
}
/**
@@ -36,27 +29,29 @@ export class UmbAuthContext extends UmbBaseController implements IUmbAuth {
return this.#authFlow.makeAuthorizationRequest();
}
/* TEMPORARY METHOD UNTIL RESPONSIBILITY IS MOVED TO CONTEXT */
setLoggedIn(newValue: boolean): void {
return this.#isLoggedIn.next(newValue);
}
isAuthorized() {
return this.#authFlow.isAuthorized();
/**
* Checks if the user is authorized. If Authorization is bypassed, the user is always authorized.
* @returns {boolean} True if the user is authorized, otherwise false.
*/
getIsAuthorized() {
if (this.#isBypassed) {
this.#isAuthorized.next(true);
return true;
} else {
const isAuthorized = this.#authFlow.isAuthorized();
this.#isAuthorized.next(isAuthorized);
return isAuthorized;
}
}
/**
* Sets the initial state of the auth flow.
* @returns {Promise<void>}
*/
setInitialState(): Promise<void> {
return this.#authFlow.setInitialState();
}
async fetchCurrentUser(): Promise<UmbLoggedInUser | undefined> {
const { data } = await tryExecuteAndNotify(this._host, UserResource.getUserCurrent());
this.#currentUser.next(data);
return data;
}
/**
* Gets the latest token from the Management API.
* If the token is expired, it will be refreshed.
@@ -71,19 +66,14 @@ export class UmbAuthContext extends UmbBaseController implements IUmbAuth {
/**
* Signs the user out by removing any tokens from the browser.
* @return {*} {Promise<void>}
* @memberof UmbAuthContext
*/
signOut(): Promise<void> {
return this.#authFlow.signOut();
}
/**
* Checks if a user is the current user.
*
* @param userId The user id to check
* @returns True if the user is the current user, otherwise false
*/
async isUserCurrentUser(userId: string): Promise<boolean> {
const currentUser = await firstValueFrom(this.currentUser);
return currentUser?.id === userId;
#getRedirectUrl() {
return `${window.location.origin}${this.#backofficePath}`;
}
}

View File

@@ -1,6 +0,0 @@
import { IUmbAuth } from './auth.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_AUTH_CONTEXT = new UmbContextToken<IUmbAuth>(
'UmbAuthContext'
);

View File

@@ -1,5 +1,3 @@
export * from './auth.interface.js';
export * from './auth.context.interface.js';
export * from './auth.context.js';
export * from './types.js';
export * from './auth.token.js';
export * from './auth.context.token.js';

View File

@@ -1 +0,0 @@
export type { CurrentUserResponseModel as UmbLoggedInUser } from '@umbraco-cms/backoffice/backend-api';