CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-azure--msal-node

Microsoft Authentication Library for Node - A comprehensive Node.js authentication library for Microsoft identity platform supporting multiple OAuth 2.0 flows

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

MSAL Node provides comprehensive error handling with specific error classes, detailed error codes, and structured error information. The error system helps developers diagnose authentication issues and implement appropriate error recovery strategies.

Capabilities

Error Class Hierarchy

MSAL Node uses a hierarchical error system with specific error classes for different scenarios.

/**
 * Base authentication error class (from @azure/msal-common)
 */
class AuthError extends Error {
  /** Error code identifier */
  errorCode: string;
  /** Detailed error message */
  errorMessage: string;
  /** Sub-error code for additional context */
  subError: string;
  /** Correlation ID for tracking requests */
  correlationId: string;
  
  constructor(errorCode: string, errorMessage?: string, suberror?: string);
  
  /** Create error message with error codes */
  createPostRequestFailed(endpoint: string, serverErrorMsg: string): string;
  /** Create error message for GET request failures */
  createGetRequestFailed(endpoint: string, serverErrorMsg: string): string;
}

/**
 * Client-side authentication errors (from @azure/msal-common)
 */
class ClientAuthError extends AuthError {
  constructor(errorCode: string, errorMessage?: string);
}

/**
 * Client configuration errors (from @azure/msal-common)
 */
class ClientConfigurationError extends AuthError {
  constructor(errorCode: string, errorMessage?: string);
}

/**
 * Server-side authentication errors (from @azure/msal-common)
 */
class ServerError extends AuthError {
  constructor(errorCode: string, errorMessage?: string, subError?: string);
}

/**
 * Interaction required errors (from @azure/msal-common)
 */
class InteractionRequiredAuthError extends AuthError {
  constructor(errorCode: string, errorMessage?: string, subError?: string);
}

Node-Specific Errors

MSAL Node provides additional error classes for Node.js-specific scenarios.

/**
 * Node.js specific authentication errors
 */
class NodeAuthError extends AuthError {
  constructor(errorCode: string, errorMessage?: string);
  
  /** Create error for invalid loopback server address type */
  static createInvalidLoopbackAddressTypeError(): NodeAuthError;
  /** Create error when unable to load redirect URL */
  static createUnableToLoadRedirectUrlError(): NodeAuthError;
  /** Create error when no auth code is found in response */
  static createNoAuthCodeInResponseError(): NodeAuthError;
  /** Create error when no loopback server exists */
  static createNoLoopbackServerExistsError(): NodeAuthError;
  /** Create error when loopback server already exists */
  static createLoopbackServerAlreadyExistsError(): NodeAuthError;
  /** Create error for loopback server timeout */
  static createLoopbackServerTimeoutError(): NodeAuthError;
  /** Create error for invalid state parameter */
  static createStateNotFoundError(): NodeAuthError;
  /** Create error when client certificate thumbprint is missing */
  static createThumbprintMissingError(): NodeAuthError;
  /** Create error when redirect URI is not supported */
  static createRedirectUriNotSupportedError(): NodeAuthError;
}

/**
 * Managed Identity specific errors
 */
class ManagedIdentityError extends AuthError {
  constructor(errorCode: string);
}

/**
 * Helper function to create managed identity errors
 */
function createManagedIdentityError(errorCode: string): ManagedIdentityError;

Error Codes

Comprehensive error codes for different error scenarios.

/**
 * Base authentication error codes
 */
const AuthErrorCodes = {
  /** Unexpected error occurred */
  unexpectedError: "unexpected_error",
  /** POST request failed */
  postRequestFailed: "post_request_failed",
  /** GET request failed */
  get_request_failed: "get_request_failed",
  /** Network request failed */
  networkError: "network_error",
  /** Request timeout */
  requestTimeout: "request_timeout",
  /** Invalid JSON in response */
  jsonParseError: "json_parse_error",
  /** Invalid authority */
  invalidAuthority: "invalid_authority"
} as const;

/**
 * Client authentication error codes
 */
const ClientAuthErrorCodes = {
  /** Multiple matching accounts found */
  multipleMatchingAccounts: "multiple_matching_accounts",
  /** Multiple matching tokens found */
  multipleMatchingTokens: "multiple_matching_tokens",
  /** No matching account found */
  noAccountFound: "no_account_found",
  /** No matching token found */
  noTokenFound: "no_token_found",
  /** Token request cannot be made */
  tokenRequestCannotBeMade: "token_request_cannot_be_made",
  /** Silent token request failed */
  silentTokenRequestFailure: "silent_token_request_failure"
} as const;

/**
 * Client configuration error codes
 */
const ClientConfigurationErrorCodes = {
  /** Invalid client ID */
  invalidClientId: "invalid_client_id",
  /** Invalid authority */
  invalidAuthority: "invalid_authority",
  /** Invalid redirect URI */
  invalidRedirectUri: "invalid_redirect_uri",
  /** Invalid request */
  invalidRequest: "invalid_request",
  /** Invalid scope */
  invalidScope: "invalid_scope",
  /** Invalid claims */
  invalidClaims: "invalid_claims"
} as const;

/**
 * Interaction required error codes
 */
const InteractionRequiredAuthErrorCodes = {
  /** User interaction required */
  interactionRequired: "interaction_required",
  /** User consent required */
  consentRequired: "consent_required",
  /** User login required */
  loginRequired: "login_required",
  /** Account selection required */
  accountSelectionRequired: "account_selection_required",
  /** Basic action required */
  basicActionRequired: "basic_action_required",
  /** Additional action required */
  additionalActionRequired: "additional_action_required"
} as const;

/**
 * Node-specific error codes
 */
const NodeAuthErrorCodes = {
  /** Invalid loopback server address type */
  invalidLoopbackAddressType: "invalid_loopback_server_address_type",
  /** Unable to load redirect URL */
  unableToLoadRedirectUrl: "unable_to_load_redirectUrl",
  /** No auth code in response */
  noAuthCodeInResponse: "no_auth_code_in_response",
  /** No loopback server exists */
  noLoopbackServerExists: "no_loopback_server_exists",
  /** Loopback server already exists */
  loopbackServerAlreadyExists: "loopback_server_already_exists",
  /** Loopback server timeout */
  loopbackServerTimeout: "loopback_server_timeout",
  /** State parameter not found */
  stateNotFound: "state_not_found",
  /** Thumbprint missing from client certificate */
  thumbprintMissing: "thumbprint_missing_from_client_certificate",
  /** Redirect URI not supported */
  redirectUriNotSupported: "redirect_uri_not_supported"
} as const;

/**
 * Managed Identity error codes
 */
const ManagedIdentityErrorCodes = {
  /** Invalid file extension */
  invalidFileExtension: "invalid_file_extension",
  /** Invalid file path */
  invalidFilePath: "invalid_file_path",
  /** Invalid managed identity ID type */
  invalidManagedIdentityIdType: "invalid_managed_identity_id_type",
  /** Invalid secret */
  invalidSecret: "invalid_secret",
  /** Missing client ID */
  missingId: "missing_client_id",
  /** Network unavailable */
  networkUnavailable: "network_unavailable",
  /** Platform not supported */
  platformNotSupported: "platform_not_supported",
  /** Unable to create Azure Arc */
  unableToCreateAzureArc: "unable_to_create_azure_arc",
  /** Unable to create Cloud Shell */
  unableToCreateCloudShell: "unable_to_create_cloud_shell",
  /** Unable to create source */
  unableToCreateSource: "unable_to_create_source",
  /** Unable to read secret file */
  unableToReadSecretFile: "unable_to_read_secret_file",
  /** URL parse error */
  urlParseError: "url_parse_error",
  /** User assigned not available at runtime */
  userAssignedNotAvailableAtRuntime: "user_assigned_not_available_at_runtime",
  /** WWW-Authenticate header missing */
  wwwAuthenticateHeaderMissing: "www_authenticate_header_missing",
  /** WWW-Authenticate header unsupported format */
  wwwAuthenticateHeaderUnsupportedFormat: "www_authenticate_header_unsupported_format",
  /** Environment variable URL malformed - Azure Pod Identity Authority Host */
  azurePodIdentityAuthorityHostUrlMalformed: "azure_pod_identity_authority_host_url_malformed",
  /** Environment variable URL malformed - Identity Endpoint */
  identityEndpointUrlMalformed: "identity_endpoint_url_malformed",
  /** Environment variable URL malformed - IMDS Endpoint */
  imdsEndpointUrlMalformed: "imds_endpoint_url_malformed",
  /** Environment variable URL malformed - MSI Endpoint */
  msiEndpointUrlMalformed: "msi_endpoint_url_malformed"
} as const;

Error Handling Patterns

Common error handling patterns and best practices.

/**
 * Error handling utility functions
 */
type ErrorHandlingUtils = {
  /** Check if error is retryable */
  isRetryableError(error: AuthError): boolean;
  /** Check if error requires user interaction */
  requiresInteraction(error: AuthError): boolean;
  /** Extract user-friendly error message */
  getUserFriendlyMessage(error: AuthError): string;
  /** Check if error is due to network issues */
  isNetworkError(error: AuthError): boolean;
};

Usage Examples:

import { 
  AuthError, 
  ClientAuthError, 
  InteractionRequiredAuthError,
  NodeAuthError,
  ManagedIdentityError 
} from "@azure/msal-node";

// Basic error handling
try {
  const response = await pca.acquireTokenSilent(silentRequest);
} catch (error) {
  if (error instanceof AuthError) {
    console.error("Authentication error:", error.errorCode, error.errorMessage);
    console.error("Correlation ID:", error.correlationId);
  } else {
    console.error("Unexpected error:", error);
  }
}

// Comprehensive error handling
async function handleAuthenticationError(error: unknown): Promise<void> {
  if (error instanceof InteractionRequiredAuthError) {
    console.log("User interaction required, falling back to interactive flow");
    // Implement interactive flow fallback
  } else if (error instanceof ClientAuthError) {
    switch (error.errorCode) {
      case "no_account_found":
        console.log("No cached accounts found, prompting for login");
        break;
      case "multiple_matching_accounts":
        console.log("Multiple accounts found, prompting for account selection");
        break;
      default:
        console.error("Client authentication error:", error.errorMessage);
    }
  } else if (error instanceof NodeAuthError) {
    switch (error.errorCode) {
      case "loopback_server_timeout":
        console.error("Interactive authentication timed out");
        break;
      case "state_not_found":
        console.error("Invalid state parameter - possible CSRF attack");
        break;
      default:
        console.error("Node authentication error:", error.errorMessage);
    }
  } else if (error instanceof ManagedIdentityError) {
    switch (error.errorCode) {
      case "platform_not_supported":
        console.error("Managed Identity not available on this platform");
        break;
      case "network_request_failed":
        console.error("Failed to connect to managed identity endpoint");
        break;
      default:
        console.error("Managed Identity error:", error.errorMessage);
    }
  } else if (error instanceof AuthError) {
    console.error("General authentication error:", error.errorCode, error.errorMessage);
  } else {
    console.error("Unexpected error:", error);
  }
}

Silent Flow Error Handling

Specific error handling for silent token acquisition scenarios.

/**
 * Silent flow error handling pattern
 */
async function acquireTokenWithFallback(
  silentRequest: SilentFlowRequest,
  interactiveRequest: InteractiveRequest
): Promise<AuthenticationResult> {
  try {
    // Try silent acquisition first
    return await pca.acquireTokenSilent(silentRequest);
  } catch (error) {
    if (error instanceof InteractionRequiredAuthError) {
      // Silent acquisition failed, user interaction required
      console.log("Silent acquisition failed, using interactive flow");
      return await pca.acquireTokenInteractive(interactiveRequest);
    } else if (error instanceof ClientAuthError && error.errorCode === "no_account_found") {
      // No cached accounts, need to authenticate
      console.log("No cached accounts found, using interactive flow");
      return await pca.acquireTokenInteractive(interactiveRequest);
    } else {
      // Other errors should be handled or re-thrown
      throw error;
    }
  }
}

Network Error Handling

Handling network-related errors and implementing retry logic.

/**
 * Network error handling with retry logic
 */
async function acquireTokenWithRetry(
  request: ClientCredentialRequest,
  maxRetries: number = 3
): Promise<AuthenticationResult | null> {
  let lastError: AuthError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await cca.acquireTokenByClientCredential(request);
    } catch (error) {
      lastError = error as AuthError;
      
      if (error instanceof AuthError) {
        // Check if error is retryable
        if (isRetryableError(error) && attempt < maxRetries) {
          const delayMs = Math.pow(2, attempt) * 1000; // Exponential backoff
          console.log(`Attempt ${attempt} failed, retrying in ${delayMs}ms`);
          await new Promise(resolve => setTimeout(resolve, delayMs));
          continue;
        }
      }
      
      // Non-retryable error or max retries reached
      throw error;
    }
  }
  
  throw lastError;
}

function isRetryableError(error: AuthError): boolean {
  const retryableErrors = [
    "network_error",
    "request_timeout",
    "post_request_failed",
    "get_request_failed"
  ];
  
  return retryableErrors.includes(error.errorCode);
}

Device Code Flow Error Handling

Specific error handling for device code flow scenarios.

/**
 * Device code flow error handling
 */
async function deviceCodeFlowWithErrorHandling(): Promise<AuthenticationResult | null> {
  const deviceCodeRequest = {
    scopes: ["user.read"],
    deviceCodeCallback: (response) => {
      console.log("Please visit:", response.verificationUri);
      console.log("And enter code:", response.userCode);
    }
  };

  try {
    const response = await pca.acquireTokenByDeviceCode(deviceCodeRequest);
    return response;
  } catch (error) {
    if (error instanceof AuthError) {
      switch (error.errorCode) {
        case "authorization_pending":
          console.log("User has not completed authentication yet");
          break;
        case "slow_down":
          console.log("Polling too frequently, slowing down");
          break;
        case "expired_token":
          console.log("Device code has expired, please try again");
          break;
        case "access_denied":
          console.log("User denied authorization");
          break;
        default:
          console.error("Device code flow error:", error.errorMessage);
      }
    }
    throw error;
  }
}

Error Recovery Strategies

Implementation patterns for error recovery and user experience.

/**
 * Comprehensive error recovery strategy
 */
class AuthenticationService {
  private pca: PublicClientApplication;
  
  constructor(config: Configuration) {
    this.pca = new PublicClientApplication(config);
  }
  
  async acquireToken(scopes: string[]): Promise<AuthenticationResult> {
    const accounts = await this.pca.getAllAccounts();
    
    if (accounts.length > 0) {
      // Try silent acquisition first
      try {
        return await this.pca.acquireTokenSilent({
          account: accounts[0],
          scopes: scopes
        });
      } catch (error) {
        return await this.handleSilentFlowError(error, scopes);
      }
    } else {
      // No accounts cached, use interactive flow
      return await this.acquireTokenInteractively(scopes);
    }
  }
  
  private async handleSilentFlowError(
    error: unknown, 
    scopes: string[]
  ): Promise<AuthenticationResult> {
    if (error instanceof InteractionRequiredAuthError) {
      // Token expired or consent required
      return await this.acquireTokenInteractively(scopes);
    } else if (error instanceof ClientAuthError) {
      if (error.errorCode === "no_token_found") {
        // No cached token, use interactive flow
        return await this.acquireTokenInteractively(scopes);
      }
    } else if (error instanceof AuthError && this.isNetworkError(error)) {
      // Network error, implement retry with backoff
      await this.delay(1000);
      return await this.acquireToken(scopes);
    }
    
    // Re-throw unhandled errors
    throw error;
  }
  
  private async acquireTokenInteractively(scopes: string[]): Promise<AuthenticationResult> {
    return await this.pca.acquireTokenInteractive({
      scopes: scopes,
      openBrowser: async (url: string) => {
        const { open } = await import("open");
        await open(url);
      }
    });
  }
  
  private isNetworkError(error: AuthError): boolean {
    const networkErrorCodes = [
      "network_error",
      "request_timeout",
      "post_request_failed",
      "get_request_failed"
    ];
    return networkErrorCodes.includes(error.errorCode);
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

Logging and Monitoring

Error logging and monitoring best practices.

/**
 * Error logging and monitoring integration
 */
class ErrorLogger {
  static logAuthError(error: AuthError, context: string): void {
    const errorData = {
      timestamp: new Date().toISOString(),
      context: context,
      errorCode: error.errorCode,
      errorMessage: error.errorMessage,
      correlationId: error.correlationId,
      subError: error.subError,
      stack: error.stack
    };
    
    // Log to monitoring system
    console.error("Authentication Error:", JSON.stringify(errorData, null, 2));
    
    // Send to monitoring service (e.g., Application Insights, DataDog)
    // this.sendToMonitoring(errorData);
  }
  
  static logNetworkError(error: AuthError, endpoint: string): void {
    const networkErrorData = {
      timestamp: new Date().toISOString(),
      endpoint: endpoint,
      errorCode: error.errorCode,
      errorMessage: error.errorMessage,
      correlationId: error.correlationId
    };
    
    console.error("Network Error:", JSON.stringify(networkErrorData, null, 2));
  }
}

// Usage in error handling
try {
  const response = await pca.acquireTokenSilent(request);
} catch (error) {
  if (error instanceof AuthError) {
    ErrorLogger.logAuthError(error, "silent_token_acquisition");
  }
  throw error;
}

Common Error Scenarios

Token Expiration

// Handle expired tokens
if (error.errorCode === "token_expired" || error.errorCode === "interaction_required") {
  // Token has expired, need to refresh or re-authenticate
  console.log("Token expired, attempting refresh");
}

Account Selection

// Handle multiple accounts
if (error.errorCode === "multiple_matching_accounts") {
  const accounts = await pca.getAllAccounts();
  // Present account selection UI to user
  console.log("Multiple accounts found:", accounts.map(a => a.username));
}

Network Connectivity

// Handle network issues
if (error.errorCode === "network_error" || error.errorCode === "request_timeout") {
  console.log("Network connectivity issue, check internet connection");
  // Implement offline mode or retry logic
}

Configuration Issues

// Handle configuration errors
if (error instanceof ClientConfigurationError) {
  console.error("Configuration error - check client ID, authority, and other settings");
  console.error("Error details:", error.errorMessage);
}

Platform-Specific Issues

// Handle managed identity platform issues
if (error instanceof ManagedIdentityError && error.errorCode === "platform_not_supported") {
  console.error("Managed Identity not available - running outside Azure environment");
  // Fallback to other authentication methods
}

Install with Tessl CLI

npx tessl i tessl/npm-azure--msal-node

docs

authentication-flows.md

confidential-client.md

configuration.md

error-handling.md

index.md

managed-identity.md

public-client.md

token-cache.md

tile.json