Skip to main content

UserContext

UserContext is the runtime session object that every Oidis GUI application relies on. It holds the current user's authentication state, group membership flags, profile data, and device information. Created once by the Loader, populated on page load, and accessed throughout the component hierarchy via this.context in controllers or Loader.getInstance().getContext() globally.

Package: io-oidis-hub-gui

note

UserContext is the runtime bridge between Authentication (how users log in) and User Profile (what data is stored per user). It answers the question: "Who is the current user, and what can they do?"

Properties​

All properties are public and directly readable. They are populated by the Load() method.

PropertyTypeDescription
IsAuthenticatedbooleanUser has a valid auth token
IsAdminbooleanUser belongs to the Admin system group
IsSysAdminbooleanUser belongs to the SysAdmin system group
HasAdminRightsbooleanRead-only getter: IsAuthenticated && (IsAdmin || IsSysAdmin)
IsActivebooleanUser account is activated (not deactivated by admin)
Need2FACheckbooleanTwo-factor authentication still needs validation this session
IsMobilebooleanRequest came from a mobile device
IsIOSbooleanRequest came from an iOS device

Methods​

MethodReturnsDescription
Load()Promise<void>Populates all fields from the auth backend. Called once on page load.
Reload()Promise<void>Calls Restore() then Load(). Use after login, 2FA validation, or profile changes.
Restore()voidResets all flags to false and clears the user object.
getUserProfile()UserReturns the full User object (name, email, groups, image, etc.)
getGroups()ISystemGroupsReturns references to the five system group objects
getCurrentToken()stringReturns the active auth token for API calls
SwitchToProfile($group)Promise<void>Admin-only: switch between Admin/SysAdmin views without re-login

Lifecycle​

UserContext follows a well-defined lifecycle tied to the page load sequence:

1. Creation (Loader Constructor)​

// io-oidis-hub-gui/Loader.ts
export class Loader extends Parent {
private context : UserContext;

constructor() {
super();
this.context = this.initContext(); // Creates empty UserContext
}

protected initContext() : UserContext {
return new UserContext();
}

public getContext() : UserContext {
return this.context;
}
}

At this point, all flags are false and getUserProfile() returns null.

2. Population (Page Load)​

BasePageLayoutController.afterLoad() triggers the async population:

// BasePageLayoutController — simplified
protected async afterLoad() : Promise<void> {
await this.user.Load(); // this.user = Loader.getInstance().getContext()
// ... header strip, push notifications, release notes
}

3. What Load() Does Internally​

  1. Detects mobile/iOS from the HTTP request
  2. Creates AuthManagerConnector via initAuthClient()
  3. Checks auth.IsAuthenticated() — validates the current token
  4. If authenticated:
    • Fetches system groups (auth.getSystemGroups())
    • Checks Admin and SysAdmin membership (auth.HasGroup())
    • Fetches user profile (auth.getUserProfile(true))
    • Sets IsActive from user.Activated
    • Checks 2FA status (auth.Has2FAActivated())
    • If 2FA is active, checks browser persistence for prior validation
  5. If not authenticated:
    • Clears 2FA persistence

4. SPA Navigation​

UserContext persists throughout the single-page application session. It is not re-created on hash route changes. Only explicit calls to Reload() refresh the data.

5. Login / Logout​

  • Login: After successful authentication, Reload() is called to populate the context with the new user's data.
  • Logout: Navigates to /signOut, which triggers a server-side logout and full page reload (creating a fresh, empty UserContext).

Accessing UserContext​

In Page Layout Controllers​

BasePageLayoutController stores it as this.user (protected field):

export abstract class BasePageLayoutController extends AsyncBasePageController {
protected user : UserContext;

constructor() {
// ...
this.user = Loader.getInstance().getContext();
}
}
note

In BasePageLayoutController, the field is named this.user (not this.context) for historical reasons. In all other controllers and bindings, it is this.context.

In Panel Controllers​

BasePanelController sets this.context in its constructor:

export class BasePanelController extends Parent {
constructor($instance : Panel) {
super($instance);
this.context = Loader.getInstance().getContext();
}
}

From Anywhere​

Any code that has access to the Loader singleton can retrieve UserContext:

import { Loader } from "@io-oidis-hub-gui/.../Loader.js";

const context : UserContext = Loader.getInstance().getContext();
if (context.IsAuthenticated) {
const user = context.getUserProfile();
LogIt.Info("Current user:", user.UserName);
}

Common Usage Patterns​

Access Validation​

Controllers override accessValidator() to restrict page access:

export class DashboardPageController extends BasePageLayoutController {
protected async accessValidator() : Promise<boolean> {
await this.user.Reload();
return this.user.IsAuthenticated && this.user.IsAdmin;
}
}

Conditional UI Rendering​

Toggle UI elements based on authentication and role flags:

public ShowData() : void {
if (this.context.IsAuthenticated && !this.context.Need2FACheck) {
this.userControls.loginButton.Visible(false);
this.userControls.userMenu.Visible(true);
this.userControls.dashboardLink.Visible(this.context.IsAdmin);

const user : User = this.context.getUserProfile();
this.userControls.userName.Content(
ObjectValidator.IsEmptyOrNull(user.Email) ? user.UserName : user.Email
);
}
}

2FA Flow​

The 2FA challenge uses Need2FACheck combined with PersistenceHandler for session state:

// After successful 2FA validation
this.context.Need2FACheck = false;
this.persistence.Variable("last2FAResult", true); // Persist across page reloads

// On page load — check if 2FA was already validated
if (context.Need2FACheck) {
// Redirect to 2FA challenge
this.reloadTo2FAChallenge();
}

Profile Switching (Admin Only)​

Admins can switch their active role without re-authenticating:

// Switch to SysAdmin view
await this.user.SwitchToProfile(this.user.getGroups().SysAdmin);

// Switch to Admin view
await this.user.SwitchToProfile(this.user.getGroups().Admin);

// Reset to regular user view
await this.user.SwitchToProfile(null);

Extending UserContext​

Application projects can subclass UserContext to add project-specific flags and logic. This is the recommended pattern for role-based features beyond the standard Admin/SysAdmin distinction.

Step 1: Create a Subclass​

// YourProject/Gui/Structures/UserContext.ts
import { UserContext as Parent } from "@io-oidis-hub-gui/.../Structures/UserContext.js";
import { AuthManagerConnector, ISystemGroups } from "../Connectors/AuthManagerConnector.js";

export class UserContext extends Parent {
public IsChild : boolean;
public CanControlCabinet : boolean;

public async Load() : Promise<void> {
if (ObjectValidator.IsEmptyOrNull(this.getUserProfile())) {
await super.Load();
this.IsChild = false;
this.CanControlCabinet = false;
if (this.IsAuthenticated) {
this.IsChild = await this.getAuth().HasGroup(this.getGroups().Child);
if (this.IsChild) {
this.CanControlCabinet = await this.getAuth().IsAuthorizedFor("#Cabinet");
}
}
}
}

public Restore() : void {
super.Restore();
this.IsChild = false;
this.CanControlCabinet = false;
}

public getGroups() : ISystemGroups {
return <ISystemGroups>super.getGroups();
}

protected initAuthClient() : AuthManagerConnector {
return AuthManagerConnector.getClient();
}

protected getAuth() : AuthManagerConnector {
return <AuthManagerConnector>super.getAuth();
}
}

Step 2: Override Loader​

// YourProject/Gui/Loader.ts
import { Loader as Parent } from "@io-oidis-hub-gui/.../Loader.js";
import { UserContext } from "./Structures/UserContext.js";

export class Loader extends Parent {
public static getInstance() : Loader {
return <Loader>super.getInstance();
}

public getContext() : UserContext {
return <UserContext>super.getContext();
}

protected initContext() : UserContext {
return new UserContext();
}
}

Step 3: Use in Controllers​

// In your panel controller
const context = <UserContext>this.context;
if (context.IsChild && context.CanControlCabinet) {
this.userControls.cabinetPanel.Visible(true);
}
tip

If your project also extends AuthManagerConnector and ISystemGroups (to add custom system groups), override initAuthClient() and getAuth() in your UserContext subclass to return your extended connector type. This ensures type-safe access to custom groups via this.getGroups().YourCustomGroup.

Quick Reference​

FilePackagePurpose
UserContext.tsio-oidis-hub-guiMain class definition
Loader.tsio-oidis-hub-guiinitContext(), getContext()
BasePageLayoutController.tsio-oidis-hub-guithis.user assignment, Load() call
BasePanelController.tsio-oidis-hub-guithis.context assignment
AuthManagerConnector.tsio-oidis-hub-guiISystemGroups, frontend auth API
SSOBinding.tsio-oidis-hub-gui2FA flow using UserContext
UserContext.tsio-oidis-home-guiExtension example (IsChild, CanControlCabinet)

See also: Authentication | User Profile | System Groups | Panels and Controllers