MSAL Authentication Module
@equinor/fusion-framework-module-msal provides secure Azure AD authentication for browser applications using Microsoft's MSAL (Microsoft Authentication Library). Perfect for web applications, SPAs, and React apps that need to authenticate with Microsoft services.
Version: This package now uses MSAL Browser v4, providing the latest security improvements and features from Microsoft.
Features
- Single Sign-On (SSO) support for Microsoft Azure AD and Azure AD B2C
- Token Management with automatic refresh and secure caching
- Module Hoisting for shared authentication state across application scopes
- Silent Authentication for seamless user experience
- Popup & Redirect Flows for different authentication scenarios
- Zero Configuration with sensible defaults and optional customization
- MSAL v4 Compatibility with v2 proxy layer for backward compatibility
Quick Start
pnpm add @equinor/fusion-framework-module-msalimport { enableMSAL, initialize, type IMsalProvider } from '@equinor/fusion-framework-module-msal';
import { ModulesConfigurator } from '@equinor/fusion-framework-module';
// 1. Configure the module
const configurator = new ModulesConfigurator();
enableMSAL(configurator, (builder) => {
builder.setClientConfig({
auth: {
clientId: 'your-client-id',
tenantId: 'your-tenant-id',
redirectUri: 'https://your-app.com/callback'
}
});
// With requiresAuth=true, the module will attempt automatic login during initialization
// and await a valid authenticated account before initialization completes
builder.setRequiresAuth(true);
});
// 2. Initialize the framework (auto-initializes auth provider)
const framework = await initialize(configurator);
const auth: IMsalProvider = framework.auth;
// 3. Optional: Handle authentication redirect manually (auto-called during initialization)
const redirectResult = await auth.handleRedirect();
if (redirectResult?.account) {
console.log('Authenticated:', redirectResult.account.username);
}
// 4. Use authentication
// Option A: Token acquisition (v4 format - recommended)
const token = await auth.acquireAccessToken({
request: { scopes: ['api://your-app-id/.default'] }
});
// Option B: Legacy format (still supported via v2 proxy)
const legacyToken = await auth.acquireAccessToken({
scopes: ['api://your-app-id/.default']
});
// Option C: Silent authentication with fallback
try {
const result = await auth.login({
request: { scopes: ['api://your-app-id/.default'] },
silent: true // Attempts SSO first
});
} catch {
// Fallback to interactive if silent fails
await auth.login({
request: { scopes: ['api://your-app-id/.default'] },
behavior: 'popup'
});
}Important
The @equinor/fusion-framework-app enables this package by default, so applications using the app package do not need to enable this module manually.
Configuration
Required Settings
| Setting | Description | Required |
|---|---|---|
auth.clientId | Azure AD application client ID | β |
auth.tenantId | Azure AD tenant ID | β |
auth.redirectUri | Authentication callback URL | Optional |
Optional Settings
| Setting | Description | Default |
|---|---|---|
requiresAuth | Auto-authenticate on initialization | false |
version | Force specific MSAL version | Latest |
Environment Variables
# Required
AZURE_CLIENT_ID=your-client-id
AZURE_TENANT_ID=your-tenant-id
# Optional
AZURE_REDIRECT_URI=https://your-app.com/callbackAPI Reference
enableMSAL(configurator, configure?)
Enables the MSAL module in your Fusion Framework application.
Parameters:
configurator:IModulesConfigurator- The modules configurator instanceconfigure?:(builder: { setClientConfig, setRequiresAuth }) => void- Optional configuration function
Returns: void
Example:
enableMSAL(configurator, (builder) => {
builder.setClientConfig({ auth: { clientId: '...', tenantId: '...' } });
builder.setRequiresAuth(true);
});Type Definitions
LoginOptions
type LoginOptions = {
request: PopupRequest | RedirectRequest; // MSAL request object
behavior?: 'popup' | 'redirect'; // Auth method (default: 'redirect')
silent?: boolean; // Attempt silent auth first (default: true)
};LogoutOptions
type LogoutOptions = {
redirectUri?: string; // Redirect after logout
account?: AccountInfo; // Account to logout (defaults to active)
};AcquireTokenOptions
type AcquireTokenOptions = {
request: PopupRequest | RedirectRequest; // MSAL request with scopes
behavior?: 'popup' | 'redirect'; // Auth method (default: 'redirect')
silent?: boolean; // Attempt silent first (default: true if account available)
};IMsalProvider
The authentication provider interface available at framework.auth:
interface IMsalProvider {
// The MSAL PublicClientApplication instance
readonly client: IMsalClient;
// Current user account information
readonly account: AccountInfo | null;
// Initialize the MSAL provider
initialize(): Promise<void>;
// Acquire an access token for the specified scopes
acquireAccessToken(options: AcquireTokenOptionsLegacy): Promise<string | undefined>;
// Acquire full authentication result
acquireToken(options: AcquireTokenOptionsLegacy): Promise<AcquireTokenResult>;
// Login user interactively
login(options: LoginOptions): Promise<LoginResult>;
// Logout user (returns boolean)
logout(options?: LogoutOptions): Promise<boolean>;
// Handle authentication redirect (returns AuthenticationResult | null)
handleRedirect(): Promise<AuthenticationResult | null>;
}
// Note: defaultAccount and other deprecated v2 properties are available only
// when using a v2-compatible proxy via createProxyProvider()Module Hoisting
The module implements a hoisting pattern where the authentication provider is created once at the root level and shared across all sub-modules. This ensures consistent authentication state throughout your application while maintaining security and performance.
Important
Configure the auth module only in the root Fusion Framework instance - Sub-instances will automatically inherit the authentication configuration from the parent.
Migration Guide
MSAL v2 to v4 Migration
This package has been upgraded from MSAL Browser v2 to v4, providing the latest security improvements and features from Microsoft.
What Changed in v4
New MSAL Browser v4 Features:
- Enhanced security with improved token management
- Better performance and memory usage
- New authentication API structure with nested request objects
- Improved error handling and retry mechanisms
Architecture Changes:
- Module Hoisting: The module uses module hoisting, meaning sub-module instances proxy the parent module instance
- Shared Authentication State: Authentication state is shared across all module instances
- Async Initialization: New
initialize()method must be called before using the provider
Breaking Changes
Auto-initialization via Framework
// The provider initializes automatically when framework loads const framework = await initialize(configurator); const auth = framework.auth; // Already initialized // Manual initialization is only needed for standalone usage const provider = new MsalProvider(config); await provider.initialize();API Method Signature Updates
logout()now returnsPromise<boolean>instead ofPromise<void>handleRedirect()now returnsPromise<AuthenticationResult | null>instead ofPromise<void>- Methods now expect nested request objects (v4 format)
Account Property Changes
- Use
accountproperty (returnsAccountInfo | null) - v4 native defaultAccountis deprecated and only available via v2 proxy layer- Migration: Replace
defaultAccountwithaccountthroughout your code
- Use
Migration Steps
Update Token Acquisition (Recommended)
// Before (v2 format - still works via proxy) const token = await framework.auth.acquireAccessToken({ scopes: ['api.read'] }); // After (v4 format - recommended) const token = await framework.auth.acquireAccessToken({ request: { scopes: ['api.read'] } });Update Logout Handling
// Before await framework.auth.logout(); // After (check return value) const success = await framework.auth.logout(); if (success) { // Handle successful logout }Update Redirect Handling
// Before await framework.auth.handleRedirect(); // After (handle result) const result = await framework.auth.handleRedirect(); if (result?.account) { // User authenticated successfully console.log('Logged in as:', result.account.username); }Update Configuration (if needed)
// Ensure only the root module configures MSAL enableMSAL(configurator, (builder) => { builder.setClientConfig({ auth: { clientId: 'your-client-id', tenantId: 'your-tenant-id', redirectUri: 'https://your-app.com/callback' } }); builder.setRequiresAuth(true); });Remove Duplicate Configurations: Remove MSAL configuration from child modules
Backward Compatibility
The module includes a v2 proxy layer that automatically converts v2 API calls to v4 format. This means:
- β Existing code continues to work without changes
- β
Legacy format
{ scopes: [] }is still supported - β
Deprecated v2 properties like
defaultAccountare available via v2 proxy (with deprecation warnings) - β οΈ New v4 features require using v4 format
Benefits of Migration
- Better Security: Latest MSAL v4 security improvements and token handling
- Improved Performance: Faster token acquisition, better caching, reduced memory usage
- Enhanced Error Handling: More robust error recovery and retry mechanisms
- Future-Proof: Access to latest Microsoft authentication features and updates
- Shared State: Improved authentication state management across app scopes via module hoisting
- Better Developer Experience: Cleaner API, better TypeScript support, comprehensive documentation
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Authentication Loop | Ensure redirect URIs match your application's routing |
| Token Acquisition Fails | Check that required scopes are properly configured |
| Module Not Found | Ensure the module is properly configured and framework is initialized |
| Multiple MSAL Instances | Remove duplicate configurations from child modules |
| Redirect Returns Void | For redirect flows, use handleRedirect() after navigation completes |
| Token Empty/Undefined | Verify user is authenticated and scopes are correct |
Getting Help
- π MSAL Cookbook - Complete working examples
- π Report Issues - Bug reports and feature requests
Version Management
The MSAL module includes built-in version checking to ensure compatibility between different MSAL library versions.
Version Resolution
import { resolveVersion, VersionError } from '@equinor/fusion-framework-module-msal/versioning';
// Resolve and validate a version
const result = resolveVersion('2.0.0');
console.log(result.isLatest); // false
console.log(result.satisfiesLatest); // true
console.log(result.enumVersion); // MsalModuleVersion.V2Version Checking Behavior
- Major Version Incompatibility: Throws
VersionErrorif requested major version is greater than latest - Minor Version Mismatch: Logs warning but allows execution
- Patch Differences: Ignored for compatibility
- Invalid Versions: Throws
VersionErrorwith descriptive message
API Reference
resolveVersion(version: string | SemVer): ResolvedVersion
Resolves and validates a version string against the latest available MSAL version.
Parameters:
version- Version string or SemVer object to resolve
Returns: ResolvedVersion object containing:
wantedVersion: SemVer- The parsed requested versionlatestVersion: SemVer- The latest available versionisLatest: boolean- Whether the version is exactly the latestsatisfiesLatest: boolean- Whether the major version matches latestenumVersion: MsalModuleVersion- Corresponding enum version
Throws: VersionError for invalid or incompatible versions
VersionError
Error class for version-related issues with the following types:
InvalidVersion- Requested version is not a valid semverInvalidLatestVersion- Latest version parsing failed (build issue)MajorIncompatibility- Major version is greater than latestMinorMismatch- Minor version differs (warning only)PatchDifference- Patch version differs (info only)IncompatibleVersion- General incompatibility
Error Handling
import { resolveVersion, VersionError } from '@equinor/fusion-framework-module-msal/versioning';
try {
const result = resolveVersion('3.0.0'); // Assuming latest is 2.x
} catch (error) {
if (error instanceof VersionError) {
console.error('Version error:', error.message);
console.error('Requested:', error.requestedVersion);
console.error('Latest:', error.latestVersion);
console.error('Type:', error.type);
}
}Additional Resources
Official Documentation
- π Azure AD App Registration Guide
- π MSAL Browser Documentation
- ποΈ Fusion Framework Documentation
- π Microsoft Identity Platform Overview
Learning Resources
- π MSAL Cookbook Examples
- π― OAuth 2.0 Scopes Explained
- π οΈ MSAL Troubleshooting Guide
- π Azure AD API Permissions Guide
Support
- π¬ For questions: Fusion Framework Discussions
- π Report bugs: Fusion Framework Issues
- π§ Contact: Equinor Fusion Framework Team