mesauth-angular
v1.53.0
Published
Angular helper library for MesAuth: user auth, SignalR notifications, approval workflow, AI chat panel with custom client tools, and dark/light theme support. See INTEGRATION.md for setup guidance.
Maintainers
Readme
mesauth-angular
Angular helper library to connect to a backend API and SignalR hub to surface the current logged-in user and incoming notifications with dark/light theme support.
Changelog
v1.22.0 (2026-05-16) — AI panel push-layout mode
- AI chat panel now pushes the host layout by default. When the panel opens,
<body>getspadding-inline-end: var(--ma-ai-panel-width)so the host app reflows to make room — wide content (tables, charts) is no longer hidden under the drawer. The library self-injects the global stylesheet on first construction; zero changes required in consumer apps. - New config flag
panelMode: 'static' | 'floating'onprovideMesAuthAi(). Default is'static'(push-layout). Pass'floating'to keep the v1.21 overlay behaviour for hosts whose layout can't reflow safely. - CSS variable
--ma-ai-panel-widthis set live during drag-resize, so the host reflows smoothly as the user widens/narrows the panel.
v1.21.0 (2026-05-16) — AI Assistant integration docs
- Documentation only: full integration guide for the AI Assistant module added below (jump). CHANGELOG backfilled for v1.19.0 + v1.20.0 entries.
v1.20.0 (2026-05-16) — Ollama + Markdown + same-layer panel
- Ollama is the default LLM provider. Backend
OpenAiCompatibleLlmClientPOSTs to${BaseUrl}/chat/completions— works against Ollama (http://localhost:11434/v1), OpenAI, LM Studio, vLLM, Anthropic's OpenAI-compat endpoint, OpenRouter. Tool-callargumentsparsed in both JSON-string (spec) and parsed-object (Ollama) shapes. - Markdown rendering inside assistant bubbles via new
MaAiMarkdownPipe. Supports GFM pipe tables, fenced + inline code, lists, blockquotes, bold/italic, sanitized links. URL allowlist blocksjavascript:/data:. Pipe applies only to assistant bubbles — user input stays plain text. quickPromptsmanifest field: hosted-manifest can override the AI panel empty-state suggestions per deployment without republishing the npm package.- AI chat panel is now same-layer with the host app — backdrop removed;
z-index: 1040(above page chrome, below CoreUI modals at 1055). Page stays fully interactive while the panel is open.
v1.19.0 (2026-05-16) — AI Assistant module
- New
src/ai/module:MaAiButtonComponent,MaAiChatPanelComponent(right-side resizable drawer, 320–800 px),MaAiService(SSE viafetch+ReadableStream),MaAiToolsRegistry,provideMesAuthAi(config)provider. - Auto-wired into
ma-usernext to the bell/approval buttons. Toggle off with[showAi]="false"on<ma-user>. - Built-in client tools shipped:
navigate,toggle_theme,complete,who_am_i_client,reload_page. Consumer apps register additional tools (incl. write-class) viaprovideMesAuthAi({ tools: [...] })— write tools surface an in-panel approval card. - Backend:
POST /ai/chatSSE endpoint in MesAuth.Api with 7 curated server tools + 3 universal tools (list_apps,list_app_functions,call_app_endpoint) that proxy to consumer apps using eachApplication.HostUrl.
v1.6.8 (2026-04-01) - Permission Header Support
- New
withXMaPerm()RxJS operator: ExtractsX-MA-PERMpermission header from HTTP responses, returning{ data, allowedActions }. Use withobserve: 'response'on anyHttpClientcall for zero-overhead permission-gated UI. - New
xMaResource()helper: CombinesrxResourcewith automatic permission extraction for GET endpoints. Returns a signal-based resource with.value().allowedActions. - New
extractXMaPerm()utility: Standalone function to parse theX-MA-PERMheader from anyHttpResponse. - Wildcard
*expansion:extractXMaPermautomatically expands["*"]into all known HTTP methods (GET,POST,PUT,DELETE,PATCH,WEBSOCKET). ALL_ACTIONSconstant: Exported array of all recognized HTTP action strings for permission checks.- Exports:
withXMaPerm,xMaResource,extractXMaPerm,PermissionHeader,RequestConfig,ALL_ACTIONS.
v1.6.0 (2026-03-28) - Approval Module Enhancements
[templateId]locks routing UI: When bound, the routing mode toggle and template dropdown are hidden — the template name is shown as read-only. Role-based steps auto-load candidate pickers fromGET /approval/roles/preview; the requester selects one user per step before submitting. Pass as number binding:[templateId]="6".(approvalSubmitting)output (corrected from single-t typo): Fires before content capture starts. Use it to hide edit controls (*ngIf="!isSubmitting") so they are excluded from the approval snapshot. Angular CD runs after the emit so DOM updates are captured.- Automatic theme support:
<ma-arv-container>appliesThemeServiceinternally — adapts to the app's light/dark theme via@HostBinding('class')with no extra setup. previewRole(orgCode, level)added toMaApprovalService: Fetch candidate users for a role-based approval step.- Callback security: MesAuth.Api sends
X-APP-ID+X-APP-KEYheaders on every callback POST using its own app credentials. Protect callback endpoints with[MesAuth]fromMesAuth.Authorizer.
v1.5.0 (2026-03-24) - Approval Module
- New
<ma-approval-panel>component: Slide-out sidebar with 3 tabs (Processing / Approved / Rejected). Shows all pending approvals requiring action, and recent approved/rejected items. Listens toapprovalEvents$for real-time refresh via SignalR. - New
<ma-arv-container>component: Content capture container for submitting documents for approval. Captures projected<ng-content>as a self-contained HTML document by inlining all computed styles, replacing canvas elements with images, and stripping Angular/script artifacts. Supports ad-hoc step builder and template selector.[templateId]locks routing UI: When bound, the routing mode toggle and template dropdown are hidden — the template name is shown as read-only. Role-based steps auto-load candidate pickers fromGET /approval/roles/preview; the requester selects one user per step before submitting. Pass as number binding:[templateId]="6".(approvalSubmitting)output (corrected from single-t typo): Fires before content capture starts. Use it to hide edit controls (*ngIf="!isSubmitting") so they are excluded from the approval snapshot. Angular CD runs after the emit so DOM updates are captured.- Automatic theme support: Applies
ThemeServiceinternally — adapts to the app's light/dark theme via@HostBinding('class')with no extra setup.
- New
MaApprovalService: Service for all approval API calls —getPendingApprovals(),getMyRequests(),getDashboard(),approve(),reject(),delegate(),getTemplates(),createApproval(),previewRole(orgCode, level), etc. Manual init pattern (same asMesAuthService). - Approval icon in
ma-user-profile: Clipboard/checkmark icon button added between notification bell and avatar. Shows pending count badge. EmitsapprovalClickoutput for panel toggle. approvalEvents$observable inMesAuthService: Real-time SignalR events (ApprovalCompleted,ApprovalStepChanged) exposed as an observable stream.- Callback security: MesAuth.Api sends
X-APP-ID+X-APP-KEYheaders on every callback POST using its own app credentials. Protect callback endpoints with[MesAuth]fromMesAuth.Authorizer. - New exports:
MaApprovalService,MaApprovalPanelComponent,MaArvContainerComponent, and all approval model interfaces/enums.
v1.4.0 (2026-03-20) - Remove Unused Route Registration API
- Removed
registerRoutes(),unregisterRoute(),getRoutesByRole()and related methods fromMesAuthService. Frontend route master/mapping CRUD is handled byMesExtensionSitevia direct HTTP calls, not through the library. getFrontEndRoutes()andUserFrontEndRoutesGrouped/FrontEndRouteinterfaces are retained as the public API for consuming route data.
v1.2.3 (2026-02-11) - Fix Register Page Auto-Redirect
- Fixed 401 redirect on public pages: The interceptor now skips the login redirect when the user is on
/register,/forgot-password, or/reset-passwordpages. Previously, unauthenticated users on the register page were incorrectly redirected to login when any API call returned 401.
v1.2.2 (2026-02-06) - Z-Index Fix for Notification UI
- Fixed notification panel z-index: Increased from
1000to1030to appear above CoreUI sticky table headers (z-index: 1020) - Fixed modal overlay z-index: Increased from
9999to1060to maintain proper layering hierarchy - Better integration with CoreUI/Bootstrap: Follows standard z-index scale (sticky: 1020, modals: 1050+, overlays: 1060+)
v1.2.0 (2026-02-05) - Notification & Auth Interceptor Fix
- Removed route-change user polling:
MesAuthServiceno longer re-fetches the user on everyNavigationEndevent. User is fetched once on app init; SignalR handles real-time updates. This eliminates redundant API calls and notification toast spam on every route change. - Removed
fetchInitialNotifications(): Historical notifications were being emitted throughnotifications$Subject on every user refresh, causing toast popups for old notifications. Thenotifications$observable now only carries truly new real-time events from SignalR. refreshUser()returnsObservable: Callers can now subscribe and wait for user data to load before proceeding (e.g., navigate after login). Previously returnedvoid.- Fixed 401 redirect for expired sessions: Removed the
!isAuthenticatedguard from the interceptor's 401 condition. When a session expires, theBehaviorSubjectstill holds stale user data, so this check was blocking the redirect to login. The!isMeAuthPage,!isLoginPage, and!isAuthPageguards are sufficient to prevent redirect loops.
v1.1.0 (2026-01-21) - Major Update
- 🚀 New
provideMesAuth()Function: Simplified setup with a single function call - ✨ Functional Interceptor: New
mesAuthInterceptorfor better compatibility with standalone apps - 📦 Automatic Initialization:
provideMesAuth()handles service initialization viaAPP_INITIALIZER - 🔧 Simplified API: Just pass
apiBaseUrlanduserBaseUrl- no manual DI required
v1.0.1 (2026-01-21)
- 🔧 Internal refactoring for better module compatibility
v0.2.28 (2026-01-19)
- ✨ Enhanced Avatar Support: Direct
avatarPathusage from user data for instant display without backend calls - 🔄 Improved Avatar Refresh: Timestamp-based cache busting prevents request cancellation issues
- 🎯 Better Change Detection: Signal-based user updates with
ChangeDetectorReffor reliable UI updates
v0.2.27 (2026-01-19)
- 🐛 Fixed avatar refresh issues in header components
- 📦 Improved build process and dependencies
Features
- 🔐 Authentication: User login/logout with API integration
- 🔔 Real-time Notifications: SignalR integration for live notifications
- ✅ Approval Workflows:
<ma-approval-panel>,<ma-arv-container>,MaApprovalServicefor multi-step document approval - 🤖 AI Assistant: in-browser chat panel (Ollama by default) with markdown rendering, agentic tool loop, server + client tools — see AI Assistant
- 🎨 Dark/Light Theme: Automatic theme detection and support
- 🖼️ Avatar Support: Direct API-based avatar loading
- 🍞 Toast Notifications: In-app notification toasts
- 🛡️ HTTP Interceptor: Automatic 401/403 error handling with redirects
Quick Start (v1.1.0+)
1. Install
npm install mesauth-angular2. Configure in app.config.ts (Recommended for Angular 14+)
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideMesAuth, mesAuthInterceptor } from 'mesauth-angular';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([mesAuthInterceptor]) // Handles 401/403 redirects
),
provideMesAuth({
apiBaseUrl: 'https://auth.domain.com',
userBaseUrl: 'https://domain.com' // For login/403 redirects
})
]
};That's it! The library handles:
- Service initialization via
APP_INITIALIZER HttpClientandRouterinjection automatically- 401 → redirects to
{userBaseUrl}/login?returnUrl=... - 403 → redirects to
{userBaseUrl}/403?returnUrl=...
3. Use in Components
import { MesAuthService } from 'mesauth-angular';
@Component({...})
export class MyComponent {
private auth = inject(MesAuthService);
// Observable streams
currentUser$ = this.auth.currentUser$;
notifications$ = this.auth.notifications$;
logout() {
this.auth.logout().subscribe();
}
}Configuration Options
interface MesAuthConfig {
apiBaseUrl: string; // Required: MesAuth API base URL
userBaseUrl?: string; // Optional: Base URL for login/403 redirects
withCredentials?: boolean; // Optional: Send cookies (default: true)
}Theme Support
The library automatically detects and adapts to your application's theme:
Automatic Theme Detection
The library checks for theme indicators on the <html> element:
class="dark"data-theme="dark"theme="dark"data-coreui-theme="dark"
Dynamic Theme Changes
Theme changes are detected in real-time using MutationObserver, so components automatically update when your app switches themes.
Manual Theme Control
import { ThemeService } from 'mesauth-angular';
// Check current theme
const currentTheme = themeService.currentTheme; // 'light' | 'dark'
// Manually set theme
themeService.setTheme('dark');
// Listen for theme changes
themeService.currentTheme$.subscribe(theme => {
console.log('Theme changed to:', theme);
});Avatar Loading
Avatars are loaded efficiently using multiple strategies:
Primary Method: Direct Path Usage
If the user object contains an avatarPath, it's used directly:
- Full URLs: Used as-is (e.g.,
https://example.com/avatar.jpg) - Relative Paths: Combined with API base URL (e.g.,
/uploads/avatar.jpg→{apiBaseUrl}/uploads/avatar.jpg)
Fallback Method: API Endpoint
If no avatarPath is available, avatars are loaded via API:
- API Endpoint:
GET {apiBaseUrl}/auth/{userId}/avatar - Authentication: Uses the same credentials as other API calls
Cache Busting
Avatar URLs include timestamps to prevent browser caching issues during updates:
- Automatic refresh when user data changes
- Manual refresh triggers for upload/delete operations
Fallback Service
- UI Avatars: Generates initials-based avatars if no user data available
- Authentication: Not required for fallback avatars
Components
Note: All components are standalone and can be imported directly.
ma-user-profile
A reusable Angular component for displaying the current user's profile information, with options for navigation and logout.
Description: Renders user details (e.g., name, avatar) fetched via the MesAuthService. Supports custom event handlers for navigation and logout actions.
Inputs: None (data is sourced from the MesAuthService).
Outputs:
onNavigate: Emits an event when the user triggers navigation (e.g., to a profile page). Pass a handler to define behavior.onLogout: Emits an event when the user logs out. Pass a handler to perform logout logic (e.g., clear tokens, redirect).
Usage Example:
<ma-user-profile (onNavigate)="handleNavigation($event)" (onLogout)="handleLogout()"> </ma-user-profile>In your component's TypeScript file:
handleNavigation(event: any) { // Navigate to user profile page this.router.navigate(['/profile']); } handleLogout() { // Perform logout, e.g., clear session and redirect this.mesAuth.logout(); // Assuming a logout method exists this.router.navigate(['/login']); }
ma-notification-panel
A standalone component for displaying a slide-out notification panel with real-time updates.
Description: Shows a list of notifications, allows marking as read/delete, and integrates with toast notifications for new alerts.
Inputs: None.
Outputs: None (uses internal methods for actions).
Usage Example:
<ma-notification-panel #notificationPanel></ma-notification-panel>In your component:
// To open the panel notificationPanel.open();
AI Assistant
Since v1.19.0 the library ships an in-browser AI chat panel that lives inside <ma-user>. It calls MesAuth.Api's /ai/chat SSE endpoint, which fronts an LLM (Ollama by default) and runs an agentic loop with two kinds of tools: server tools (data queries — whoami, list_my_approvals, etc.) and client tools (UI actions the model triggers in the browser — navigation, dialogs, custom logic the consumer registers).
1. Backend prerequisite
MesAuth.Api must be running v10.x with the Ai:* config block populated. Default points at local Ollama:
// appsettings.json (MesAuth.Api)
"Ai": {
"Enabled": true,
"Provider": "ollama",
"BaseUrl": "http://localhost:11434/v1", // any OpenAI-compatible base URL
"ApiKey": "", // optional; required for OpenAI / Anthropic compat
"Model": "qwen2.5:7b", // any tool-calling capable model
"MaxToolCallsPerTurn": 12,
"MaxTokensPerResponse": 4096,
"SessionTtlMinutes": 120
}Install Ollama (brew install ollama / winget install Ollama.Ollama) then ollama pull qwen2.5:7b (or llama3.1:8b, mistral-nemo, etc.).
2. Frontend setup
// app.config.ts
import { ApplicationConfig, inject } from '@angular/core';
import { Router } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideMesAuth, provideMesAuthAi, mesAuthInterceptor } from 'mesauth-angular';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([mesAuthInterceptor])),
provideMesAuth({
apiBaseUrl: 'https://auth.domain.com',
userBaseUrl: 'https://domain.com'
}),
provideMesAuthAi({
enabled: true,
appName: 'MesExtensionSite', // sent to the AI as host context each turn
systemPromptExtensions: [
'You are inside the MES Extension app. Use SPC tools when the user asks about lines or charts.'
],
tools: [
// example custom client tool — read-only, no approval card
{
name: 'spc_open_chart',
description: 'Open the SPC chart for a given product line',
parameters: {
type: 'object',
properties: { lineId: { type: 'string', description: 'Production line id' } },
required: ['lineId']
},
readOnly: true,
handler: (args: { lineId: string }) => {
inject(Router).navigateByUrl(`/spc/${args.lineId}`);
return `Opened SPC chart for line ${args.lineId}`;
}
}
]
})
]
};That's all that's needed — the AI button appears in the existing <ma-user> header and the chat panel slides in from the right when clicked. No template changes required in the consumer app.
3. Built-in client tools
| Tool | What it does |
|---|---|
| navigate | In-site routes use router.navigateByUrl(path); cross-site routes (target siteUrl differs from the configured siteUrl) prompt the user, then open in a new tab |
| toggle_theme | Flip between light/dark via ThemeService |
| complete | Record a persistent completion summary (shown as a chip, no popup) |
| who_am_i_client | Returns the current user from the browser (synchronous, no API call) |
| reload_page | window.location.reload() (write-class — surfaces approval card) |
Override / extend by passing your own tools in provideMesAuthAi({ tools }). Consumer tools always take precedence over built-ins of the same name.
4. Server-tool catalogue (provided by MesAuth.Api)
| Tool | Description |
|---|---|
| whoami | Identity, roles, department of the signed-in user |
| list_users | Search users by name / department / employee code |
| list_my_roles | Roles assigned to the caller |
| list_my_approvals | Pending approval documents waiting on the caller |
| list_my_notifications | Caller's recent notifications (unread by default) |
| list_health_endpoints | Registered health endpoints + latest probe status |
| current_time | Server time (UTC + Vietnam local) |
| list_apps | Apps the caller has any permission in |
| list_app_functions | Paginated endpoints in an app the caller can call (driven by the Authorizer-registered permission catalogue) |
| call_app_endpoint | Universal proxy — calls any registered endpoint in any consumer app on the caller's behalf. Permission is re-checked before the call. Requires Application.HostUrl to be set in Auth → Client Apps. |
5. Customising the empty-state suggestions
Edit wwwroot/mesauth-angular/v1/manifest.json on the MesAuth.Api server (no npm republish needed):
{
"version": "1.21.0",
"quickPrompts": [
"Show my pending approvals",
"Who is the supervisor of line A1?",
"How do I add a new user role?"
]
}6. Write tools + approval cards
A consumer-registered tool with readOnly: false (or omitted) surfaces an in-panel approval card showing the constructed args before the call runs. The user can choose Approve, Always (remembers the verb for this session), or Decline. Built-in writes (reload_page) follow the same flow.
7. Disabling the AI feature
Three knobs:
- Per app:
provideMesAuthAi({ enabled: false })hides the button and the panel. - Per server: set
Ai:Enabled = falsein MesAuth.Api appsettings — the endpoint returns 503 and the panel shows a friendly error. - Per
<ma-user>:<ma-user [showAi]="false">hides the button but keeps the service available for programmatic use.
8. Cost / safety controls
- Server-side cap on tool calls per turn (
Ai:MaxToolCallsPerTurn, default 12). - 8 KB truncation on every tool result fed back to the LLM (prevents context blow-up).
- Write client tools always require user approval before executing.
- Server tools re-check user permissions via
IFuncPermServicebefore running — the AI cannot bypass authorization. call_app_endpointforwards the caller's JWT to consumer apps, so the consumer'sMesAuth.Authorizermiddleware enforces the same permission gate it would for a normal request.
Migration Guide
Upgrading from v0.x to v1.1.0+
The setup has been greatly simplified. Here's how to migrate:
Before (v0.x):
// app.config.ts - OLD WAY
import { MesAuthModule, MesAuthService } from 'mesauth-angular';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
importProvidersFrom(MesAuthModule),
{ provide: HTTP_INTERCEPTORS, useClass: MesAuthInterceptor, multi: true }
]
};
// app.component.ts - OLD WAY
export class AppComponent {
constructor() {
this.mesAuthService.init({
apiBaseUrl: '...',
userBaseUrl: '...'
}, inject(HttpClient), inject(Router));
}
}After (v1.1.0+):
// app.config.ts - NEW WAY (everything in one place!)
import { provideMesAuth, mesAuthInterceptor } from 'mesauth-angular';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([mesAuthInterceptor])),
provideMesAuth({
apiBaseUrl: 'https://auth.domain.com',
userBaseUrl: 'https://domain.com'
})
]
};
// app.component.ts - No init() needed!
export class AppComponent {
// Just inject and use - no manual initialization required
}Key Changes:
provideMesAuth()replacesMesAuthModule+ manualinit()callmesAuthInterceptor(functional) replacesMesAuthInterceptor(class-based)- No need to inject
HttpClientorRoutermanually - Configuration moved from
AppComponenttoapp.config.ts
Troubleshooting
JIT Compiler Error in Production or AOT Mode
If you encounter an error like "The injectable 'MesAuthService' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available," this typically occurs because:
- The package is being imported directly from source code (e.g., during development) without building it first.
- The client app is running in AOT (Ahead-of-Time) compilation mode, which requires pre-compiled libraries.
Solutions:
Build the package for production/AOT compatibility:
- Ensure you have built the package using
npm run build(which uses ng-packagr or similar to generate AOT-ready code). - Install the built package via npm (e.g., from a local tarball or registry) instead of linking to the source folder.
- Ensure you have built the package using
For development (if you must link to source):
- Switch your Angular app to JIT mode by bootstrapping with
@angular/platform-browser-dynamicinstead of@angular/platform-browser.
- Switch your Angular app to JIT mode by bootstrapping with
Verify imports:
- Ensure you're importing from the built package (e.g.,
import { MesAuthService } from 'mesauth-angular';) and not from thesrcfolder.
- Ensure you're importing from the built package (e.g.,
Components Appear Empty
If components like ma-user or ma-user-profile render as empty:
- Ensure
provideMesAuth()is called in yourapp.config.ts. - Check browser console for logs from components.
- If
currentUseris null, the component shows a login button—verify the API returns user data.
Notes
- The service expects an endpoint
GET {apiBaseUrl}/auth/methat returns the current user. - Avatar endpoint:
GET {apiBaseUrl}/auth/{userId}/avatar - SignalR events used:
ReceiveNotification(adjust to your backend).
