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
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.
| Property | Type | Description |
|---|---|---|
IsAuthenticated | boolean | User has a valid auth token |
IsAdmin | boolean | User belongs to the Admin system group |
IsSysAdmin | boolean | User belongs to the SysAdmin system group |
HasAdminRights | boolean | Read-only getter: IsAuthenticated && (IsAdmin || IsSysAdmin) |
IsActive | boolean | User account is activated (not deactivated by admin) |
Need2FACheck | boolean | Two-factor authentication still needs validation this session |
IsMobile | boolean | Request came from a mobile device |
IsIOS | boolean | Request came from an iOS device |
Methods
| Method | Returns | Description |
|---|---|---|
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() | void | Resets all flags to false and clears the user object. |
getUserProfile() | User | Returns the full User object (name, email, groups, image, etc.) |
getGroups() | ISystemGroups | Returns references to the five system group objects |
getCurrentToken() | string | Returns 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
- Detects mobile/iOS from the HTTP request
- Creates
AuthManagerConnectorviainitAuthClient() - Checks
auth.IsAuthenticated()— validates the current token - If authenticated:
- Fetches system groups (
auth.getSystemGroups()) - Checks Admin and SysAdmin membership (
auth.HasGroup()) - Fetches user profile (
auth.getUserProfile(true)) - Sets
IsActivefromuser.Activated - Checks 2FA status (
auth.Has2FAActivated()) - If 2FA is active, checks browser persistence for prior validation
- Fetches system groups (
- 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();
}
}
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);
}
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
| File | Package | Purpose |
|---|---|---|
UserContext.ts | io-oidis-hub-gui | Main class definition |
Loader.ts | io-oidis-hub-gui | initContext(), getContext() |
BasePageLayoutController.ts | io-oidis-hub-gui | this.user assignment, Load() call |
BasePanelController.ts | io-oidis-hub-gui | this.context assignment |
AuthManagerConnector.ts | io-oidis-hub-gui | ISystemGroups, frontend auth API |
SSOBinding.ts | io-oidis-hub-gui | 2FA flow using UserContext |
UserContext.ts | io-oidis-home-gui | Extension example (IsChild, CanControlCabinet) |
See also: Authentication | User Profile | System Groups | Panels and Controllers