@s-spektra-25/zoho-asap-widget
v1.0.0
Published
Standalone Angular library for Zoho Desk ASAP widget with built-in MSAL token exchange and session management
Maintainers
Readme
Zoho Desk ASAP Widget - Standalone Angular Library
A completely standalone Angular library (10+) that wraps the Zoho Desk ASAP 2.0 widget. This library handles everything internally: token exchange, authentication, session management, and storage clearing. Your app only needs to pass an MSAL token - no Zoho-specific code required.
🎯 Key Features
- ✅ Truly Standalone: Accepts MSAL token, handles JWT exchange internally
- ✅ Zero Zoho Dependencies in App: No AsapService, no JWT management, no storage clearing needed
- ✅ Multi-User Safe: Automatically detects user changes and clears sessions
- ✅ Smart Token Refresh: Distinguishes token refresh (same user) from user switch (different user)
- ✅ Automatic Session Management: Clears localStorage, sessionStorage, and cookies on logout
- ✅ Prefill Support: Automatic form prefilling with dynamic values
- ✅ Angular 10-18+ Compatible: Works with all modern Angular versions
- ✅ Production Ready: Clean code, no console logs, robust error handling
📦 Installation
npm install @cloudlabs/zoho-asap-widgetImport the module in your app:
import { ZohoAsapWidgetModule } from '@cloudlabs/zoho-asap-widget';
@NgModule({
imports: [ZohoAsapWidgetModule],
// ...
})
export class AppModule { }🚀 Basic Usage
Prerequisites: Your app must have MSAL (Microsoft Authentication Library) configured to authenticate users with Azure B2C or Azure AD. The widget requires an MSAL ID token.
<!-- Simply pass your MSAL token to the widget -->
<zoho-asap-widget
*ngIf="msalToken"
[appId]="'YOUR_ZOHO_APP_ID'"
[orgId]="'YOUR_ZOHO_ORG_ID'"
[msalToken]="msalToken"
[enabled]="true">
</zoho-asap-widget>That's it! The widget handles:
- Token exchange (MSAL → Zoho JWT)
- User authentication with Zoho
- Session management
- Storage cleanup on logout
- User switching detection
With Form Prefill
<zoho-asap-widget
*ngIf="msalToken"
[appId]="'YOUR_ZOHO_APP_ID'"
[orgId]="'YOUR_ZOHO_ORG_ID'"
[msalToken]="msalToken"
[enabled]="true"
[prefillFields]="prefillData"
[departmentId]="'YOUR_DEPT_ID'"
[layoutId]="'YOUR_LAYOUT_ID'">
</zoho-asap-widget>Where prefillData is an object in your component:
prefillData: any = {
subject: 'Support Request', // Standard Zoho field
cf_event_request_id: 'MS012345678910', // Custom field
};Dynamic prefill example:
// Set subject dynamically with user name
this.prefillData.subject = `Support Request from ${this.userDisplayName}`;
// Update prefill based on user context
if (isPremiumUser) {
this.prefillData = {
subject: `Priority Support - ${userName}`,
cf_priority: 'High',
cf_user_type: 'Premium'
};
}🔧 Component Inputs
interface ZohoAsapWidgetInputs {
// Required
appId: string; // Zoho ASAP application ID
orgId: string; // Zoho organization ID
msalToken: string; // MSAL ID token (Azure B2C/AD)
// Optional
enabled?: boolean; // Enable/disable widget (default: true)
nonce?: string; // Security nonce for script loading
// Prefill (optional)
prefillFields?: { // Object with field names and values
[fieldName: string]: any; // e.g., { cf_event_request_id: '12345', cf_priority: 'High' }
};
departmentId?: string; // Department ID (required for prefill)
layoutId?: string; // Form layout ID (required for prefill)
}🔐 How It Works
Architecture
┌─────────────────┐
│ Your App │
│ (MSAL only) │
└────────┬────────┘
│ MSAL Token
▼
┌─────────────────┐
│ Zoho Widget │ ──► Backend API ──► Exchange Token ──► Zoho JWT
│ (Library) │ ◄── Zoho JWT ◄────────────────────────
└────────┬────────┘
│
▼ Login with JWT
┌─────────────────┐
│ Zoho Desk │
│ ASAP API │
└─────────────────┘Flow
- App authenticates user via MSAL (Azure B2C/AD)
- App passes MSAL token to widget via
[msalToken]input - Widget handles everything:
- Exchanges token for Zoho JWT internally
- Logs into Zoho Desk ASAP
- Manages session lifecycle
- On logout: Widget clears all Zoho storage/cookies automatically
🧠 Smart User Detection
The widget intelligently handles two scenarios:
Scenario 1: Same User, Token Refresh
// User's MSAL token expires and gets refreshed
// Old: [email protected]
// New: [email protected]
// Widget: Extracts user identity (email/oid)
// → Same user detected
// → Updates Zoho JWT without logout
// → User keeps their session stateScenario 2: Different User Login
// User A logs out, User B logs in
// Old: [email protected]...
// New: [email protected]...
// Widget: Extracts user identity
// → Different user detected
// → Fully logs out User A
// → Clears all storage/cookies
// → Logs in User B with clean sessionThis prevents unnecessary session disruption while ensuring complete isolation between users.
## 🧹 Session Management (Automatic)
The widget handles **everything automatically** when component lifecycle changes:
```typescript
// When widget is destroyed (*ngIf becomes false):
// - Detects ngOnDestroy
// - Calls Zoho logout
// - Clears ALL Zoho storage (localStorage, sessionStorage, cookies)
// - Waits for logout confirmation event
// When widget is created fresh (*ngIf becomes true):
// - Clears any residual Zoho storage (safety check)
// - Loads Zoho script
// - Exchanges MSAL token for Zoho JWT
// - Logs into Zoho with fresh sessionYour app just controls the *ngIf condition - widget does the rest!
🔄 Multi-User Scenarios
Perfect for applications where users switch accounts:
// User A logged in
this.msalToken = '[email protected]...';
// Widget: Exchanges token, logs in User A
// User A logs out
this.msalToken = null;
// Widget: Detects token removal, logs out, clears all Zoho data
// User B logs in (component recreated)
this.msalToken = '[email protected]...';
// Widget: Fresh component → clears storage → new session
// User B sees ONLY their data, never User A's📝 Form Prefilling
To prefill ticket fields when opening the widget, pass an object with field names and values:
<zoho-asap-widget
[appId]="'202588000344171472'"
[orgId]="'649984843'"
[msalToken]="msalToken"
[prefillFields]="{
subject: 'Technical Support Request', // Standard field
description: 'Issue details here', // Standard field
cf_event_request_id: 'EVENT-12345', // Custom field
cf_priority: 'High', // Custom field
cf_category: 'Technical Issue', // Custom field
cf_user_email: userEmail, // Custom field with dynamic value
cf_location: 'Building A' // Custom field
}"
[departmentId]="'202588000019741045'"
[layoutId]="'202588000019743298'">
</zoho-asap-widget>Field Types:
Standard Zoho Fields (no prefix needed):
subject- Ticket subject/titledescription- Ticket description/bodyemail- Contact emailphone- Contact phone
Custom Fields (require cf_ prefix):
cf_event_request_id- Your custom event ID fieldcf_priority- Your custom priority fieldcf_category- Your custom category fieldcf_*- Any other custom field you've created
Requirements:
prefillFields: Object where keys are field names (standard or custom)departmentId: Zoho department ID (required for prefill)layoutId: Zoho form layout ID (required for prefill)
How to find field API names:
- Go to Zoho Desk > Setup > Channels > ASAP
- Click on your department's form layout
- Standard fields: Use lowercase names (
subject,description,email) - Custom fields: Will have API names like
cf_event_request_id,cf_priority
Dynamic prefill with user context:
// Construct prefill data with user information
prefillData = {
subject: `Support Request from ${userName}`,
email: userEmail,
cf_event_request_id: ticketId,
cf_user_type: userType,
cf_priority: priority
};
// Update specific fields dynamically
this.prefillData.cf_event_request_id = newTicketId;
this.prefillData.subject = `Updated Request - ${newTicketId}`;
// Widget automatically reloads when prefillFields object reference changes
this.prefillData = { ...this.prefillData };The widget automatically:
- Configures prefill before loading the Zoho script
- Reapplies prefill after user login
- Reloads widget when
prefillFieldsobject changes
Important: What Your App Needs vs. Doesn't Need
✅ Your App Needs to Provide
MSAL ID Token - From Azure B2C/Azure AD authentication
- Get this from your MSAL authentication service
- Pass it to the widget:
[msalToken]="yourToken" - Example:
@azure/msal-browser,@azure/msal-angular, or any MSAL implementation
Zoho Configuration - Your Zoho Desk ASAP credentials
appId: From Zoho Desk ASAP setuporgId: Your Zoho organization ID
Optional: Prefill Data - Custom field values for ticket forms
prefillFields: Object with field names and valuesdepartmentId&layoutId: Required if using prefill
❌ Your App Does NOT Need
- ~~Any Zoho-specific services or logic~~
- ~~JWT token exchange code (widget calls backend internally)~~
- ~~Session management code~~
- ~~Storage/cookie clearing logic~~
- ~~Logout/login event handlers~~
- ~~User switching detection~~
- ~~Import anything except
ZohoAsapWidgetModule~~
Summary: Just provide the MSAL token and Zoho config - the widget handles all token exchange and session management internally!
🏗️ Building & Publishing
# Build the library for production
ng build zoho-asap-widget --configuration production
# Navigate to dist folder
cd dist/zoho-asap-widget
# Publish to npm (one-time login: npm login)
npm publish --access public
# For updates, increment version first:
# npm version patch # 1.0.0 -> 1.0.1
# npm version minor # 1.0.0 -> 1.1.0
# npm version major # 1.0.0 -> 2.0.0🐛 Troubleshooting
Widget shows old user's data after login
- Cause: Browser cache not cleared
- Solution: Widget now clears storage on
ngOnInit()automatically - Manual fix: Clear browser cache (Ctrl+Shift+Delete → All time)
Backend API returns 405 Method Not Allowed
- Cause: Backend expects POST, library uses GET
- Solution: Change backend to accept GET requests
Widget not loading
- Check:
appIdandorgIdare correct - Check: MSAL token is valid (not null/undefined)
- Check: Network connectivity to Zoho services
Token refresh causes logout
- Cause: Should not happen - widget detects same user
- Debug: Check console for user identity extraction logs
- Verify: JWT payload has
oid,sub, oremailclaim
🔗 Resources
📄 License
MIT
